map
함수형 프로그래밍은 인자와 인자 값으로 소통한다. map은 결과를 리턴해서 리턴된 값을 개발자가 이후에 변화를 일으키는 데 사용하는 함수이며, 고차 함수(함수를 값으로 다루는 함수)이다.
아래와 같은 객체가 있다고 치자.
const products = [
{name: '반팔티', price: 15000},
{name: '긴팔티', price: 20000},
{name: '핸드폰케이스', price: 15000},
{name: '후드티', price: 30000},
{name: '바지', price: 25000}
];
위의 객체에서 name의 값들을 반환하고 싶다면 어떻게 해야 할까?
let names = [];
for (let a of products) {
names.push(a.name);
}
console.log(names);
names라는 배열을 생성하고, for문을 통해 배열 안에 추가를 하면 된다.
map을 이용하면 아래처럼 더 간단하게 할 수 있다.
console.log(products.map(p=>p.name));
미췬...! map을 강사님이 구현한 함수는 다음과 같다.
const map = (f, iter) => {
let res = [];
for (const a of iter) {
res.push(f(a));
}
return res;
}
즉 map은 함수 내의 callback함수인 f함수를 각각의 요소에 대해 한 번씩 순서대로 불러 그 함수의 반환값으로 새로운 배열을 생성한다.
docs를 보면 이런 예시도 있다.
const kvArr = [{
key:1, value:10},
{key:2, value:20},
{key:3, value: 30}
];
const reformattedArr = kvArr.map((obj) => {
const robj = {};
robj[obj.key] = obj.value;
return robj;
});
console.log(kvArr);
// expected output: [ { key: 1, value: 10 }, { key: 2, value: 20 }, { key: 3, value: 30 } ]
console.log(reformattedArr);
//expected output: [ { '1': 10 }, { '2': 20 }, { '3': 30 } ]
잘만 사용하면 알고리즘 짤 때 진짜 유용할 것 같음..! 진짜 체화시켜서 자유자재로 사용해보즈아ㅏ..
강의에서 나오는 map의 다형성 예시
const map = (f, iter) => {
let res = [];
for (const a of iter) {
res.push(f(a));
}
return res;
}
let m = new Map();
m.set('a', 10);
m.set('b', 20);
console.log(m);
/*
Map(2) { 'a' => 10, 'b' => 20 }
[[Entries]]
0: {"a" => 10}
1: {"b" => 20}
size: 2
[[Prototype]]: Map
*/
console.log(new Map(map(([k, a]) => [k, a * 2], m)));
/*
Map(2) { 'a' => 20, 'b' => 40 }
[[Entries]]
0: {"a" => 20}
1: {"b" => 40}
size: 2
[[Prototype]]: Map
*/
querySelectorAll을 사용해서 수집된 객체들을 순회 처리할 수도 있다.
console.log(map(el => el.nodeName, document.querySelectorAll('*')));
const it = document.querySelectorAll('*')[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
제너레이터도 map의 인자로 활용될 수 있다.
function* gen() {
yield 2;
if (false) yield 3;
yield 4;
}
console.log(map(a => a * a, gen()));
// expected output: [ 4, 16 ]
filter
filter는 특정 조건만 걸러내는 함수다. 위에서 사용했던 products 객체 다시 불러오기!
const products = [
{name: '반팔티', price: 15000},
{name: '긴팔티', price: 20000},
{name: '핸드폰케이스', price: 15000},
{name: '후드티', price: 30000},
{name: '바지', price: 25000}
];
만약 위의 객체에서 가격이 20,000원 이상인 제품만 출력하려면 어떻게 해야 할까?
let under20000 = [];
for (const p of products) {
if (p.price < 20000) under20000.push(p);
}
console.log(under20000);
// expected output: [ { name: '반팔티', price: 15000 }, { name: '핸드폰케이스', price: 15000 } ]
filter 메소드를 이용하면 다음과 같다.
console.log(products.filter(p => p.price < 20000));
filter 함수 구현
const filter = (f, iter) => {
let res = [];
for (const a of iter) {
if (f(a)) res.push(a);
}
return res;
}
console.log(filter(p => p.price < 20000, products));
filter() 메소드도 map과 마찬가지로 새로운 배열을 반환하는데, 콜백 함수의 조건에서 true인 요소들만 배열에 추가한다.
아래의 예시는 코딩테스트 문제 풀었을 때의 예시
const lottos = [44, 1, 0, 0, 31, 25];
const zeroCnt = lottos.filter(num => num === 0).length;
console.log(zeroCnt); // 2
reduce
reduce는 값을 축약하는 함수다. 값들을 더해서 하나로 만든다. 즉 값을 누적시켜 간다. 파이썬은 sum()이라는 아주 간편하고 훌륭한 메소드가 있지만, 자바스크립트에는 아쉽게도 없더라구...
const nums = [1, 2, 3, 4, 5];
let total = 0;
for (const n of nums) {
total = total + n;
}
console.log(total); // expected output: 15
그래서 만약 배열의 원소들을 합친다면 다음처럼 구현할 수 있다.
이를 reduce를 사용하면 다음처럼 할 수 있다.
console.log(nums.reduce((a,b) => a+b));
const reduce = (f, acc, iter) => {
if (!iter) { // 이터레이터가 아니라면 이터러블 값을 이터레이터로!
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const a of iter) {
acc = f(acc, a); // recursion
}
return acc;
};
const add = (a, b) => a+b;
console.log(reduce(add, 0, nums));
reduce()메소드는 네 개의 인자를 가진다.
- acc: 누산기: 콜백의 반환값을 누적
- cur: 현재 값: 처리할 현재 요소
- idx: 현재 인덱스: initialValue를 제공한 경우 0, 아니면 1부터 시작(제공하지 않으면 배열의 첫 번째 요소)
- src: 원본 배열: reduce()를 호출한 배열
리듀스 함수의 반환 값은 누산기에 할당 되고, 누산기는 순회 중 유지되므로 결국 최종 결과는 하나의 값이 된다.
reduce() 작동 방식
[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array) {
return accumulator + currentValue;
});
위의 예시에서 콜백은 4번 호출된다.
callback | accumulator | currentValue | currentIndex | array | 반환 값 |
1번째 호출 | 0 | 1 | 1 | [0, 1, 2, 3, 4] | 1 |
2번째 호출 | 1 | 2 | 2 | [0, 1, 2, 3, 4] | 3 |
3번째 호출 | 3 | 3 | 3 | [0, 1, 2, 3, 4] | 6 |
4번째 호출 | 6 | 4 | 4 | [0, 1, 2, 3, 4] | 10 |
이를 애로우펑션으로 바꾸면 다음처럼 표현할 수 있다.
[0, 1, 2, 3, 4].reduce( (prev, curr) => prev + curr );
'개발자 도전기 > [STUDY] JS || TS' 카테고리의 다른 글
JavaScript | FP && ES6+ | 맵 객체(Map Object) (0) | 2022.02.15 |
---|---|
JavaScript | FP && ES6+ | go, pipe, curry (0) | 2022.01.27 |
JavaScript | FP && ES6+ | 제너레이터와 이터레이터 (0) | 2022.01.21 |
JavaScript | FP && ES6+ | ES6에서의 순회와 이터러블:이터레이터 프로토콜 (0) | 2022.01.20 |
JavaScript | FP && ES6+ | 함수형 자바스크립트 기본기 (0) | 2022.01.19 |
댓글