Array.prototype.reduce()
주의 : reduce는 원본배열을 변환시키지 않는다.
reduce 메서드는 자신을 호출한 배열을 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출한다.
그리고 콜백 함수의 반환값을 다음 순회 시에 콜백 함수의 첫 번째 인수로 전달하면서 콜백 함수를 호출하여 하나의 결과값을 만들어 반환한다.
이때 원본 배열은 변경되지 않는다
reduce 메서드는 자신을 호출한 배열의 모든 요소를 순회하며 하나의 결과값을 구해야 하는 경우에 사용한다.
const sum = [1, 2, 3, 4].reduce((accumulator, currentValue, index, array)
=> accumulator + currentValue, 0)
console.log(sum) // 10
맨 마지막 0 이 초기값이다
이것은 10으로 하면 초기값이 10이된다.
reduce 메서드의 콜백 함수는 4개의 인수를 전달받아 배열의 length만큼 총 4회 호출된다.
이때 콜백 함수로 전달되는 인수와 콜백 함수의 반환값은 다음과 같다.
구분 | 콜백 참수에 전달되는 인수 | 콜백 함수의 반환값 | |||
accumulator | currentvalue | index | array | ||
첫 번째 순회 | 0(초기 값) | 1 | 0 | [1,2,3,4] | 1 (accumlator + currentValue) |
두 번째 순회 | 1 | 2 | 1 | [1,2,3,4] | 3 |
세 번째 순회 | 3 | 3 | 2 | [1,2,3,4] | 6 |
네 번째 순회 | 6 | 4 | 3 | [1,2,3,4] | 10 |
다양한 활용법
평균 구하기
const values = [1, 2, 3, 4, 5, 6];
const average = values.reduce((acc, cur, i, { length } ) => {
// 마지막 순회가 아니면 누적값을 반환하고 마지막 순회면 누적값으로 평균을 구해 반환한다.
return i === length - 1 ? (acc + cur) / length : acc + cur;
}, 0);
console.log(average); // 3.5
이게 잘 이해가 안된다면
const values = [1, 2, 3, 4, 5, 6];
const average = values.reduce((acc, cur, i, {length} ) => {
console.log(
acc,
cur,
i,
length
)
return i === length - 1 ? (acc + cur) / length : acc + cur;
}, 0);
console.log(average); // 3.5
//0 1 0 6
//1 2 1 6
//3 3 2 6
//6 4 3 6
//10 5 4 6
//15 6 5 6
//3.5
//length 에서 {} 이걸 벗기면
[ 1, 2, 3, 4, 5, 6 ] 이렇게 출력이된다.
이건 아직 이해 x
최대값 구하기
const values = [ 1, 2, 3, 4, 5];
const max = values.reduce((acc, cur) => (acc > cur ? acc : cur), 0);
console.log(max); // 5
최대값을 구할 때는 reduce 메서드보다 Math.max 메서드를 사용하는 방법이 더 직관적이다.
const values = [1, 2, 3, 4, 5];
const max = Math.max(...values);
console.log(max); // 5
요소의 중복 횟수 구하기
const fruits = ['banana', 'apple', 'orange', 'orange', 'apple'];
const count = fruits.reduce((acc, cur) => {
// 첫 번째 순회 시 acc는 초기값인 {}이고 cur은 첫 번째 요소인 'banana'다.
// 초기값으로 전달받은 빈 객체에 요소값인 cur을 프로퍼티 키로, 요소의 개수를 프로퍼티 값으로 할당한다.
// 만약 프로퍼티 값이 undefined(처음 등장하는 요소)이면 프로퍼티 값을 1로 초기화한다.
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
//콜백 함수는 총 5번 호출되고 다음과 같이 결과값을 반환한다.
/* {banana: 1} => {banana: 1, apple: 1} => {banana: 1, apple: 1, orange:1}
=> {banana:1, apple: 1, orange: 2} => {banana: 1, apple: 2, orange: 2}
console.log(count); // {banana: 1, apple: 2, orange: 2}
이해가 잘 되지 않는다면
const fruits = ['banana', 'apple', 'orange', 'orange', 'apple'];
const count = fruits.reduce((acc, cur) => {
console.log(
acc,
cur,
acc[cur]
)
acc[cur] = (acc[cur] || 0) + 1;;
return acc;
}, {});
console.log(count)
---------------------------
객체 key values 지정하는 법
let box = {}
let papa = 'test'
box[papa] = 1
console.log(box)
//{} banana undefined
//{ banana: 1 } apple undefined
//{ banana: 1, apple: 1 } orange undefined
//{ banana: 1, apple: 1, orange: 1 } orange 1
//{ banana: 1, apple: 1, orange: 2 } apple 1
//{ banana: 1, apple: 2, orange: 2 }
--------------------------
//{ test: 1 }
중첩 배열 평탄화 <Array.prototype.concat()>
const values = [1, [2, 3], 4, [5, 6]];
const flatten = values.reduce((acc, cur) => acc.concat(cur), []);
// [1] => [1,2,3] => [1,2,3,4] => [1,2,3,4,5,6]
console.log(flatten); // [1, 2, 3, 4, 5, 6]
concat() 메서드는 인자로 주어진 배열이나 값들을 기존 배열에 합쳐서 새 배열을 반환합니다.
const array1 = ['a', 'b', 'c'];
const array2 = ['d', 'e', 'f'];
const array3 = array1.concat(array2);
console.log(array3);
// expected output: Array ["a", "b", "c", "d", "e", "f"]
중복 요소 제거
const values = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];
const result = values. reduce(( acc, cur, i, arr) => {
if(arr.indexOf(cur) === i ) acc.push(cur);
return acc;
}, []);
console.log(result); // [1, 2, 3, 5, 4]
중복 요소를 제거할 때는 reduce 메서드보다 filter 메서드를 사용하는 방법이 더 직관적이다.
const values = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];
const result = values.filter((v, i, arr)) => arr.indexOf(v) === i );
console.log(result); // [1,2,3,5,4]
또는 중복되지 않는 유일한 값들의 집한인 Set을 사용할 수도 있다.
중복 요소를 제거할 때는 이 방법을 추천한다.
const values = [1, 2, 1, 3, 5, 3, 5, 3, 4, 4];
const result =[...new Set(values)];
console.log(result); // [1, 2, 3, 5, 4]
이처럼 map, filter, some, every, find 같은 모든 배열의 고차 함수는 reduce 메서드로 구현할 수 있다.
앞서 살펴보았듯이 reduce 메서드의 두 번째 인수로 전달하는 초기값은 첫 번째 순회에 콜백 함수의 첫 번째 인수로 전달된다.
주의할 것은 두 번째 인수로 전달하는 초기값이 옵션이라는 것이다.
즉, reduce 메서드의 두 번째 인수로 전달하는 초기값은 생략할 수 있다.
const sum = [1, 2, 3, 4].reduce((acc, cur) => acc + cur);
console.log(sum); // 10
하지만 reduce 메서드를 호출할 때는 언제나 초기값을 전달하는 것이 안전하다.
다음 예제를 보자
const sum = [].reduce((acc, cur) => acc + cur);
// typeError: Reduce of empty array with no initial value
이처럼 빈 배열로 reduce 메서드를 호출하면 에러가 발생한다.
이때 reduce 메서드에 초기값을 전달하면 에러가 발생하지 않는다.
const sum = [].reduce((acc, cur) => acc + cur, 0);
console.log(sum); // 0
또 다른 예제를 보자
특정 프로퍼티 값을 합산할 경우
const products = [
{ id: 1, price: 100 },
{ id: 2, price: 200 },
{ id: 3, price: 300 }
];
const priceSum = products.reduce((acc, cur) => acc.price + cur.price);
//1번째 순회 시 acc는 { id : 1, price : 100}, cur { id: 2, price: 200} 이고
//2번째 순회 시 300, cur은 { id:3, price: 300}이다.
//2번째 순회 시 acc에 함수에 객체가 아닌 숫자값이 전달된다.
//이때 acc.price는 undefined다.
console.log(priceSum); // NaN
이처럼 객체의 특정 프로퍼티 값을 합산하는 경우에는 반드시 초기값을 전달해야 한다.
const products = [
{ id: 1, price: 100 },
{ id: 2, price: 200 },
{ id: 3, price: 300 }
];
const priceSum = products.reduce((acc, cur) => acc + cur.price, 0);
console.log(priceSum); // 600
이처럼 reduce 메서드를 호출할 때는 초기값을 생략하지 말고 언제나 전달하는 것이 안전하다.
reduce 직접 구현해보기
console.log(reduce([1, 2, 3, 4], (acc, cur) => acc + cur, 0));
function reduce(arr, callback, initalValue) {
if (initalValue === undefined) initalValue = arr[0];
let returnValue = initalValue;
for (item of arr) returnValue = callback(returnValue, item);
return returnValue;
}
'Javascript > 개념' 카테고리의 다른 글
[JS - 개념] delete (0) | 2022.07.03 |
---|---|
[JS - 개념] Filter() (0) | 2022.06.30 |
[JS - 개념] Map Object (0) | 2022.06.28 |
[JS - 개념 ] 자바스크립트에 점점점(...)의 기능 (0) | 2022.06.17 |
[JS - 개념] 정규표현식 (0) | 2022.06.11 |