27.9 배열 고차 함수
고차함수(HOF)
- 함수를 인수로 전달받거나 함수를 반환하는 함수
- 외부 상태의 변경이나 가변 데이터를 피하고 불변셩(immutability)을 지향하는 함수형 프로그래밍에 기반을 둠
27.9.1 Array.prototype.sort
- 배열의 요소를 정렬
- 원본 배열을 직접 변경하며 정렬된 배열을 반환
- 기본적으로 오름차순으로 요소 정렬
- 내림차순으로 요소를 정렬하려면 sort 메서드를 사용하여 오름차순으로 정렬한 후 reverse 메서드를 사용하여 요소의 순서를 뒤집음
const fruits = ['Banana','Orange','Apple'];
//오름차순 정렬
fruits.sort();
//원본 배열을 직접 변경
console.log(fruits); //[ 'Apple', 'Banana', 'Orange' ]
//내림차순 정렬
fruits.reverse();
console.log(fruits); //[ 'Orange', 'Banana', 'Apple' ]
- sort 메서드의 기본 정렬 순서는 유니코드 코드 포인트의 순서를 따름. 배열의 요소가 숫자 타입이라 할지라도 배열의요소를 일시적으로 문자열로 변환한 후 유니코드 코드 포인트의 순서를 기준으로 정렬
-숫자 요소를 정렬할 때는 sort 메서드에 정렬 순서를 정의하는 비교함수를 인수로 전달해야함
- 비교함수의 반환값이 0보다 작으면 비교 함수의 첫 번째 인수를 우선하여 정렬, 0이면 정렬 x, 0보다 크면 두 번째 인수를 우선하여 정렬
const points = [40,100,1,5,2,25,10];
//숫자 배열의 오름차순 정렬
//비교함수의 반환값이 0보다 작으면 a를 우선하여 정렬
points.sort((a,b) => a-b);
console.log(points); //[ 1, 2, 5, 10,25,40,100]
//숫자 배열에서 최소/최대값 취득
console.log(points[0], points[points.length-1]); //1 100
//숫자 배열의 내림차순 정렬
//비교함수의 반환값이 0보다 작으면 b를 우선하여 정렬
points.sort((a,b) => b-a);
console.log(points); //[100, 40, 25, 10, 5, 2, 1]
//숫자 배열에서 최소/최대값 취득
console.log(points[0], points[points.length-1]); //1 100
27.9.2 Array.prototype.forEach
- forEach 메서드는 for문을 대체할 수 있는 고차 함수
- forEach 메서드는 자신의 내부에서 반복문을 실행
- forEach 메서드는 반복문을 추상화한 고차함수로서 내부에서 반복문을 통해 자신을 호출한 배열을 순회하면서 수행해야할 처리를 콜백함수로 전달받아 반복 호출
const numbers = [1,2,3];
const pows = [];
//foreach 메서드는 numbers 배열의 모든 요소를 순회하면서 콜백 함수를 반복 호출
numbers.forEach(item => pows.push(item ** 2));
console.log(pows); //[ 1, 4, 9 ]
- forEach 메서드는 콜백함수를 호출할 때, 3개의 인수 즉 forEach 메서드를 호출한 배열의 요소값과 인덳, forEach 메서드를 호출한 배열(this)를 순차적으로 전달
[1,2,3].forEach((item,index,arr)=> {
console.log(`요소값: ${item}, 인덱스: ${index}, this: ${JSON.stringify(arr)}`);
});
/*
요소값: 1, 인덱스: 0, this: [1,2,3]
요소값: 2, 인덱스: 1, this: [1,2,3]
요소값: 3, 인덱스: 2, this: [1,2,3]
*/
- forEach 메서드는 원본 배열을 변경하지 않음. 하지만 콜백함수를 통해 원본 배열을 변경할 수는 있음
const numbers = [1,2,3];
//forEach 메서드는 원본 배열을 변경하지 않지만 콜백함수를 통해 원본 배열을 변경할 수는 있다
//콜백 함수의 세 번째 매개변수 arr은 원본 배열 numbers를 가리킴
//따라서 콜백 함수의 세 번째 매개변수 arr을 직접 변경하면 원본 배열 numbers가 변경됨
numbers.forEach((item,index,arr) => {arr[index] = item ** 2});
console.log(numbers); //[ 1, 4, 9 ]
- forEach 메서드의 반환값은 언제나 undefiend
const result = [1,2,3].forEach(console.log);
console.log(result); //undefined
- forEach 메서드는 break문, continue문을 사용할 수 없다. 배열의 모든 요소를 빠짐없이 모두 순회하며 중간에 순회를 중단할 수 없다
- 희소배열의 경우 존재하지 않는 요손느 순회 대상에서 제외된다.
27.9.3 Array.prototype.map
- 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출
- 콜백함수의 반환값들로 구성된 새로운 배열 반환
- 원본 배열은 변경되지 않음
- forEach 메서드는 단순히 반복문을 대체하기 위한 고차함수이고, map 메서드는 요소값을 다른 값으로 매핑한 새로운 배열을 생성하기 위한 고차함수
const numbers = [1,4,9];
const roots = numbers.map(item => Math.sqrt(item));
//map 메서드는 새로운 배열을 반환
console.log(roots); //[ 1, 2, 3 ]
//map 메서드는 원본 배열을 변경하지 않음
console.log(numbers); //[ 1, 4, 9 ]
- map 메서드는 콜백함수를 호출할 때 3개의 인수, 즉 map 메서드를 호출한 배열의 요소값과 인덱스 그리고 map 메서드를 호출한 배열(this)를 순차적으로 전달
[1,2,3].map((item, index, arr)=> {
console.log( `요소값: ${item}, 인덱스 : ${index}, this: ${JSON.stringify(arr)}`);
return item;
});
/*
요소값: 1, 인덱스 : 0, this: [1,2,3]
요소값: 2, 인덱스 : 1, this: [1,2,3]
요소값: 3, 인덱스 : 2, this: [1,2,3]
*/
27.9.4 Array.prototype.filter
- filter 메서드는 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출
- 콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환
- 자신을 호출한 배여에서 필터링 조건을 만족하는 특정 요소만 추출하여 새로운 배열을 만들고 싶을 때 사용
const numbers = [1,2,3,4,5];
const odds = numbers.filter(item => item % 2);
console.log(odds); //[ 1, 3, 5 ]
- filter 메서드는 콜백 함수를 호출할 때 3개의 인수, 즉 filter 메서드를 호출한 배열의 요소값과 인덱스 ,filter 메서드를 호출한 배열(this)를 순차적으로 전달
[1,2,3].filter((item, index, arr)=> {
console.log( `요소값: ${item}, 인덱스 : ${index}, this: ${JSON.stringify(arr)}`);
return item % 2;
});
/*
요소값: 1, 인덱스 : 0, this: [1,2,3]
요소값: 2, 인덱스 : 1, this: [1,2,3]
요소값: 3, 인덱스 : 2, this: [1,2,3]
*/
- filter 메서드는 자신을 호출한 배열에서 특정 요소를 제거하기 위해 사용
class Users {
constructor() {
this.users = [
{id:1, name:'Lee'},
{id:2, name: 'Kim'}
];
}
//요소 추출
findById(id) {
//id가 일치하는 사용자만 반환
return this.users.filter(user => user.id === id);
}
//요소 제거
remove(id) {
//id가 일치하지 않는 사용자를 제거
this.users = this.users.filter(user => user.id !== id);
}
}
const users = new Users();
let user = users.findById(1);
console.log(user); //[ { id: 1, name: 'Lee' } ]
//id가 1인 사용자를 제거
users.remove(1);
user = users.findById(1);
console.log(user); //[]
27.9.5 Array.prototype.reduce
- reduce 메서드는 자신을 호출한 배열을 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출
- 콜백 함수의 반환값을 다음 순회시에 콜백 함수의 첫 번째 인수로 전달하면서 콜백 함수를 호출하여 하나의 결과값을 만들어 반환
- 원본 배열은 변경되지 않음
- 첫 번쨰 인수로 콜백 함수, 두 번쨰 인수로 초기값을 전달받음
- reduce 메서드의 콜백 함수에는 4개의 인수, 초기값(또는 콜백 함수의 이전 반환값), reduce 메서드를 호출한 배열의 요소값과 인덱스, reduce 메서드를 호출한 배열 자체 , 즉 this가 전달됨
//1부터 4까지 누적을 구함
//reduce 메서드는 2개의 인수, 즉 콜백함수와 초기값 0을 전달받아 자신을 호출한 배열의 모든 요소를 누적한 결과를 반환
const sum = [1,2,3,4].reduce((accumulator,currentValue,index,array) => accumulator + currentValue,0);
console.log(sum); //10
- 자신을 호출한 배열의 모든 요소를 순회하면서 하나의 결과값을 구해야 하는 경우에 사용
1) 평균
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
2) 최대값
const values = [1,2,3,4,5];
const max = values.reduce((acc,cur) => (acc>cur ? acc: cur),0);
console.log(max); //5
3) 요소의 중복 횟수 구하기
const fruits = ['banana','apple','orange','orange','apple'];
const count = fruits.reduce((acc,cur) => {
//첫 번째 순회 시 acc는 초기값인 {}이고 cur은 첫 번째 요소인 'banana'
//초기값으로 전달받은 빈 객체에 요소값인 cur을 프로퍼티 키로, 요소의 개수를 프로퍼티 값으로 할당
//만약 프로퍼티 키 값이 undefiend(처음 등장하는 요소)이면 프로퍼티 값을 1로 초기화
acc[cur] = (acc[cur] || 0) + 1;
return acc;
},{});
console.log(count); //{ banana: 1, apple: 2, orange: 2 }
4) 중복 요소 제거
const values = [1,2,1,3,5,4,5,3,4,4];
const result = values.reduce (
(unique, val, i, _values) =>
//현재 순회중인 요소의 인덱스 i가 val의 인덱스와 같다면 val은 처음 순회하는 요소
//현재 순회중인 요소의 인덱스 i가 val의 인덱스와 다르다면 val은 중복된 요소
//처음 순회하는 요소만 초기값 []가 전달된 unique 배열에 담아 반환하면 중복된 요소는 제거
_values.indexOf(val) === i ? [...unique,val] : unique, []
);
console.log(result); //[ 1, 2, 3, 5, 4 ]
25.9.5 Array.prototype.some
- some 메서드는 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출
- some 메서드는 콜백 함수의 반환값이 단 한 번이라도 참이면 true, 모두 거짓이면 false를 반환
- some 메서드르 호출한 배열이 빈 배열인 경우 언제나 false를 반환
//배열의 요소 중 10보다 큰 요소가 1개 이상 존재하는지 확인
[5,10,15].some(item => item >10); //true
//배열의 요소 중 0보다 작은 요소가 1개 이상 존재하는지 확인
[5,10,15].some(item => item < 0); //false
//배열의 요소 중 'banana'가 1개 이상 존재하는지 확인
['apple','banana','mango'].some(item => item === 'banana'); //true
25.9.7 Array.prototype.every
- every 메서드는 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출
- every 메서드는 콜백 함수의 반환값이 모두 참이면 true, 단 한번이라도 거짓이면 false를 반환
- every 메서드를 호출한 배열이 빈 배열인 경우 언제나 true 반환
//배열의 모든 요소가 3보다 큰지 확인
[5,10,15].every(item => item >3); //true
//배열의 모든 요소가 10보다 큰지 확인
[5,10,15].some(item => item > 10); //false
25.9.8 Array.prototype.find
- find 메서드는 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출하여 반환값이 true인 첫 번째 요소를 반환
- 콜백 함수의 반환값이 true인 요소가 존재하지 않는다면 undefined를 반환
- find 메서드는 콜백 함수의 반환값이 true인 첫 번째 요소를 반환하므로 find의 결과값은 배열이 아닌 해당 요소값
const users = [
{id:1, name: 'Lee'},
{id:2, name: 'Kim'},
{id:2, name: 'Choi'},
{id:4, name: 'Park'},
];
//id가 2인 첫 번째 요소를 반환. find 메서드는 배열이 아니라 요소를 반환
users.find(user => user.id === 2); // {id:2, name: 'Kim'}
27.9.9 Array.prototype.findIndex
- findIndex 메서드는 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출하여 반환값이 true인 요소의 첫 번째 요소의 인덱스를 반환
- 콜백 함수의 반환값이 true인 요소가 존재하지 않는다면 -1을 반환
const users = [
{id:1, name: 'Lee'},
{id:2, name: 'Kim'},
{id:2, name: 'Choi'},
{id:4, name: 'Park'},
];
//id가 2인 요소의 인덱스를 구함
users.findIndex(user => user.id === 2); //1
//name이 'Park'인 요소의 인덱스를 구함
users.findIndex(user => user.name === 'Park'); //3
//위와 같이 프로퍼티 키와 프로퍼티 값으로 요소의 인덱스를 구하는 경우 다음과 같이 콜백 함수를 추상화할 수 있음
function predicate(key, vlaue) {
//key와 value를 기억하는 클로저 반환
return item => item[key] === vlaue;
}
//id가 2인 요소의 인덱스를 구함
users.findIndex(predicate('id',2)); //1
//name이 'Park'인 요소의 인덱스를 구함
users.findIndex(predicate('name','Park'));//3
27.9.10 Array.prototype.flatMap
- flatMap 메서드는 map 메서드를 통해 생성된 새로운 배열을 평탄화
- map 메서드와 flat 메서드를 순차적으로 실행하는 효과
- flatMap 메서드는 인수를 전달하여 평탄화 깊이를 지정할 수는 없고 1단계만 평탄화
const arr = ['hello','world'];
//map과 flat을 순차적으로 실행
arr.map(x => x.split('')).flat();
//['h', 'e', 'l', 'l','o', 'w', 'o', 'r','l', 'd']
//flatMap은 map을 통해 생성된 새로운 배열을 평탄화
arr.flatMap(x => x.split(''));
//['h', 'e', 'l', 'l','o', 'w', 'o', 'r','l', 'd']
'JavaScript' 카테고리의 다른 글
[Deep dive] 29장 Math (0) | 2023.08.17 |
---|---|
[Deep dive] 28장 Number (0) | 2023.08.17 |
[Deep dive] 27장 배열(2) (0) | 2023.08.16 |
[Deep dive] 27장 배열(1) (0) | 2023.08.16 |
[Deep dive] 26장 ES6 함수의 추가 기능 (0) | 2023.08.16 |