본문으로 바로가기

[JavaScript] map, filter, reduce / 반복 후 새로운 배열 반환

이전 게시글 에서 for, for...in, for...of, forEach에 대해 알아보았습니다. 앞 4개의 구문은 반복만 하고 끝입니다. 새로운 배열을 만들고 싶은 경우 빈 배열을 생성하여 추가해주어야 합니다. 만약 반복을 다 한 후 새로운 배열을 만들고 어떻게 할까요? 앞서 말한대로 빈 배열에 추가하여도 되지만 map, filter, reduce 를 사용해도 됩니다. 한번 알아봅시다. (아래 예시들은 설명을 위한 예시이므로 참고만 하시면 됩니다.)

map

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

map 함수는 위와 같이 생겼습니다. 배열에 사용할 수 있고 반복을 한 뒤 새로운 배열을 반환해줍니다. 간단한 예시로는 모든 배열 값에 *2를 하고 싶다 라고 하면 아래와 같이 구현할 수 있습니다.

const numbers = [1,2,3,4,5];
const doubleNumbers = numbers.map(item => item * 2);

console.log(doubleNumbers);

매우 간단하죠? 만약 위 과정을 for문으로 구현한다고 해봅시다.

const numbers = [1,2,3,4,5];
const doubleNumbers = [];
const doubleNumbersForEach = [];

for(let i = 0 ; i < numbers.length ; i++) {
    doubleNumbers.push(numbers[i] * 2);
}

numbers.forEach(item => {
    doubleNumbersForEach.push(item * 2);
})

console.log(doubleNumbers);

결과는 동일하지만 가독성의 차이는 분명히 있습니다. 그렇다면 이런 생각이 들 수 있습니다. 처음부터 map만 사용해서 구현하면 되지 않을까? 실제로 신입분들의 코드를 보면 map을 순수 반복만 하는 용도로 사용하는 분들이 있었습니다. map은 반복을 한 후 새로운 배열을 return 해줍니다. 만약 반복만 하는 것이었으면 for 를 사용하는 것이 맞습니다. 1가지 예를 들어봅시다.

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

numbers.forEach(num => console.log(num))
numbers.map(num => console.log(num))

위 코드의 결과는 (거의)동일합니다. 그러나 map의 경우 용도에 맞게 사용하지 않았다고 볼 수 있습니다. 실제 결과 이미지를 보아도 마지막 undefined 5개의 배열이 출력되어 있습니다. 무엇이든 용도에 맞게 사용하는 습관을 길러야 합니다.

filter

arr.filter(callback(element[, index[, array]])[, thisArg])

filter는 이름에서 볼 수 있듯이 filtering을 하겠다 입니다. 배열을 반복하면서 특정 조건에 맞는 값만 알고 싶을 때 filter를 사용합니다. 바로 예제를 확인해봅시다. 아래 코드는 배열 중 홀수인 것만 골라내는 예제입니다.

const numbers = [1,2,3,4,5];
const oddNumbers = numbers.filter(num => num % 2)

console.log(oddNumbers);

이번에는 for문 예시를 안보여드려도 되겠죠? 분명히 위 코드보다 더 길고 복잡할 것입니다. 그렇다면 이런 생각을 할 수 있습니다. map이랑은 비슷한거 같은데 map으로 하면 안되나? 안됩니다. map은 전체 배열을 return 해주기 때문입니다. 배열 크기가 10이라면 return 되는 배열도 10개의 크기인 것이죠. 이해가 잘 안되시면 위 filter로 구현한 것을 map으로 구현해보시길 바랍니다. 직접 해보면 무슨 의미인 지 쉽게 파악할 수 있을 것입니다.

reduce

arr.reduce(callback[, initialValue])

위 reduce 함수를 더 쉽게 자주 사용하는 코드 형식으로 적어보겠습니다.

arr.reduce((acc, cur) => {
    // ...
}, initialValue)

acc는 누적 값, cur는 현재 값입니다. reduce로 위 map, filter 함수를 모두 구현할 수 있고 2가지 일을 동시에 할 수도 있습니다. map, filter를 구현해보고 2가지 일을 동시에 하는 것도 한번 구현해보겠습니다. 우선 위에서 구현해본 map, filter와 동일한 동작을 하도록 구현해보겠습니다.

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

const mapReduce = numbers.reduce((acc, cur) => {
    return acc.concat(cur * 2);
}, []);

const filterReduce = numbers.reduce((acc, cur) => {
    if(cur % 2) return acc.concat(cur); 
    return acc;
}, []);

console.log(mapReduce);
console.log(filterReduce);

위에서 본 결과와 동일합니다. 그럼 이번에는 2가지 일을 동시에 하도록 홀수만 filter한 후 해당 홀수 값에 2를 곱한 배열을 만들어봅시다. map, filter를 사용한 코드와 reduce를 사용한 코드를 비교해봅시다.

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

const resultWithMapFilter = numbers.filter(num => num % 2).map(num => num * 2);

const resultWithReduce = numbers.reduce((acc, cur) => {
    if(!Boolean(cur % 2)) return acc;
    return acc.concat(cur * 2);
}, [])

console.log(resultWithMapFilter)
console.log(resultWithReduce)

위 결과는 동일합니다. 예시가 너무 쉬운 것이서 filter, map이 더 짧아보이긴 하는데 분명히 2가지 일을 같이할 때가 있을 것입니다. 그 경우를 대비해서 reduce도 공부하는 것이 좋습니다. 이것 뿐만이 아니라 속도에서 분명히 차이가 있을 것입니다. filter, map을 하게 되면 반복문을 2번 돌아야 합니다. 그러나 reduce의 경우 1번으로 해결할 수 있기 때문에 속도면에서도 더 빠릅니다.

◆ 간단정리

map, filter, reduce는 새로운 배열을 반환해준다.

상황에 따라 용도에 맞게 사용하면 된다.

reduce 1개로 map, filter를 동시에 할 수 있다.

◆ 참고자료

마지막

해당 내용은 틀릴 수도 있다는 것을 감안하여 봐주세요. 틀린 내용 및 오탈자 수정 요청 환영입니다.

반응형