본문 바로가기
개발자 도전기/[STUDY] JS || TS

JavaScript | FP && ES6+ | map, filter, reduce

by 답수 2022. 1. 25.
728x90
반응형

 

 

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 );

 

 

 

728x90
반응형
LIST

댓글