Underscore.js
:
map
,filter
,invoke
등 100여개의 유용한 함수들을 제공하는 JavaScript 라이브러리
Underscore.js
: 데이터(data)/자료의 모음, 대표적으로 배열과 객체
우리는 주로 collection을 가지고 collection 각 데이터에 반복 작업(iteration)을 한다.
JavaScript에서는 이러한 iteration을 위해 반복문과 반복을 위한 내장 메소드를 제공한다.
for
, for...of
, for...in
, while
arr.map
, arr.filter
for(let i = 0; i < 3; i++) {
console.log(i); // 0 // 1 // 2
}
이때, console.log(i);
처럼 반복되는 작업을 iteratee라고 부른다.
Underscore.js 라이브러리에서 제공하는 함수 중, collection을 다루는 몇 가지 함수를 직접 구현해보자.
_.slice()
_.slice(arr, start, end)
: 배열의 start
부터 end
전까지의 요소를 얕게 복사(shallow copy)해 새로운 배열로 만들어 반환하는 함수
_.slice = function(arr, start, end) {
let _start = start || 0;
let _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;
}
_.slice = function(arr, start, end) {
// start를 입력하지 않았을 경우(undefined), _start는 0부터 시작한다.
let _start = start || 0;
let _end = end;
// 만약 index에 -1을 넣었을 경우, arr.length - 1을 반환한다.
// 만약 배열의 길이를 초과하는 index를 넣을 경우, 0을 반환한다.
if(start < 0) {
_start = Math.max(0, arr.length + start);
}
if(end < 0) {
_end = Math.max(0, arr.length + end);
}
// end를 입력하지 않았을 경우(undefined), _end는 배열의 끝까지이다.
// end가 배열의 범위를 벗어날 경우, _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;
}
_.take()
_.take(arr, n)
: 배열의 처음 n
개의 요소를 담은 새로운 배열을 반환하는 함수
n
이 undefined
이거나 음수인 경우, 빈 배열을 리턴한다.n
이 배열의 길이를 벗어난 경우, 전체 배열 얕게 복사한 새로운 배열을 리턴한다._.take = function(arr, n) {
let result = [];
if(n === undefined || n < 0) n = 0;
else if(n > arr.length) n = arr.length;
for(let i = 0; i < n; i++) {
result.push(arr[i]);
}
return result;
};
_.drop()
_.drop(arr, n)
: _.take()
와 정반대로 배열의 처음 n
개의 요소를 제외한 새로운 배열을 반환하는 함수
n
이 undefined
거나 음수인 경우, 전체 배열을 얕게 복사한 새로운 배열을 리턴한다.n
이 배열의 길이를 벗어날 경우, 빈 배열을 리턴한다._.drop = function(arr, n) {
let result = [];
if(n === undefined || n < 0) n = 0;
else if(n > arr.length) n = arr.length;
for(let i = n; i < arr.length; i++) {
result.push(arr[i]);
}
return result;
};
_.last()
_.last(arr, n)
: 배열의 마지막 n개의 요소를 담은 새로운 배열을 반환하는 함수
n
이 undefined
거나 음수인 경우, 배열의 마지막 요소만 담은 배열을 리턴한다.n
이 배열의 길이를 벗어날 경우, 전체 배열을 얕게 복사한 새로운 배열을 리턴한다._.drop()
함수를 활용한다._.last = function(arr, n) {
if(n === undefined || n < 0) n = 1;
return _.drop(arr, arr.length - n);
};
forEach()
arr.forEach(callback)
: 배열을 순회하며 배열의 각 요소에 주어진 함수를 실행하는 메소드
callback(currentValue, index, array)
forEach()
메소드의 매개변수인 콜백 함수는 다음 세 가지 매개변수와 함께 호출된다.
currentValue
: 요소 값index
: 요소 인덱스array
: 순회 중인 배열
_.each()
_.each(collection, iteratee)
: collection을 순회하며 collection의 각 데이터에 iteratee 함수를 실행하는 함수
element/value
), 접근자(index/key
), collection(배열/객체
)을 다룰 수 있어야 한다.el
idx
collection
)value
key
collection
)_.each = function(collection, iteratee) {
// collection이 배열일 때
if(Array.isArray(collection)) {
for(let i = 0; i < collection.length; i++) {
iteratee(collection[i], i, collection);
}
// collection이 객체일 때
} else {
for(let key in collection) {
iteratee(collection[key], key, collection);
}
}
};
indexOf()
arr.indexOf(searchelement)
: 배열에서 지정된 요소를 찾을 수 있는 첫 번째 인덱스를 반환하고 존재하지 않으면 -1을 반환하는 메소드
_.indexOf()
_.indexOf(arr, target)
: target
이 arr
의 요소인 경우, 배열에서의 index
를 반환하고, 그렇지 않으면 -1을 반환하는 함수
target
이 중복해서 존재하는 경우, 가장 낮은 index
를 반환한다._.each()
함수를 활용해 배열을 순회한다._.indexOf = function(arr, target) {
let result = -1;
_.each(arr, function(el, idx) {
if(target === el && result === -1) { // 가장 낮은 index를 반환하기 위한 장치
result = idx;
}
});
return result;
};
filter()
arr.filter(callback)
: 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환하는 메소드
callback(element, index, array)
filter()
메소드의 매개변수인 콜백 함수는 다음 세 가지 매개변수와 함께 호출된다.
element
: 요소값index
: 요소 인덱스array
: 순회되는 배열 객체
_.filter()
_.filter(arr, test)
: test
함수를 통과하는 모든 요소를 담은 새로운 배열을 반환하는 함수
test
함수의 결과값이 truthy일 경우 통과이다.test
함수는 각 요소에 반복 적용된다._.filter = function(arr, test) {
let result = [];
_.each(arr, function(el, idx, collection) {
if(test(el, idx, collection)) {
result.push(el);
}
});
return result;
};
_.reject()
_.reject(arr, test)
: _.filter()
와 정반대로 test
함수를 통과하지 않는 모든 요소를 담은 새로운 배열을 반환하는 함수
_.reject = function(arr, test) {
let result = [];
_.each(arr, function(el, idx, collection) {
if(!test(el, idx, collection)) { // 조건문에 !만 추가
result.push(el);
}
});
return result;
};
_.uniq()
_.uniq(arr)
: 주어진 배열의 요소가 중복되지 않는 새로운 배열을 반환하는 함수
===
)을 사용한다._.uniq = function(arr) {
let result = [];
_.each(arr, function(el, idx, collection) {
// result 배열에 el이 없으면, 추가한다.
if(_.indexOf(result, el) === -1) {
result.push(el);
}
});
return result;
};
map()
arr.map(callback)
: 배열 내의 각각의 요소에 주어진 함수를 실행한 결과를 모아 새로운 배열을 반환하는 메소드
callback(currentValue, index, array)
map()
메소드의 매개변수인 콜백 함수는 다음 세 가지 매개변수와 함께 호출된다.
currentValue
: 요소 값index
: 요소 인덱스array
: 순회 중인 배열
_.map()
_.map(arr, iteratee)
: 배열의 각 요소에 iteratee 함수를 적용한 결과를 담은 새로운 배열을 반환하는 메소드
_.map = function(arr, iteratee) {
let result = [];
_.each(arr, function(el, index, collection) {
result.push(iteratee(el, index, collection));
});
return result;
};
_.pluck()
_.pluck(arr, keyOrIdx)
: 매개변수로 ① 객체 또는 배열을 요소로 갖는 배열과 ② 각 요소에서 찾고자 하는 key/index
를 입력 받아,
각 요소의 특정 요소/값만을 추출해 새로운 배열로 반환하는 함수
key/index
가 없는 요소의 경우, 추출 결과는 undefined
이다._.map()
함수를 활용한다. let result = []; // [[1, 2, 3], [4, 5, 6]]
_.map(arr, function(el) { // el = [1, 2, 3]
result.push(el[keyOrIdx]);
});
return result;
reduce()
arr.reduce(callback, initialValue)
: 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환하는 메소드
callback(acc, cur, idx, arr)
reduce()
메소드의 매개변수인 콜백 함수는 다음 네 가지 매개변수와 함께 호출된다.
accumulator
: 누적값currentValue
: 현재 요소currentIndex
: 현재 요소의 인덱스array
: 순회 중인 배열
initialValue
- 최초 호출에서 첫 번째 인수(
acc
)에 제공하는 값- 초기값을 제공하지 않으면 배열의 첫 번째 요소를 사용한다.
_.reduce()
_.reduce(arr, iteratee, initVal)
: 배열을 순회하며 각 요소에 iteratee 함수를 적용하여 그 결과값을 계속해서 누적하고, 최종적으로 누적된 결과값을 리턴하는 함수
_.reduce = function (arr, iteratee, initVal) {
let acc = initVal; // acc(누적값) = initVal(초기값)
// 이때 initVal은 값 또는 undefined
_.each(arr, function(el, idx, collection) {
// 초기값이 없는 경우, 배열의 arr[0]을 초기값으로 설정해준다.
if(acc === undefined) {
acc = arr[0];
// 초기값이 있는 경우, 배열을 순회하며 iteratee 함수를 실행하고, 결과값을 acc에 재할당한다.
} else {
acc = iteratee(acc, el, idx, collection);
}
});
return acc;
};