
인트로
고차 함수 map, filter, reduce에 대해서 공부했는데 그걸론 좀 부족한 것 같아 나머지 고차 함수들도 좀 알아보려고 합니다.
고차 함수란?
함수를 인수로 전달받거나 함수를 반환하는 함수를 말합니다.
자바스크립트에는 고차 함수가 많습니다. 특히 배열에 유용한 고차 함수가 정말 많습니다.
그럼 몇 가지를 살펴보죠. 참고로 제가 몰랐던 내용이 있거나 헷갈리는 고차 함수 위주로 정리하였기 때문에 빠진 함수가 있을 수 있습니다.
Array.prototype.Sort
sort 메서드는 배열의 요소를 정렬합니다.
이때 원본 배열을 직접 변경하며 정렬된 배열을 반환합니다.
기본적으로 오름차순으로 요소를 정렬합니다. 한글도 마찬가지입니다.
const abc = ['b', 'a', 'c'];
abc.sort();
console.log(abc); // [ 'a', 'b', 'c' ]
내림차순으로 요소를 정렬하려면 오름차순으로 정렬한 후 reverse 메서드를 사용하여 뒤집어주면 됩니다.
const abc = ['b', 'a', 'c'];
abc.sort();
console.log(abc.reverse()); // [ 'c', 'b', 'a' ]
그러나
숫자로 이루어진 배열을 정렬할 때는 차이가 있습니다.
sort의 기본 정렬 순서는 유니코드 코드 포인트 순서를 따르기 때문에 배열의 요소가 숫자 타입일지라도
배열의 요소를 문자열로 변환한 후 유니코드 코드 포인트 순서를 기준으로 정렬합니다.
따라서 다음과 같은 결과가 나옵니다.
console.log([2,10].sort()); // [10,2]
숫자 요소를 정렬하고 싶다면 정렬 순서를 정의하는 비교 함수를 인수로 전달해야 합니다.
전 사실 이 부분은 그냥 외우는 게 편하다고 봅니다.
console.log([2,10].sort((a,b)=> a-b)); // [2,10]
내림차순은 a-b 부분을 b-a로 바꾸면 됩니다.
객체 요소를 갖는 배열을 정렬하는 방법은 다음과 같습니다.
const obj = [
{id: 2},
{id: 3},
{id: 1},
]
const compare = (key) => {
return (a,b) => (a[key] > b[key]) ? 1 : (a[key] < b[key] ? -1 : 0);
}
obj.sort(compare('id'));
console.log(obj); //[ { id: 1 }, { id: 2 }, { id: 3 } ]
Array.prototype.forEach
forEach는 for문을 대체할 수 있는 고차 함수입니다. 그러나 for문과 달리 break, continue 문을 사용할 수 없습니다.
for문보다 성능이 떨어지지만 가독성이 좋습니다.
forEach 메서드는 자신의 내부에서 반복문을 실행합니다.
const number = [1,2,3];
const pow = [];
number.forEach(el => pow.push((el**2)));
console.log(pow); //[ 1, 4, 9 ]
forEach는 원본 배열을 변경하지 않습니다. 그러나 콜백 함수를 통해 원본 배열을 변경할 수는 있습니다.
forEach의 반환값은 언제나 undefined입니다.
const number = [1,2,3];
number.forEach((item, index, arr) => { arr[index] = item ** 2});
console.log(number); //[ 1, 4, 9 ]
이후 나올 map, filter, reduce에서도 마찬가지이지만 희소 배열의 경우 존재하지 않는 요소는 순회 대상에서 제외됩니다.
Array.prototype.map
콜백 함수의 반환값들로 구성된 새로운 배열을 반환합니다. 따라서 원본 배열이 변경되지 않습니다.
forEach 메서드는 언제나 undefined를 반환하고 map 메서드는 콜백 함수의 반환값들로 구성된 새로운 배열을 반환한다는 점에서 차이가 있습니다.
만약 새로운 배열을 생성할 필요가 없이 단순 반복을 위한 거라면 forEach를 쓰면 되는 거겠죠?
map 메서드가 생성해 반환하는 새로운 배열의 length 값은 map 메서드를 호출한 배열의 length 값과 같습니다.
즉, map 메서드를 호출한 배열과 map 메서드가 반환한 배열은 1:1 매핑입니다.
Array.prototype.reduce
reduce는 콜백 함수의 반환값을 다음 순회 시에 콜백 함수의 첫 번째 인수로 전달하면서
콜백 함수를 호출하여 하나의 결과값을 만들어 반환합니다.
이때 원본 배열은 변경되지 않습니다.
첫 번째 인수로 콜백 함수, 두 번째 인수로 초기값을 전달받습니다.
콜백 함수는 다음 네 가지 인자를 받습니다.
- 누산기 (acc) - 누적
- 현재 값 (cur) - 처리할 현재 요소
- 현재 인덱스 (idx) - 처리할 현재 요소의 인덱스
- 원본 배열 (src) - 호출한 배열
가장 대표적인 예는 배열의 모든 요소를 합하는 것입니다.
const a =[1,2,3,4].reduce((acc,cur)=> acc+cur,0);
console.log(a); //10
reduce 메서드는 자신의 호출한 배열의 모든 요소를 순회하면 하나의 결괏값을 구해야 하는 경우에 사용합니다.
reduce 메서드를 이해하면 다양하게 활용할 수 있습니다.
(다양한 활용법)
평균 구하기
const a =[1,2,3,4].reduce((acc,cur)=> acc+cur,0) / [1,2,3,4].length;
console.log(a); //2.5
최대값 구하기 (물론 Math.max를 사용하는 게 더 직관적입니다.)
const a =[1,5,3,4].reduce((acc,cur)=> acc > cur ? acc : cur,0);
console.log(a); //5
요소의 중복 횟수 구하기
초기값으로 전달받은 빈 객체에 요소값인 cur을 프로퍼티 키로, 요소의 개수를 프로퍼티 값으로 할당합니다.
만약 프로퍼티 값이 undefined(처음 등장하는 요소)이면 프로퍼티 값을 1로 초기화합니다.
const a = [1,5,5,4,2,1].reduce((acc,cur)=> {
acc[cur] = (acc[cur] || 0) + 1; return acc;
}, {});
console.log(a);
//{ 1: 2, 2: 1, 4: 1, 5: 2 }
중첩 배열 평탄화 (flat 메서드가 더 직관적입니다.)
const values = [1, [2,3], 4, [5,6]];
const flatten = values.reduce((acc, cur) => acc.concat(cur), []);
console.log(flatten); //[ 1, 2, 3, 4, 5, 6 ]
중복 요소 제거 (filter, Set 추천)
이 부분은 직접 출력해 보면서 이해하는 걸 추천합니다.
순회 중인 요소 인덱스 i가 val의 인덱스와 같다면 val이 처음 나온 것이고
인덱스가 다르다면 val는 중복이 됩니다. 처음 나온 값만 초기값 []가 전달된 unique 배열에 담아 반환합니다.
const values = [1,2,1,3,5,3,4];
const result = values.reduce((unique, val, i, _values) => {
console.log(i +'- uniq: ' + unique + ' val :' + val);
console.log(_values);
return _values.indexOf(val) === i ? [...unique, val] : unique
}, []);
console.log(result);
어쨋든 모든 배열의 고차 함수를 reduce로 구현할 수 있다는 점에서 짱짱 멋진 메서드입니다.
reduce 메서드의 두 번째 인수로 전달하는 초기값은 생략 가능하지만 초기값을 전달하는 것이 안전합니다.
console.log([].reduce((acc,cur)=> acc+cur,0));//0
console.log([].reduce((acc,cur)=> acc+cur)); //Reduce of empty array with no initial value
(객체의 특정 프로퍼티 값을 합산하는 경우)
2번째 순회할 때 acc에 숫자값이 전달되기 때문에 나타나는 당연한 결과입니다.
const products = [
{id:1, price:100},
{id:2, price:200},
{id:3, price:300},
];
const pricesum = products.reduce((acc, cur)=> acc.price + cur.price);
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;
Array.prototype.some
콜백 함수의 반환값이 단 한 번이라도 참이면 true, 모두 거짓이면 false를 반환합니다.
배열의 요소 중 콜백 함수를 통해 정의한 조건을 하나라도 만족한다면 그 결과를 불리언 타입으로 반환합니다.
이때 some 메서드를 호출한 배열이 빈 배열이면 언제나 false를 반환합니다.
console.log([1,2,3].some(el => el === 1)); //true
console.log([1,2,3].some(el => el === 4)); //false
Array.prototype.every
some과 반대(?)입니다.
콜백 함수의 반환값이 모두 참이면 true, 하나라도 거짓이면 false를 반환합니다.
배열의 요소 중 콜백 함수를 통해 정의한 조건을 하나라도 만족한다면 그 결과를 불리언 타입으로 반환합니다.
이때 every 메서드를 호출한 배열이 빈 배열이면 언제나 true를 반환합니다.
console.log([1,1,1].every(el => el === 1)); //true
console.log([1,2,3].every(el => el === 1)); //false
console.log([].every(el => el === 1)); //true
Array.prototype.find
자신이 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출하여 반환값이 true인 첫 번째 요소를 반환합니다. true인 요소가 존재하지 않는다면 undefined를 반환합니다.
const users =[
{id: 1 , name: 'Lee'},
{id: 1 , name: 'Kim'},
{id: 2 , name: 'Lee'},
]
console.log(users.find(user => user.id === 1)); //{ id: 1, name: 'Lee' }
Array.prototype.findIndex
자신이 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출하여 반환값이 true인 첫 번째 요소의 인덱스를 반환합니다. true인 요소가 존재하지 않는다면 -1을 반환합니다.
const users =[
{id: 1 , name: 'Lee'},
{id: 1 , name: 'Kim'},
{id: 2 , name: 'Lee'},
]
console.log(users.findIndex(user => user.id === 1)); //0
console.log(users.findIndex(user => user.id === 3)); //-1
Array.prototype.flatMap
map 메서드를 통해 생성된 새로운 배열을 평탄화합니다. map 메서드와 flat 메서드를 순차적으로 실행하는 것과 같습니다.
그러나 flatMap은 flat과 달리 평탄화 깊이를 지정할 수 없으며 1단계만 평탄화합니다.
const arr = ['hello', 'world'];
const a = arr.map(x => x.split(''));
console.log(a);
//[ [ 'h', 'e', 'l', 'l', 'o' ], [ 'w', 'o', 'r', 'l', 'd' ] ]
console.log(a.flat());
//[ 'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd' ]
arr.flatMap(x => x.split(''));
//[ 'h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd' ]
하루에 딥다이브 2장은 정말 힘드네요... 머리가 깨질 것 같습니다.. 다시는 이런 짓하지 않아야지
MDN & 모던 자바스크립트 Deep Dive 내용을 참고하였습니다.
'내가 해냄 > JS' 카테고리의 다른 글
동기/비동기 내가 해냄 (0) | 2023.03.16 |
---|---|
프로토타입 내가 해냄 (0) | 2023.03.15 |
ES6 함수 내가 해냄 (0) | 2023.03.14 |
클래스 내가 해냄 (0) | 2023.03.08 |
DOM 내가 해냄 (1) | 2023.03.07 |
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!