Javascript/개념

[JS - 개념] reduce() <feat.. Array.prototype.concat()>

개발자성장기 2022. 6. 27. 02:29
반응형



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(초기 값) 0 [1,2,3,4] 1 (accumlator +    currentValue)
두 번째 순회 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;
}
반응형