참 많고 많은 배열의 메서드가 있지만 잘 활용하지 못한다는 생각이 드는 오늘입니다..ㅎㅎ
오늘의 공부 주제는 reduce() 친구입니다! 이 친구가 굉장히 강력한 녀석이라고 하더라구요. 은근 활용법들이 많아서 한 번 차근차근 정리해보려고 합니다.
기본 문법
array.reduce(<콜백함수>, <초기값>);
//예시 코드
const array1 = [1, 2, 3, 4];
// 0 + 1 + 2 + 3 + 4
const initialValue = 0;
const sumWithInitial = array1.reduce(
(accumulator, currentValue) => accumulator + currentValue,
initialValue,
);
console.log(sumWithInitial);
// Expected output: 10
reduce()메서드는 배열의 각 요소에 대해 주어진 콜백 함수(reducer 함수)를 실행하고, 하나의 결과값을 반환한다.
콜백 함수(reducer 함수)에는 총 4개의 인자를 가지지만, 대부분의 경우에는 첫 2개의 인자만 필요합니다.
accumulator: 이전 요소를 상대로 콜백 함수를 실행한 결과(누적자)
currentValue: 현재 요소의 값
currentIndex: 현재 요소의 인덱스 (initialValue를 제공한 경우 0, 아니면 1부터 시작)
array: reduce() 메서드를 호출하는 배열
initialValue: 콜백의 최초 호출에서 첫 번째 인수에 제공하는 값. 초기값을 제공하지 않으면 배열의 첫 번째 요소를 사용. 빈 배열에서 초기값 없이 reduce()를 호출하면 오류가 발생함!
콜백 함수의 반환 값은 accumulator에 할당되고, 누적된 값은 순회 중에 유지되므로 결국 최종 결과는 하나의 값이 됩니다.
reduce()의 가장 기본적인 활용은 누적 계산을 할 때!
//배열에 들어있는 숫자의 누적합 구하기
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 10
👉 sum을 const 키워드로 선언하여 변경하지 못하게 할 수 있기에 더 견고한 코드 작성 가능
//숫자 배열에서 최소값, 최대값 계산
const numbers = [1, 2, 3, 4];
const min = numbers.reduce((min, num) => (min < num ? min : num));
console.log(min); // 1
const max = numbers.reduce((max, num) => (max > num ? max : num));
console.log(max); // 4
👉 초기값을 생략함: 배열의 첫 번째 값이 초기값으로 사용 됨
reduce()는 원소의 개수를 셀 때에도 많이 사용합니다!
//배열에 들어 있는 각 과일의 개수 세기
const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];
const fruitCounts = fruits.reduce(
(counter, fruit) => ({
...counter,
[fruit]: fruit in counter ? counter[fruit] + 1 : 1,
}),
{}
);
console.log(fruitCounts); // { apple: 3, banana: 2, orange: 1 }
타입스크립트에서 reduce() 메서드를 사용할 때는 타입 오류가 발생하기 쉽습니다.
개수 세기에서의 예시 코드 같은 경우에는 초기값으로 빈 객체를 넘기기 때문에, counter의 타입이 빈 객체로 추론되기 때문입니다.
타입 오류를 해결하려면 fruits.reduce<{ [key: string]: number }>(와 같이reduce<결과 타입>의 형태로 결과 값의 타입을 표시해주면 됩니다. 또는 타입스크립트의 유틸리티 타입인 Record를 활용해도 됩니다.
2차원 배열을 1차원 배열로 평탄화할 때도 쓰일 수 있습니다!
const nested = [
[1, 2],
[3, 4],
[5, 6],
];
const flattened = nested.reduce((nums, num) => [...nums, ...num], []);
console.log(flattened); // [1, 2, 3, 4, 5, 6]
객체 배열에서 특정 속성의 값만 추출하고 싶을 때도 사용 가능합니다!
//각 사용자의 country 값을 중복 없이 수집
const users = [
{ name: "John", age: 25, country: "US" },
{ name: "Jane", age: 30, country: "KR" },
{ name: "Robin", age: 22, country: "CA" },
{ name: "Doe", age: 13, country: "US" },
{ name: "Smith", age: 20, country: "KR" },
];
const distinctCountries = users.reduce((countries, user) => {
countries.add(user.country);
return countries;
}, new Set()); //초기값: Set(중복을 허용하지 않는 유일한 값 집합, 국가 값을 추가할 때 자동으로 중복 제거됨)
console.log(distinctCountries); // { "US", "KR", "CA" }
초기값으로 자료구조인 Set를 사용했는데, 이와 관련한 자세한 개념은 따로 정리해서 올려야겠습니당..ㅎ
reduce()는 데이터를 그룹화할 때 빛을 발한다고 합니다.
// 사용자 배열을 국가 기준으로 분류
const users = [
{ name: "John", age: 25, country: "US" },
{ name: "Jane", age: 30, country: "KR" },
{ name: "Robin", age: 22, country: "CA" },
{ name: "Doe", age: 13, country: "US" },
{ name: "Smith", age: 20, country: "KR" },
];
const usersByCountry = users.reduce((users, user) => {
const country = user.country;
if (!(country in users)) {
users[country] = [];
}
users[country].push(user);
return users;
}, {});
console.log(usersByCountry);
reduce()를 사용해서 이렇게 분류를 하게 되면, 각 국가를 키로 해당 국적자 목록 객체가 만들어집니다.
{
US: [
{ name: "John", age: 25, country: "US" },
{ name: "Doe", age: 13, country: "US" }
],
KR: [
{ name: "Jane", age: 30, country: "KR" },
{ name: "Smith", age: 20, country: "KR" }
],
CA: [
{ name: "Robin", age: 22, country: "CA" }
],
}
사실 문제 풀면서 단순히 누적 계산을 하려고 찾아본 reduce()였으나.. 활용도가 높은 메서드였다는 사실에 큰 깨달음을 얻게 되었습니다.
아직은 체화시키기 어려운 것 같지만 빠르게 습득해보길~!
나 자신 화이탱❗