RATSENO

[JS]map(), reduce() 함수 본문

DEV/JS

[JS]map(), reduce() 함수

RATSENO 2019. 12. 3. 11:11

자바스크립트의 내장 메서드 중에서 가장 강력하고 유용하다고 알려진 map(), reduce() 함수에 대해서 알아보겠습니다.

개발을 하다보면 배열을 다뤄야하는 경우가 굉장히 많습니다. 최근에 filter()메서드를 유용하게 잘 써먹게되면서, 다른 메서드들에 대해서도 알아봐야겠다는 생각이 들었습니다. (ㅠㅠ 반복문으로 해결하려던 과거의 저를 되돌아보게됬습니다.)

 

함께 map(), reduce() 함수를 알아보도록 하겠습니다.!

 

 

map() 메서드


 

arr.map(callback(currentValue[, index[, array]])[, thisArg])

매개변수

callback : 새로운 배열 요소를 생성하는 함수. 다음 세 가지 인수를 가집니다.

            -currentValue : 처리할 현재 요소

            -index(optional : 사용해도 되고 안해도 되고) : 처리할 현재 요소의 인덱스

            -array(optional : 사용해도 되고 안해도 되고) : map()을 호출한 배열

            -thisArg(optional : 사용해도 되고 안해도 되고) : callback을 실행할 때this로 사용되는 값

반환값

배열의 각 요소에 대해 실행한 callback의 결과를 모은 새로운 배열.

 

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

간단한 예로 주어진 배열의 요소들을 곱하기 2 하여 반환하는 예제를 들어보겠습니다.

const array = [1, 4, 9, 16];

const map = array.map(function(currentValue, /* index, array */){
    return currentValue*2;
});
//ES6의 화살표 함수로 표기하게 되면
//const map = array.map(currentValue => currentValue*2);

console.log(array);
//[1, 4, 9, 16]
console.log(map);
//[2, 8, 18, 32]

map callback 함수를 각각의 요소에 대해 한번씩 순서대로 불러 그 함수의 반환값으로 새로운 배열을 만듭니다.

map은 호출한 배열의 값을 변형하지 않습니다. 단, callback 함수에 의해서 변형될 수는 있습니다.

 

ex) map을 활용해 배열 속 객체를 재구성하기

var kvArray = [{key:1, value:10},
               {key:2, value:20},
               {key:3, value: 30}];

var reformattedArray = kvArray.map(function(obj){ 
   var rObj = {};
   rObj[obj.key] = obj.value;
   return rObj;
});
// reformattedArray
//[{1:10}, {2:20}, {3:30}]

// kvArray
// [{key:1, value:10},
//  {key:2, value:20},
//  {key:3, value: 30}]

 

ex) map을 포괄적으로 사용하기

var map = Array.prototype.map;
var a = map.call('Hello World', function(x) {
			//charCodeAt은 주어진 index에 해당하는 유니코드 값을 리턴하는데 
			//이 값은 unicode가 지원되는 모든 시스템에서 동일한 문자를 가르킨다.
			return x.charCodeAt(0); 
		});
//[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

 

ex) map을 포괄적으로 사용하기 (querySelectorAll)

아래 예제는 querySelectorAll을 사용해서 수집된 객체들을 순회 처리하는 법을 보여줍니다. 이번 경우 체크한 옵션 박스를 콘솔에 프린트합니다.

var elems = document.querySelectorAll('select option:checked');
var values = [].map.call(elems, function(obj) {
  return obj.value;
});

 

 

 

reduce() 메서드


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

map이 배열의 각 요소를 변형한다면 reduce는 배열 자체를 변형합니다. reduce라는 이름은 이 메서드가 보통 배열을 값 하나로 줄이는 쓰이기 때문에 붙었습니다. 예를 들어 배열에 들어있는 숫자를 더하거나 평균을 구하는 것은 배열을 값 하나로 줄이는 동작입니다. 하지만 reduce가 반환하는 값 하나는 객체일 수도 있고, 다른 배열일 수도 있습니다.

 

리듀서 함수는 네 개의 인자를 가집니다.

  1. 누산기accumulator (acc)
  2. 현재 값 (cur)
  3. 현재 인덱스 (idx)
  4. 원본 배열 (src)

리듀서 함수의 반환 값은 누산기에 할당되고, 누산기는 순회 중 유지되므로 결국 최종 결과는 하나의 값이 됩니다.

arr.reduce(callback[, initialValue])

매개변수

callback : 배열의 각 요소에 대해 실행할 함수. 다음 네 가지 인수를 받습니다

            -accumulator : 산기accmulator는 콜백의 반환값을 누적합니다. 콜백의 이전 반환값 또는, 콜백의 첫 번째                                    호출이면서 initialValue를 제공한 경우에는initialValue의 값입니다.

            -currentValue : 처리할 현재 요소.

            -currentIndex(optional : 사용해도 되고 안해도 되고) : 처리할 현재 요소의 인덱스. initialValue를 제공한

                                                                                   경우 0, 아니면 1부터 시작합니다.

            -array(optional : 사용해도 되고 안해도 되고) : reduce()를 호출한 배열.

initialValue(optional : 사용해도 되고 안해도 되고) : callback의 최초 호출에서 첫 번째 인수에 제공하는 값. 초기값을 제공하지 않으면 배열의 첫 번째 요소를 사용합니다. 빈 배열에서 초기값 없이 reduce()를 호출하면 오류가 발생합니다.

 

반환값

누적 계산의 결과 값.

 

ex) reduce() 작동 방식 - initValue값이 없는 경우

var res = [0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array){
    console.log(`currentIndex : ${currentIndex}`);
    console.log(`accumulator : ${accumulator}`);
    console.log(`currentValue : ${currentValue}`);
    console.log(`currentIndex : ${currentIndex}`);
    console.log("                              ");
    return accumulator + currentValue;
}, 0);

console.log("res:", res);

//initialValue를 제공하지 않으면, reduce()는 인덱스 1부터 시작해 콜백 함수를 실행하고 첫 번째 인덱스는 건너 뜁니다.
//reduce()함수 호출시 initialValue 값이 없는 경우
//-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

reduce()가 반환하는 값으로는 마지막 콜백 호출의 반환값(10)을 사용합니다

 

 

ex) reduce() 작동 방식 - initValue값이 없는 경우

var res = [0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array){
    console.log(`currentIndex : ${currentIndex}`);
    console.log(`accumulator : ${accumulator}`);
    console.log(`currentValue : ${currentValue}`);
    console.log(`currentIndex : ${currentIndex}`);
    console.log("                              ");
    return accumulator + currentValue;
}, 10);

console.log("res:", res);

//initialValue를 제공하면 인덱스 0에서 시작합니다.
//reduce()함수 호출시 initialValue 값이 있는 경우
//-accumulator 는 initialValue
//-currentValue 는 배열의 첫번째 값
callback accumulator currentValue currentIndex array 반환 값
1번째 호출 10 0 0 [0, 1, 2, 3, 4] 10
2번째 호출 10 1 1 [0, 1, 2, 3, 4] 11
3번째 호출 11 2 2 [0, 1, 2, 3, 4] 13
4번째 호출 13 3 3 [0, 1, 2, 3, 4] 16
5번째 호출 16 4 4 [0, 1, 2, 3, 4] 20

이 때 reduce()가 결과로 반환하는 값은 20입니다.

 

ex) 객체 배열에서의 값 합산

객체로 이루어진 배열에 들어 있는 값을 합산하기 위해서는 반드시 초기값을 주어 각 항목이 여러분의 함수를 거치도록 해야 합니다.

var initialValue = 0;
var list = [
    {
        x : 1
    },
    {
        x : 2
    },
    {
        x : 3
    }
];
var sum = list.reduce(function (accumulator, currentValue) {
    console.log(`accumulator : ${accumulator}`);
    console.log(`currentValue : ${currentValue.x}`);
    console.log("                              ");   
    return accumulator + currentValue.x;
}, initialValue)

console.log(sum) // logs 6

 

ex) 중첩 배열 펼치기

var arr = [
    [0, 1],
    [2, 3],
    [4, 5]
]
var flattened = arr.reduce(function(accumulator, currentValue) {
        console.log(`accumulator : ${accumulator}`);
        console.log(`currentValue : ${currentValue}`);
        return accumulator.concat(currentValue);
    }
,[]);
console.log(flattened);
//[0, 1, 2, 3, 4, 5]

 

ex) 속성으로 객체 분류하기

var peoples = [
    {
        name : 'Alice',
        age : 21
    },
    {
        name : 'Max',
        age : 20
    },
    {
        name : 'Jane',
        age : 20
    }

];

function groupBy(objectArray, property){
    return objectArray.reduce(function (accumulator, currentObj) {
        var key = currentObj[property];
        console.log(`key : ${key}`);
        if (!accumulator[key]) {
            accumulator[key] = [];
        }
        accumulator[key].push(currentObj);
        return accumulator;
    }, {});
};

var groupedPeople = groupBy(peoples, 'age');
console.log(`groupedPeople : ${JSON.stringify(groupedPeople)}`);
//"age" 속성으로 객체 분류 (나이 별)

 

ex) 배열의 중복 항목 제거

let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
let result = arr.sort().reduce((accumulator, currentValue) => {
    console.log(`accumulator : ${accumulator}`);
    console.log(`currentValue : ${currentValue}`);
    console.log(`                              `);

    const length = accumulator.length
    if (length === 0 || accumulator[length - 1] !== currentValue) {
        accumulator.push(currentValue);
    }
    return accumulator;
}, []);
console.log(result); 
//[1,2,3,4,5]

 

ex) 프로미스를 순차적으로 실행하기

프로미스란? 

이전 포스팅 : [JS]Promise (프라미스) - 1

                   [JS]Promise (프라미스) - 2

function runPromiseInSequence(arr, input){
    return arr.reduce(function(accumulator, currentFunction){
        console.log(`accumulator : ${accumulator}`);
        console.log(`currentFunction : ${currentFunction}`);
        console.log(`                                    `);
        return accumulator.then(currentFunction);
    }, Promise.resolve(input));
};

function promise1(value){
    return new Promise(function(resolve, reject){
        console.log(`promise1 -value : ${value}`)
        resolve(value * 5);
    });
};

function promise2(value){
    return new Promise(function(resolve, reject){
        console.log(`promise2 -value : ${value}`)
        resolve(value * 2);
    });
};
  
function function3(value){
    return value * 3;
};

function promise4(value){
    return new Promise(function(resolve, reject){
        console.log(`promise4 -value : ${value}`)
        resolve(value * 4);
    });
};

const promiseArr = [promise1, promise2, function3, promise4, ]
runPromiseInSequence(promiseArr, 10)
.then(function(value){
    console.log(`result value : ${value}`);
});

 

'DEV > JS' 카테고리의 다른 글

[JS]맵(Map)과 셋(Set) -2  (0) 2019.12.04
[JS]맵(Map)과 셋(Set) -1  (0) 2019.12.04
[JS]Promise (프라미스) - 2  (0) 2019.11.21
[JS]Promise (프라미스) - 1  (0) 2019.11.20
[JS]Ajax  (0) 2019.11.19
Comments