_.identity = function (val) { return val; };
전달인자를 그대로 리턴해주는 함수
_.slice = function (arr, start, end) { let _start = start || 0, // `start`가 undefined인 경우, slice는 0부터 동작합니다. _end = end; if (start < 0) _start = Math.max(0, arr.length + start); if (end < 0) _end = Math.max(0, arr.length + end); if (_end === undefined || _end > arr.length) _end = arr.length; let result = []; for (let i = _start; i < _end; i++) { result.push(arr[i]); } return result; };
start 인덱스부터 end 인덱스 이전까지의 요소를 shallow copy하여 새로운 배열을 리턴
_.take = function (arr, n) { if(n < 0 || n === undefined) { return []; } return _.slice(arr, 0, n); };
배열의 처음 n개의 element를 담은 새로운 배열을 리턴
n이 undefined이거나 음수인 경우, 빈 배열을 리턴
n이 배열의 길이를 벗어날 경우, 전체 배열을 shallow copy한 새로운 배열을 리턴
_.drop = function (arr, n) { if(n < 0 || n === undefined) { return arr; } else if(n > arr.length) { return []; } else { return _.slice(arr, n, arr.length); } };
처음 n개의 element를 제외한 새로운 배열을 리턴
n이 undefined이거나 음수인 경우, 전체 배열을 shallow copy한 새로운 배열을 리턴
n이 배열의 길이를 벗어날 경우, 빈 배열을 리턴
_.last = function (arr, n) { if(n === undefined || n < 0) { return _.slice(arr, -1); } else if(n > arr.length) { return _.take(arr, n); } else if(n === 0) { return []; } else { return _.slice(arr,-n); } };
배열의 마지막 n개의 element를 담은 새로운 배열을 리턴
n이 undefined이거나 음수인 경우, 배열의 마지막 요소만을 담은 배열을 리턴
n이 배열의 길이를 벗어날 경우, 전체 배열을 shallow copy한 새로운 배열을 리턴
_.each = function (collection, iteratee) { if(Array.isArray(collection)) { for(let i = 0; i < collection.length; i++) { iteratee(collection[i], i, collection); } } else { for(let key in collection) { iteratee(collection[key], key, collection); } } };
collection의 각 데이터에 반복적인 작업을 수행
_.indexOf = function (arr, target) { let result = -1; _.each(arr, function (item, index) { if (item === target && result === -1) { result = index; } }); return result; };
target으로 전달되는 값이 arr의 요소인 경우, 배열에서의 위치(index)를 리턴
그렇지 않은 경우, -1을 리턴
target이 중복해서 존재하는 경우, 가장 낮은 index를 리턴
_.filter = function (arr, test) { let result = []; _.each(arr, function(el) { if(test(el)) { result.push(el); } }) return result; };
test 함수를 통과하는 모든 요소를 담은 새로운 배열을 리턴
test(element)의 결과(return 값)가 truthy일 경우, 통과
test 함수는 각 요소에 반복 적용
_.reject = function (arr, test) { let result = []; _.each(arr, function(el) { if(!test(el)) { result.push(el); } }) return result; };
_.filter와 정반대로 test 함수를 통과하지 않는 모든 요소를 담은 새로운 배열을 리턴
_.uniq = function (arr) { let result = []; for(let el of arr) { if(_.indexOf(result, el) === -1) { result.push(el); } } return result; };
주어진 배열의 요소가 중복되지 않도록 새로운 배열을 리턴
입력으로 전달되는 배열의 요소는 모두 primitive value라고 가정
_.map = function (arr, iteratee) { let result = []; _.each(arr, function(el) { result.push(iteratee(el)); }) return result; };
iteratee(반복되는 작업)를 배열의 각 요소에 적용(apply)한 결과를 담은 새로운 배열을 리턴
배열의 각 요소를 다른 것(iteratee의 결과)으로 매핑(mapping)
_.pluck = function (arr, keyOrIdx) { let result = []; _.map(arr, function(el) { result.push(el[keyOrIdx]); }) return result; };
_.reduce = function (arr, iteratee, initVal) { if(initVal === undefined) { initVal = arr[0]; _.each(_.slice(arr, 1), function(cur, idx, arr) { initVal = iteratee(initVal, cur, idx, arr); }) } else { _.each(arr, function(cur, idx, arr) { initVal = iteratee(initVal, cur, idx, arr); }) } return initVal; };
_.once = function (func) { let checked = false; let result; return function () { if (!checked) { result = func(...arguments); checked = true; } return result; }; };
callback 함수를 한 번만 호출하는 '함수'를 리턴
여러 번 호출해도 callback 함수는 한 번 이상 호출되지 않는다.
_.delay = function (func, wait, ...rest) { setTimeout(func, wait, ...rest) };
입력으로 전달되는 시간(ms, 밀리초)후 callback 함수를 함께 전달되는 (임의의 개수의) 인자와 함께 실행
_.includes = function (arr, target) { let result = false; _.each(arr, function(el) { if (el === target){ result = true; } }) return result; };
배열이 주어진 값을 포함하는지 확인
입력으로 전달되는 배열의 요소는 모두 primitive value라고 가정
_.every = function (arr, iteratee) { let result = true; if (arr.length === 0) { return true; } if (iteratee === undefined) { for (let el of arr) { if(!el) { result = false; break; } } } else { for (let el of arr) { if (!iteratee(el)) { result = false; break; } } } return result; };
배열의 모든 요소가 test 함수(iteratee)를 통과하면 true를, 그렇지 않은 경우 false를 리턴
test(element)의 결과(return 값)가 truthy일 경우, 통과
빈 배열을 입력받은 경우, true를 리턴
_.some = function (arr, iteratee) { let result = false; if (arr.length === 0) { return false; } if (iteratee === undefined) { for (let el of arr) { if(el) { result = true; break; } } } else { for (let el of arr) { if(iteratee(el)) { result = true; break; } } } return result; };
배열의 요소 중 하나라도 test 함수(iteratee)를 통과하면 true를, 그렇지 않은 경우 false를 리턴
빈 배열을 입력받은 경우, false를 리턴
_.extend = function (...rest) { let result = rest[0]; _.each(_.slice(rest,1), function(el) { for (let prop in el) { result[prop] = el[prop] } }) return result; };
여러 개의 객체를 입력받아, 순서대로 객체를 결합
첫 번째 입력인 객체를 기준으로 다음 순서의 객체들의 속성을 덮어씀
최종적으로 (속성이 추가된) 첫 번째 객체를 리턴 (새 객체 X)
_.defaults = function (...rest) { let result = rest[0]; _.each(_.slice(rest,1), function(el) { for (let prop in el) { if (result[prop] === undefined) { result[prop] = el[prop] } } }) return result; };
_.extend와 비슷하게 동작하지만, 이미 존재하는 속성(key)을 덮어쓰지 않는다.
_.zip = function (...rest) { let max =_.reduce(rest, (acc, cur) => acc.length > cur.length ? acc : cur).length; let result = []; _.each(rest, function (el) { for (let i = 0; i <max; i++) { if (result[i] === undefined) { result.push([]) } result[i].push(el[i]) } }) return result; };
여러 개의 배열을 입력받아, 같은 index의 요소들을 묶어 배열로 만들기
각 index 마다 하나의 배열을 만들고, 최종적으로 이 배열들을 요소로 갖는 배열을 리턴
최종적으로 리턴되는 배열의 각 요소의 길이는 입력으로 전달되는 배열 중 가장 '긴' 배열의 길이로 통일
특정 index에 요소가 없는 경우, undefined를 사용
_.zipStrict = function (...rest) { let min =_.reduce(rest, (acc, cur) => acc.length < cur.length ? acc : cur).length; let result = []; _.each(rest, function (el) { for (let i = 0; i <min; i++) { if (result[i] === undefined) { result.push([]) } result[i].push(el[i]) } }) return result; };
_.zip과 비슷하게 동작하지만,
최종적으로 리턴되는 배열의 각 요소의 길이는 입력으로 전달되는 배열 중 가장 '짧은' 배열의 길이로 통일
코드 짤 때, 생각을 좀하면서 짜야겠다. 다 짜고나서 테스트를 통과했다고 무턱대고 넘어가지말고 좀 더 이해하기 쉽게 코드를 만들어야겠다.