학습 자료

배열 메서드 — map, filter, reduce


Article

9강에서 배열을 돌 때 for문을 썼다. 이제 더 나은 방법을 배운다.

11강에서 배운 콜백이 여기서 진가를 발휘한다. "어떻게 반복할지"는 배열에 맡기고, 우리는 "각 요소로 무엇을 할지"만 말한다.

forEach — 그냥 하나씩 돈다

const fruits = ["사과", "바나나", "포도"];

for (let i = 0; i < fruits.length; i++) {
  console.log(fruits[i]);
}

fruits.forEach(fruit => console.log(fruit));
콘솔
  • 사과
  • 바나나
  • 포도
  • 사과
  • 바나나
  • 포도

i도, length도, fruits[i]도 없다. 실수할 자리가 사라졌다.

콜백은 인자를 세 개까지 받는다. 필요한 만큼만 쓰면 된다.

["a", "b"].forEach((value, index, array) => {
  console.log(index, value, array.length);
});
콘솔
  • 0 'a' 2
  • 1 'b' 2

그리고 forEach아무것도 돌려주지 않는다. 값을 만들어내려면 다른 메서드가 필요하다.

map — 하나씩 바꿔서 새 배열을 만든다

가장 많이 쓰는 메서드다.

const numbers = [1, 2, 3];

const doubled = numbers.map(n => n * 2);

console.log(doubled);
console.log(numbers);
콘솔
  • [2, 4, 6]
  • [1, 2, 3]

원본은 그대로다. 길이가 같은 새 배열이 만들어진다.

map 은 일대일로 옮긴다
  1. [1, 2, 3]원본
  2. n => n * 2각 요소에 적용
  3. [2, 4, 6]새 배열 (길이 같음)

객체 배열에서 특정 값만 뽑을 때 자주 쓴다.

const users = [
  { name: "지원", age: 27 },
  { name: "민수", age: 31 },
];

const names = users.map(user => user.name);
console.log(names);
콘솔
  • ['지원', '민수']

filter — 조건에 맞는 것만 남긴다

콜백이 true를 돌려준 요소만 살아남는다.

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

const evens = numbers.filter(n => n % 2 === 0);
const big = numbers.filter(n => n > 4);

console.log(evens);
console.log(big);
콘솔
  • [2, 4, 6]
  • [5, 6]

map과 달리 길이가 줄어든다. 하나도 안 맞으면 빈 배열이 된다.

map 과 filter

map

변환

  • 길이가 그대로
  • 콜백의 반환값이 새 요소
  • "각각을 무엇으로 바꿀까"

filter

선별

  • 길이가 줄어든다
  • 콜백의 참·거짓으로 판단
  • "각각을 남길까 버릴까"

7강의 truthy·falsy가 여기서 쓰인다. filter(Boolean)은 falsy 값을 전부 걷어내는 관용구다.

const messy = [1, null, 2, "", 3, undefined, 0];

console.log(messy.filter(Boolean));
콘솔
  • [1, 2, 3]

reduce — 전부 하나로 접는다

가장 어렵지만 가장 강력하다. 배열을 훑으며 값 하나로 줄여나간다.

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

const sum = numbers.reduce((acc, cur) => acc + cur, 0);

console.log(sum);
콘솔
  • 10

콜백이 인자를 둘 받는다. acc는 지금까지 쌓인 값(accumulator), cur는 이번 요소(current)다. 마지막 0초기값이다.

reduce 가 접어가는 과정
  1. 01

    초기값에서 시작

    acc0. 아직 아무것도 더하지 않았다.

    acc = 0
  2. 02

    첫 요소

    acc(0) + cur(1) → 다음 acc1.

    0 + 1 = 1
  3. 03

    계속 접는다

    1 + 2 = 3, 3 + 3 = 6, 6 + 4 = 10.

  4. 04

    마지막 acc 가 결과

    돌 게 없으면 acc 를 반환한다.

    console.log(sum);  // 10
acc 가 자라는 모습
시작0
+11
+23
+36
+410

숫자만 더하는 게 아니다. 객체를 만들 수도 있다.

const words = ["apple", "banana", "avocado", "blueberry"];

const grouped = words.reduce((acc, word) => {
  const key = word[0];
  if (!acc[key]) acc[key] = [];
  acc[key].push(word);
  return acc;
}, {});

console.log(grouped);
콘솔
  • { a: ['apple', 'avocado'], b: ['banana', 'blueberry'] }

찾기 — find, some, every

filter는 전부 훑지만, 하나만 찾으면 되는 경우가 있다.

메서드돌려주는 것언제 멈추나
find첫 번째로 맞는 요소찾는 즉시
findIndex첫 번째로 맞는 인덱스찾는 즉시
some하나라도 맞으면 true맞는 걸 찾는 즉시
every전부 맞아야 true안 맞는 걸 찾는 즉시
const users = [
  { name: "지원", age: 27 },
  { name: "민수", age: 15 },
];

console.log(users.find(u => u.age < 19));
console.log(users.some(u => u.age < 19));
console.log(users.every(u => u.age < 19));
console.log(users.find(u => u.age > 100));
콘솔
  • { name: '민수', age: 15 }
  • true
  • false
  • undefined

find는 못 찾으면 undefined를 준다. 15강의 indexOf-1을 주던 것과 다르다.

some은 6강의 ||처럼 너그럽고, every&&처럼 까다롭다.

체이닝 — 이어붙인다

이 메서드들은 모두 배열을 돌려주므로 점으로 이어 쓸 수 있다.

const products = [
  { name: "노트북", price: 1200000, inStock: true },
  { name: "마우스", price: 30000, inStock: true },
  { name: "모니터", price: 400000, inStock: false },
];

const total = products
  .filter(p => p.inStock)
  .map(p => p.price)
  .reduce((acc, price) => acc + price, 0);

console.log(total);
콘솔
  • 1230000

읽어보면 그대로 문장이다. "재고가 있는 것만 골라서, 가격만 뽑아서, 전부 더한다."

for문으로 쓰면 같은 일이 이렇게 된다.

let total = 0;
for (let i = 0; i < products.length; i++) {
  if (products[i].inStock) {
    total += products[i].price;
  }
}

동작은 같지만 무엇을 하는지가 아니라 어떻게 도는지가 먼저 보인다.

정리하면

  • forEach는 실행만 한다. 값을 돌려주지 않고 멈출 수도 없다.
  • map은 길이를 유지하며 변환하고, filter는 조건으로 걸러낸다.
  • reduce는 배열을 값 하나로 접는다. 콜백은 다음 acc를 반드시 반환하고, 초기값을 준다.
  • 하나만 찾으면 find, 존재 여부는 some, 전부 만족하는지는 every.
  • 이 메서드들은 원본을 바꾸지 않는다. 체이닝으로 이어 쓴다.

다음 강의는 객체다. 배열이 순서로 값을 찾는다면, 객체는 이름으로 값을 찾는다.

배열 메서드 — map, filter, reduce — 디코드랩(DCODELAB)