map, reduce, filter 서로 대체가 가능할까 (javascript)

자바스크립트 배열을 처리하는 메서드 map, reduce, filter

자바스크립트에서 배열의 요소에 접근하는 대표 메서드인 map, reduce, filter는 개발자라면 필수적으로 그리고 좀 더 상세히 알고 가면 좋을 것 같습니다.

각각의 특징에 대해 이야기하면 대부분은 아마도

  • map -> 모든 요소에 한 번씩 접근하기
  • reduce -> 모든 요소를 하나로 합치기
  • filter -> 모든 요소에서 원하는 것만 골라내기

정도의 개념을 갖고 있을 것 같습니다.

굳이 위 메서드가 아니라 forEach 문 등 다른 방식을 사용해도 같은 로직의 구현이 가능한데요.

그렇다면 위 메서드끼리도 서로 변경하여 같은 결과를 반환하도록 사용이 가능할까요?

각 메서드의 기능을 살펴보면서 알아보도록 하겠습니다.


1. map

먼저 메서드는 위와 같으며 *가 붙어있는 파라미터는 생략할 수 있습니다.

인덱스는 현재값의 인덱스, 배열은 map이 순회하는 배열 array를 의미합니다.

이제 map을 사용하는 가장 기본적인 코드를 확인해 보겠습니다.

const myArray = [10, 20, 30, 40, 50];

const newArray = myArray.map((value) => { return value });

console.log(newArray);  // [10, 20, 30, 40, 50]
console.log(myArray===newArray); //false

map은 해당 배열 자체를 바꾸는 것이 아니므로 리턴값을 사용하기 위해서는 다른 변수에 넣어 주어야 합니다.

파라미터에서 index는 현재 접근한 요소의 index이며 array는 순회하는 배열 전체를 전달합니다.

const myArray = [10, 20, 30, 40, 50];

const newArray = myArray.map((value, index, array) => {
   if(index%2){
     return value-array[1];   
   }else{
     return value-array[0];
   }                              
});

console.log(newArray);

다음 코드는 자주 접하는 실수 중 하나로 코드의 동작 여부를 생각해 볼 필요가 있습니다.

const myData = {10, 20, 30, 40, 50};

const newData = myData.map((value) => {return value+1});

console.log(newData);

위 코드는 에러가 발생합니다.

바로 map의 특성 때문인데요. map은 배열에서 동작하는 메서드이며 객체 object에서는 동작하지 않습니다.

하지만 다음과 같이 배열 내부의 object는 접근 가능합니다.

const myData = [{name:'chamchi', age:3},{name:'ggongchi', age:2},{name:'myulchi', age:1}];

const newData = myData.map((value) => value.name );

console.log(newData); //['chamchi', 'ggongchi', 'myulchi']

그렇다면 map을 사용해 reduce나 filter 메서드와 같은 로직을 구현할 수 있을까요?

다음 코드를 통해 확인할 수 있을 것 같습니다.

const myArray = [1, 2, 3, 4, 5];

const newArray = myArray.map((value) => {
   if(value%2){
     return value;   
   }                            
});

console.log(newArray); //[1, undefined, 3, undefined, 5]

홀수의 요소만 반환하고자 위와 같이 작성하더라도 map은 모든 요소를 반환합니다.

짝수 부분은 리턴값이 정의되지 않아 undefined로 반환된 것을 확인할 수 있습니다.

따라서 map은 reduce, filter의 기능은 구현할 수 없을 것 같습니다.


2. reduce

먼저 메서드는 위와 같으며 *가 붙어있는 파라미터는 생략할 수 있습니다.

누적값은 배열을 순회하면서 작업의 처리 결과를 누적하는 값으로 순회가 종료되면 최종 리턴값이 됩니다.

reduce의 예시로 모든 요소를 더하는 예시가 많다보니 용도는 전체 요소 더하거나 빼서 하나로 만들기라고

알고 있을 수 있지만 누적값 설정을 통해 다양한 방식으로 활용이 가능합니다.

인덱스는 현재값의 인덱스, 배열은 reduce가 순회하는 array 배열을 의미합니다.

마지막에 있는 시작값은 누적값의 처음 기본값을 전달합니다.

생략하면 0이 기본값이 됩니다.

const myArray = [10, 20, 30, 40, 50];
const newArray = myArray.reduce((acc, value) => { return acc + value });

console.log(newArray); // 150

시작값이 생략되었으므로 acc의 기본값은 0이 됩니다.

그럼 초기값을 설정한 샘플을 보겠습니다.

const myArray = [10, 20, 30, 40, 50];
const newArray = myArray.reduce((acc, value) => { return acc + value },100);

console.log(newArray); // 250

이를 응용하여 초기값을 설정하면 다양한 결과를 만들 수 있습니다.

먼저 배열을 다른 배열로 복사하는 기능을 구현해 보겠습니다.

const myArray = [10, 20, 30, 40, 50];
const newArray = myArray.reduce((acc, value) => { 
     acc.push(value);
     return acc; 
 },[]);

console.log(newArray); // [10, 20, 30, 40, 50]

이것으로 map과 똑같은 로직의 구현이 가능합니다.

그렇다면 filter를 구현해 보겠습니다.

const myArray = [10, 20, 30, 40, 50];
const newArray = myArray.reduce((acc, value, index) => { 
     if(index%2){
       acc.push(value);
     }
     return acc; 
 },[]);

console.log(newArray); // [20, 40]

코드를 통해 확인해보니 reduce는 초기값 설정을 통해 map이나 filter와 같은 로직의 구현이 가능합니다.


3. filter

filter는 말 그대로 배열의 요소를 필터링하여 조건이 참이 되는 요소만 반환합니다.

const myArray = [10, 20, 30, 40, 50];

const newArray = myArray.filter((value) => { 
    return value%20 === 0
 });

console.log(newArray); // [20, 40]

filter는 필터링 기능만 갖고 있어 map이나 reduce의 로직을 만들긴 어려울 것 같습니다.



map과 filter는 용도가 조금 더 특정되어 있다보니 다른 메서드의 로직을 그대로 구현하기는 어렵지만 reduce는 초기값을 통해 다양한 로직의 구현이 가능해 다재다능한 메서드인 것 같습니다.