> 기초/Javascript
TIL-2024.02.15 - JS - 고차함수와 Reduce
Janku
2024. 2. 15. 21:50
------- 고차함수 란?
고차함수:
- JS에서 함수는 일급 객체이므로 함수를 값처럼 인수로 전달할수 있고, 반환 가능.
- 고차 함수는 외부 상태의 변경이나 가변 데이터를 피하고 immutability (불변성)을 지향하는 함수형 프로그래밍에 기반을 둠
------- Reduce 란?
Reduce 특징:
- 고차함수: 함수를 인수로 전달받거나 함수를 반환하는 함수
- 순수함수: 동일한 인자가 들어오면 항상 같은 값이 나와야하고 return 값으로만 소통하고 데이터베이스 호출이나 HTTP 호출 등 외부의 데이터 구조를 변형하는 호출을 허용하지 않는 함수
설명:
- 자신을 호출한 배열의 모든 요소를 순회하며, 인수로 전달받은 콜백 함수를 반복 호출.
- 콜백 함수의 반환값을 다음 순회 시에 콜백 함수의 첫번째 인수로 전달하면서 콜백 함수를 호출하여 하나의 결과값을 만들어 반환.
- 원본 배열은 변경하지 않음
- 첫번째 인수로 콜백함수, 두번째 인수로 초기값 전달 받음.
- 첫번째 인수인 콜백함수는 4개의 인수 (초기값/콜백함수의 이전 반환값, reduce를 호추한 배열의 요소값-index, 배열 자체)가 전달
예제:
const sum = [1,2,3,4].reduce((accumulator,currentVal,index,array) => accumulator + currentVal, 0);
console.log(sum) // 10 >> 이러한 과정을 반복하여 reduce 메서드는 하나의 결과값 반환
예제 설명:
구분 | 콜백 함수에 전달되는 인수 | 콜백 함수의 반환값 | |||
accumulator | currentVal | index | array | ||
첫 번째 순회 | 0 (초기값) | 1 | 0 | [1,2,3,4] | 1(accumulator + currentVal) |
두 번째 순회 | 1 | 2 | 1 | [1,2,3,4] | 3(accumulator + currentVal) |
세 번째 순회 | 3 | 3 | 2 | [1,2,3,4] | 6(accumulator + currentVal) |
네 번쨰 순회 | 6 | 4 | 3 | [1,2,3,4] | 10(accumulator + currentVal) |
사용 예제:
// 1. 평균 구하기
const average = [1,2,3,4,5,6].reduce((acc, cur, index , {length}) => {
return index === length - 1 ? (acc + cur) / length : acc + cur;
}, 0);
console.log(average) // 3.5
// 2. 최대값 구하기
const max = [1,2,3,4,5].reduce((acc, cur) => acc > cur ? acc: cur, 0 )
console.log(max) // 5
// 3. 요소의 중복 횟수 구하기
const countFruits = ['banana', 'apple', 'orange', 'orange', 'apple'].reduce((acc, cur) => {
// 1. 첫번째 순회 시, acc는 초기값인 {} 이고, cur은 첫번째 요소인 'banana'
// 2. 초기값으로 전달받은 빈 객체에 요소값인 cur을 프로퍼티 키로, 요소의 갯수를 프로퍼티 값으로 할당
// 3. 만약 프로퍼티 값이 undefined이면 프로퍼티 값을 1로 초기화한다.
acc[cur] = (acc[cur] || 0 ) + 1
return acc
}, {}}
// 4. 중첩 배열 평탄화 (.flat)
const flatten = [1,[2,3],[4,5]].reduce((acc,val) => acc.concat(val),[]);
console.log(flatten) // [1,2,3,4,5]
// 5. 중복 요소 제거
const duplicated = [ 1,2,1,3,5,4,5,3,4,4].reduce((acc,val,i , array) =>
array.indexOf(val) === i ?[...acc, val] : acc, [])
// 1. i가 val의 인덱스와 같으면, 처음 순회하는 요소.
// 2. i가 val의 인덱스와 다르면, val은 중복된 요소
이와 같이, 다양하게 reduce를 활용할 수 있다. Reduce 에서 초기값 (두번째로 인수) 생략 가능하지만, 언제나 초기값을 전달하는 것이 안전하다.
왜?
const price = [
{id: 1 , price: 100},
{id: 2 , price: 200},
{id: 3 , price: 300}
];
> 초기값 X
// 1. 첫번째 순회시, acc = {id: 1 , price: 100}, cur = {id: 2 , price: 200}
// 2. 두번째 순회시, acc = 300, cur = {id: 3 , price: 300}
// 2. 두번째 순횐시, acc 함수에 객체가 아닌 숫자 전달, thus undefined.
return price.reduce((acc, cur) => acc.price + cur.price);
> 초기값 O
// 1. 첫번째 순회시, acc = 0, cur = {id: 1 , price: 100}
// 2. 두번째 순회시, acc = 100, cur = {id: 2 , price: 200}
// 2. 세번째 순회시, acc = 300, cur = {id: 3 , price: 300}
return price.reduce((acc, cur) => acc.price + cur.price , 0);