ES6에서의 map, filter, reduce

자바스크립트 ES6에서 map, filter, reducer 메소드는 Array 객체에서 제공하는 메서드이다.

const prod = [
    {name:'과자', price:2500},
    {name:'소시지', price:2000},
    {name:'맥주', price:5000},
    {name:'음료수', price:1500},
    {name:'', price:1000},
]

const str = ['name', 'old', 'address'];

map()

map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환한다.

매개변수

  • callback : 새로운 배열 요소를 생성하는 함수. 다음 세 가지 인수값을 갖는다
    • currentValue : 처리할 현재 요소
    • index : 처리할 현재 요소의 인덱스
    • array : map()을 호출한 배열
  • thisArg : callback을 실행할 때 this로 사용되는 값

반환 값

  • 배열의 각 요소에 대해 실행한 callback의 결과를 모은 새로운 배열
prod.map(p=>p.name)
// (5) ['과자', '소시지', '맥주', '음료수', '껌']

prod.map(p=>p.price)
// (5) [2500, 2000, 5000, 1500, 1000]

str.map(p=>p.length)
// (3) [4, 3, 7]

filter()

filter() 메서드는 주어진 함수의 조건을 만족하는 모든 요소를 모아 새로운 배열로 반환한다.

매개변수

  • callback : 새로운 배열 요소를 생성하는 함수. 다음 세 가지 인수값을 갖는다
    • element : 처리할 현재 요소
    • index : 처리할 현재 요소의 인덱스
    • array : filter를 호출한 배열
  • thisArg : callback을 실행할 때 this로 사용되는 값

반환 값

  • 조건을 만족하는 요소로 이루어진 새로운 배열. 어떤 요소도 조건을 만족하지 못했으면 빈 배열을 반환한다.
console.log(
    ...prod.filter(p=>p.price>2000)
)
// {name: '과자', price: 2500} {name: '맥주', price: 5000}

prod.filter(p=>p.price>5000)
// 어떠한 요소도 만족하지 못하므로 빈 배열을 반환한다.
// []

str.filter(s=>s.length>3)
// (2) ['name', 'address']

reduce()

reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환한다.

매개변수

  • callback : 배열의 각 요소에 대해 실행할 함수. 다음 네 가지 인수를 받는다.
    • accumulator : 누산기는 콜백의 반환값을 누적한다. 콜백의 이전 반환값 또는, 콜백의 첫 번째 호출이면서 initialValue를 제공한 경우에는 initialValue의 값입니다.
    • currentValue : 처리할 현재 요소
    • currentIndex : 처리할 현재 요소의 인덱스, initialValue를 제공한 경우 0, 아니면 1부터 시작합니다.
    • array : reduce를 호출한 배열
  • initialValue : callback의 최초 호출에서 첫 번째 인수에 제공하는 값. 초기값을 제공하지 않으면 배열의 첫 번째 요소를 사용한다. 빈 배열에서 초기값 없이 reduce()를 호출하면 오류가 발생합니다.

반환 값

  • 누적 계산의 결과 값.
// Array.prototype.reduce()
arr.reduce(callback[, initialValue])
// 상품의 가격만 따로 모아 배열로 담는다.
const price = prod.map(p=>p.price); 

price.reduce(
  (accumulator, currentValue, currentIndex, array) => {
    console.log(`${currentIndex} 번째 ==================`);
    console.log('accumulator', accumulator) // output: 10 11 13 16
    console.log('currentValue', currentValue) // output: 1 2 3 4
    console.log('currentIndex', currentIndex) // output: 0 1 2 3
    console.log('array', array) // output: [1, 2, 3, 4]
    return accumulator + currentValue
  })
/*
1 번째 ==================
accumulator 2500
currentValue 2000
currentIndex 1
array (5) [2500, 2000, 5000, 1500, 1000]
2 번째 ==================
accumulator 4500
currentValue 5000
currentIndex 2
array (5) [2500, 2000, 5000, 1500, 1000]
3 번째 ==================
accumulator 9500
currentValue 1500
currentIndex 3
array (5) [2500, 2000, 5000, 1500, 1000]
4 번째 ==================
accumulator 11000
currentValue 1000
currentIndex 4
array (5) [2500, 2000, 5000, 1500, 1000]
12000
*/

만약 initialValue가 있을 경우 accumulatorinitialValue을 갖는다. 또한 인덱스는 0부터 시작.

const initialValue = 10000;

price.reduce(
  (accumulator, currentValue, currentIndex, array) => {
    console.log(`${currentIndex} 번째 ==================`);
    console.log('accumulator', accumulator) // output: 10 11 13 16
    console.log('currentValue', currentValue) // output: 1 2 3 4
    console.log('currentIndex', currentIndex) // output: 0 1 2 3
    console.log('array', array) // output: [1, 2, 3, 4]
    return accumulator + currentValue
  },initialValue)

/*
0 번째 ==================
accumulator 10000
currentValue 2500
currentIndex 0
array (5) [2500, 2000, 5000, 1500, 1000]
1 번째 ==================
accumulator 12500
currentValue 2000
currentIndex 1
array (5) [2500, 2000, 5000, 1500, 1000]
2 번째 ==================
accumulator 14500
currentValue 5000
currentIndex 2
array (5) [2500, 2000, 5000, 1500, 1000]
3 번째 ==================
accumulator 19500
currentValue 1500
currentIndex 3
array (5) [2500, 2000, 5000, 1500, 1000]
4 번째 ==================
accumulator 21000
currentValue 1000
currentIndex 4
array (5) [2500, 2000, 5000, 1500, 1000]
22000
*/


직접 구현해보기

map, filter, reduce는 Array.prototype에서 구현되어있는 함수들이다. 따라서, Set이나, map과 같은 다른 객체에서는 사용할 수 없다.

따라서, 이터러블/이터레이터 프로토콜을 따르는 모든 객체에서도 사용할 수 있도록 구현해보자.

  • map
const map = (f, iter) => {
    let result = [];

    for(const a of iter){
        result.push(f(a));
    }
    return result
}
  • filter
const filter = (f, iter) => {
    let result = [];
    for(const a of iter) {
        if(f(a)){
            result.push(a);
        }
    }
    return result;
}
  • reduce
const reduce = (f, acc, iter) => {
    if(!iter) {
        iter = acc[Symbol.iterator]();
        acc = iter.next().value;
    }

    for(const a of iter){
        acc = f(acc,a);
    }
    return acc;
}


  • map 예제
/* Set */
const set = new Set(prod);
console.log(map(p=>p.price, set)) // (5) [2500, 2000, 5000, 1500, 1000]


/* Map */
const map2 = new Map([['a',1],['b',2],['c',3]])
console.log(map((v)=>v[1], map2)) // (3) [1, 2, 3]

/* NodeList */
const dom = document.querySelectorAll('*');
console.log(map(e=>e.tagName, dom)) // ['HTML', 'HEAD', 'META', 'META', 'META', 'TITLE', 'BODY', 'SCRIPT', 'SCRIPT', 'SCRIPT', 'SCRIPT', 'SCRIPT', 'SCRIPT', 'SCRIPT']
  • filter 예제
/* Set */
const set2 = new Set(prod);
console.log(filter(p=>p>2000, map(p=>p.price, set2))) // (2) [2500, 5000]

/* Map */
const map2 = new Map([['a',1],['b',2],['c',3]])
console.log(filter(v=>v>2, map(v=>v[1], map2))) // [3]

/* NodeList */
const dom = document.querySelectorAll('*');
console.log(filter(n=>n==='HTML', map(e=>e.tagName, dom))) // ['HTML']
  • reduce 예제
/* Set */
const set2 = new Set(prod);
const add = (a,b) => a+b;
console.log(
    reduce(add, 
        filter(
            p=>p>2000, 
            map(p=>p.price, set2)
        )
    )
) // 7500

/* Map */
const map2 = new Map([['a',1],['b',2],['c',3]])
console.log(
    reduce(add, 
        filter(
            v=>v>1, 
            map(v=>v[1], map2)
        )
    )
) // 5