[jQuery]map()과reduce()란 무엇일까?

2020년 07월 16일 by Xion

    [jQuery]map()과reduce()란 무엇일까? 목차

실무에서 하드 코딩을 하다가  배열 안에서 중복되는 값들을 count 해야 하는 상황이 왔다.

찾아보니 reduce()라는 기능을 가진 녀석이 있었는데 글을 적으며 이해하기 위해 노력해본다.

ex)  arr = {"김동수","김동수","김영희","김영희"} 라는 녀석이 있는데 중복 값을 count 해야 한다.

 

얼핏 정리한 글들을 봤는데 자바스크립트 내장 메서드 중에서 제일 강력하다는 글을 보았다.

먼저 map()을 살펴보자.

 

map()이란?


 

 

 

map의 기본 원리는 간단한데,

반복문을 돌며 배열 안의 요소들을 1:1로 짝지어 주는 것이다.

어떻게 짝지어줄 것인가 정의한 함수를 메서드의 인자로 넣어주면 된다.

 

// map 활용
const arr = [1,2,3];

let result = arr.map( (result) => {

	console.log(result)
    return result;
 });
 
 // console = 1,2,3
 arr;  // [1,2,3]
 result; // [1,2,3]
 
 arr === result  // false
 
 

반복문으로 요소를 순회하여 각 요소를 어떻게 짝지어줄지 알려주는데

여기서는 return 값으로 result를 반환하였으니 값은 그대로 짝짓습니다.

중요한 점은, map을 실행하는 배열과 결과로 나오는 배열이 다른 객체라는 것입니다.

 

map은 규칙적인 배열만 반환하는 것이 아니라, 함수 안에 적어준 대로 반환할 수 있기 때문에 자유도가 높습니다.

 

abc = arr.map( (result) => {

	return result+1;
});

result; // [ 2, 3, 4]

이런 식으로 함수에서 사용자가 임의로 정의를 하면 result 값이 바뀌는 것을 볼 수 있습니다.

 

한 가지 예시를 더 살펴보도록 하겠습니다.

abc = arr.map( (result) => {

	
	if(result % 2)
    {
    	return '홀수';	
    }
    return '짝수';
    

});

result // ['홀수','짝수','홀수']

 

이렇게

map()은 배열을 1:1로 짝을짓지만 절대 기존 객체를 수정하지 않는 메서드입니다.

(return 해서 나오는 값과 기존 arr은 다른 객체입니다. )

 

 

 

reduce()란?


 

reduce는 "줄이다"라는 의미를 가지고 있습니다.

배열을 순회하며 인덱스 데이터를 줄여가며 어떠한 기능을 수행할 수 있죠.

reduce()란 덧셈 함수가 아닙니다.

reduce() 메서드의 기본 문법을 먼저 알아보겠습니다.

배열.reduce( ( 누적값, 현재값, 인덱스, 요소 ) =>
{
	return 결과
    
 }, 초깃값);
 
 

이런 식으로 사용됩니다.

누적 값이라는 것을 잘 알고 계셔야 합니다.!!

 

reduce()는 기본적으로 다른 메서드와 동일하게 첫 번째 인자로 함수를 받습니다.

단, 두 번째 인자로 초기값을 세팅할 수 있다는 것에서 차이가 있습니다.

물론 두 번째 인자는 생략이 가능하며 생략 시에는 첫 번째 값이 그 값을 담당합니다

 

 

 

바로 예제를 살펴보도록 하겠습니다.

abc = arr.reduce( ( acc(누적값), cur(현잿값), i )  => {

		console.log( acc, cur , i );
        return acc + cur;
        
}, 0(초깃값) );

//결과

  (acc)	  (cur)	(i)
  누적값  현잿값  i
0 	 1 	   0
1 	 2 	   1
3 	 3 	   2

abc; // 6

 

acc ( 누적 값 )이 초깃값인 0부터 시작하여 return 하는 대로 누적되는 것을 볼 수 있습니다.

(여기서 초깃값이 0으로 설정되었기 때문에 처음 누적 값은 0입니다.)

또한, 초깃값을 적어주지 않으면 자동으로 초깃값이 0번째 인덱스의 값이 됩니다.

 

-초깃값을 적지 않은 경우

//초깃값 제거
abc = arr.reduce( ( acc(누적값), cur(현잿값), i )  => {

		console.log( acc, cur , i );
        return acc + cur;
        
}, );

//결과

  (acc)	  (cur)	(i)
  누적값  현잿값  i
1 	 2 	   1
3 	 3 	   2

abc; // 6

 

reduce() 메서드는 덧셈뿐만 아니라 초깃값을 활용하여 무궁무진한 방법을 활용할 수 있게 해 줍니다.

또한 map, filter와 같은 함수형 메서드를 모두 reduce로 구현할 수 있습니다.

 

map을 구현한 reduce()

abc = arr.reduce( (acc,cur) => {

	acc.push( cur % 2 ? '홀수' : '짝수 ');
    return acc;
 }, [] );			//초깃값을 배열로 만들었습니다.
 
 result; // ['홀수','짝수','홀수']
 
    

배열에 값들을 push 하면 map과 같아집니다. 여기서, 조건부로 push를 하면 filter와 같아집니다.

 

홀수만 필터링하는 code (조건부) filter와 같은 기능

abc = arr.reduce( ( acc, cur ) => {

		if( cur % 2 )
        {
       	 	acc.push(cur);
        }
         
         return acc;
         
 }, [] );
 
 
 //결과
 result;  // [1,3]

 

 

저가 사용하고 싶었던 함수는 바로 이것을 활용하였습니다.  ( 뭐, 대충 count 세는 로직이 필요했었던...)

const name = ['홍길동','홍길동','김영희','김영희','바둑이','김영희','바둑이'];

const result = name.reduce( (object, currentValue ) => {

	if( !object[currentValue]) 
    {
    	obect[currentValue] = 0 ; 
    }
    
    object[currentValue]++;
    
    return object;

}, {} );							//초기값을 {}로 초기화

console.log(result);

=> {홍길동 : 2, 김영희 : 3 , 바둑이 : 2 } 라는 값이 출력이 된다.

결론


reduce()는 map(), filter()의 차이, 그리고 적재적소에 사용하는 다양한 방법에 있습니다.

 

1.reduce vs map

 

//reduce()활용

var arr = [1,2,3];

//초기값
var init = [];

// 2씩 곱하는 함수를 만들었습니다
var reduefunc = function ( acc,value) {
	
    
    acc.push( value * 2 );
    return acc;
    
 };
 
 
 //기존 배열에서 reduefunc 함수만든 것을 적용
 //첫 번째 값으로 함수, 두 번째 값으로는 초기값을 주었습니다. (기존과 같은 문법임 따지고보면)
 var result = arr.reduce( reduefunc, init );
 console.log(result) // [2,4,6]
 
 //map()활용
 var result2 = arr.map( x => x*2 );
 console.log(result2); // [2,4,6]

 

간단한 로직 같은 경우 reduce() 메서드 보다 map() 메서드가 더욱 직관적이고 짧습니다.

 

2.reduce vs filter

 

 

var arr [1,2,3,4,5,6];

//초기값을 위해 지정
//배열이므로 초깃값 [] 을 지정해줌
var init = [];

var reducer = function( acc , val ) {
	
    if( val % 2  != 0 )
    {
    	acc.push(val);	
    }
    
    return acc;

};

//만들어둔 reducer function을 넘깁니다.
var result = arr.reduce(reducer,init);
console.log(result); // [ 1,3,5]

//filter() 메소드 적용
var result2 = arr.filter( x => x % 2 !=0 );
console.log(result2); //[1,3,5]

fiter() 메서드 역시 reduce() 메서드 보다 더욱 직관적으로 보입니다.

 

하지만, 1번 상황과 2번 상황을 동시에 작업을 해야 한다면 어떨까요?

원래 배열의 값 ( arr ) 중   arr % 2의 나머지가 0이 아닌 값들을 골라서 2배를 곱한 배열을 return 하고자 한다면?

->이럴 경우 reduce()는 한 번에 작업이 끝나지만 map()과 filter()는 두 번의 작업을 거치게 됩니다.

 

3.reduce vs filter + map

var arr = [1,2,3,4,5,6];

var init = [];

//reduce function 대신 미리 작성
var reducer = function( acc,val ) {

	if( val % 2 != 0)
    {
    	acc.push( val * 2 );
    }
    return acc;
 }
 
 //정의해둔 reducer와 초기값을 인자로 넘김
 var result = arr.reduce( reducer, init );
 [2,6,10]
 
 //filter를 통해 먼저 2로나눈 나머지가 0이아닌 애들을 검사 후
 //map을 이용하여 값을 저장함 총 2번의 과정이 필요로 함.
 var result2 = arr.filter( x => x % 2 != 0 ).map( x => x * 2 );
 // [2,6,10]

이처럼 결과는 같지만 reduce()는 한 번의 과정이 진행되는 반면 map과 filter는 서로 1번씩 총 두 번의 과정이 필요로 하게 됩니다.

상황에 맞게 데이터 크기, 종류에 맞는 선택을 하시길 바랍니다!