반응형
고차 함수를 사용하는 이유
(HOF : Higher-Order Function)
추상화를 통한 효율성 증대 때문이다.
map, filter, reduce는
자주 사용되므로 반드시 익히자!!
★ map
// map((el) => el * 2);
// map(el, index, arr);
// 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출하고,
// 호출된 콜백함수의 반환값들로 구성된 새로운 배열을 반환한다.
// (= 배열의 처음 요소부터 마지막 요소까지 하나씩 매개변수에 입력하여
// 모든 요소를 조건에 따른 결과값을 새로운 배열에 모아서 리턴한다.)
// 원본 배열을 변경하지 않는다.
// 매핑(mapping) : 배열의 요소를 map 메서드을 통하여 다른 값으로 적용하여 새로운 배열을 생성하는 것
// = 배열에 map 메서드를 적용한 것
const num = [1, 4, 9];
const roots = num.map(el => Math.sqrt(el));
console.log(roots); // [1, 2, 3]
console.log(num); // [1, 4, 9]
const multiply3 = num.map(el => el * 3);
console.log(multiply3); // [3, 12, 27]
console.log(num); // [1, 4, 9]
// map 메서드가 생성하여 반환하는 새로운 배열의 length 값은
// map 메서드를 호출한 배열의 length 값과 반드시 일치한다.
// (= beforeMapArr === afterMapArr)
// map 메서드는 콜백 함수를 호출하면서 3개(el, index, arr)의 인수를 순차적으로 전달한다.
// (요소값, 인덱스값, 배열) (= filter, forEach, every, some, find, findIndex)
[1, 2, 3].map((el, index, arr) => {
console.log(`요소값: ${el}, 인덱스: ${index}, this: ${JSON.stringify(arr)}`);
});
// 요소값: 1, 인덱스: 0, this: [1,2,3]
// 요소값: 2, 인덱스: 1, this: [1,2,3]
// 요소값: 3, 인덱스: 2, this: [1,2,3]
반응형
★ filter
// filter (el => el * 2);
// filter (el, index, arr);
// 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백함수를 반복 호출하고,
// 콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환한다.
// (= 배열의 각 요소에 콜백 함수를 적용시켰을 때,
// true를 리턴하는 요소들만 모은 새로운 배열을 리턴한다.)
// 원본 배열을 변경하지 않는다.
const numbers = [1, 2, 3, 4, 5];
const odds = numbers.filter(el => el % 2);
console.log(odds); // [1, 3, 5]
// filter 메서드가 생성하여 반환한 새로운 배열의 length 값은
// filter 메서드를 호출한 배열의 length 값과 같거나 작다.
// (= beforeFilteredArr.length >= afterFilteredArr.length)
// filter 메서드는 콜백 함수를 호출하면서 3개(el, index, arr)의 인수를 순차적으로 전달한다.
// (요소값, 인덱스값, 배열) (= map, forEach, every, some, find, findIndex)
[1, 2, 3].filter((el, index, arr) => {
console.log(`요소값: ${el}, 인덱스: ${index}, this: ${JSON.stringify(arr)}`);
return el % 2;
});
// 요소값: 1, 인덱스: 0, this: [1,2,3]
// 요소값: 2, 인덱스: 1, this: [1,2,3]
// 요소값: 3, 인덱스: 2, this: [1,2,3]
class Users {
constructor() {
this.users = [
{id: 1, name: 'Lee'},
{id: 1, name: 'Kim'}
];
}
// 요소 추출
findById(id) {
// id가 일치하는 사용자만 반환한다.
return this.users.filter(el => el.id === id);
}
// 요소 제거
remove(id) {
// id가 일치하지 않는 사용자를 제거한다.
this.users = this.users.filter(el => el.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); // []
★ reduce
// reduce ((acc, cur), init);
// reduce ((accmulator, curentValue, index, array), initialValue);
// 자신을 호출한 배열을 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출하고,
// 콜백 함수의 반환값을 다음 순회 시에 콜백 함수의 첫 번째 인수로 전달하면서
// 콜백 함수를 호출하여 하나의 결과값을 만들어 반환한다.
// (= 배열의 각 요소를 콜백 함수에 맞게 하나로 응축시킨 값으로 리턴한다.)
// 원본 배열을 변경하지 않는다.
const sum = [1, 2, 3, 4].reduce((acc, cur, index, arr) => acc + cur, 0);
console.log(sum); // 10
// 초기값은 생략할 수 있다.
// 하지만, 초기값을 설정하는 것이 안전하다.
// 만약, 초기값을 설정하지 않으면 배열의 첫 번째 요소가 초기값이 된다.
const sum = [1, 2, 3, 4].reduce((acc, cur, index, arr) => acc + cur);
console.log(sum); // 10
// 초기값을 전달한 경우
const sum1 = [].reduce((acc, cur) => acc + cur, 0);
console.log(sum1); // 0
// 초기값 생략하여 Error 발생한 경우
const sum2 = [].reduce((acc, cur) => acc + cur);
// TypeError: Reduce of empty array with no initial value
// in Object
// 객체의 특정 프로퍼티 값을 합산하는 경우 반드시 초기값을 설정하여야 한다.
const products = [
{ id: 1, price: 100 },
{ id: 2, price: 200 },
{ id: 3, price: 300 }
];
// 초기값 설정 시
const priceSumUsedInit = products.reduce((acc, cur) => acc + cur.price, 0);
// 1번째 순회 : acc = 0, cur = { id: 1, price: 100 }
// 2번째 순회 : acc = 100, cur = { id: 2, price: 200 }
// 3번째 순회 : acc = 300, cur = { id: 3, price: 300 }
console.log(priceSumUsedInit); // 600
// 초기값 미설정 시
const priceSumUnusedInit = products.reduce((acc, cur) => acc.price + cur.price);
// 1번째 순회 : acc = { id: 1, price: 100 }, cur = { id: 2, price: 200 }
// 2번째 순회 : acc = 300, cur = { id: 3, price: 300 }
// 2번째 순회 시 acc에 함수에 객체가 아닌 숫자값이 전달된다.
// 2번째 순회시 arr.price === undefined 이다. (300.price === undefined)
console.log(priceSumUnusedInit); // NaN
// example
// 평균 구하기
const values = [ 1, 2, 3, 4, 5 ];
const average = values.reduce((acc, cur, index, { length }) => {
// 마지막 순회가 아니면 누적값을 반환하고, 마지막 순회이면 누적값으로 평균을 구해 반환한다.
return index === length -1 ? (acc + cur) / length : acc + cur;
}, 0);
console.log(average); // 3
// reduce보다는 반복문을 활용하자!
// example
// 최대값 구하기
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를 활용하자!
// example
// 요소의 중복 횟수 구하기
const alphabet = ['a', 'c', 'd', 'e', 'a', 'b', 'b', 'c'];
const count = alphabet.reduce((acc, cur) => {
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
console.log(count); // { a: 2, c: 2, d: 1, e: 1, b: 2 }
// 1번째 순회 : acc = {}, cur = 'a'
// 초기값으로 전달된 빈 객체에 요소값인 cur을 프로퍼티 ket로, 요소의 개수를 프로퍼티 값으로 할당한다.
// 만약 프로퍼티 값이 undefined(처음 등장하는 요소)이면 프로퍼티 값을 1로 초기화한다.
// example
// 배열의 평탄화
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]
// reduce 보다는 flat를 활용하자!
// example
// 중복 요소 제거
const values = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];
const result = values.reduce((unique, val, i, _values) =>
_values.indexOf(val) === i ? [ ...unique, val ] : unique, []
);
console.log(result); // [1, 2, 3, 5, 4]
// reduce 보다는 Set을 활용하자!
★ forEach
// forEach(el => el * 2);
// forEach(el, index, arr);
// forEach는 반복문을 추상화한 고차 함수로서
// 내부에서 반복문을 통해 자신을 호출한 배열을 순회하면서
// 수행해야 할 처리를 콜백 함수로 전달받아 반복 호출한다.
const arrNum = [1, 3, 5];
const pows = [];
arrNum.forEach(el => pows.push(el ** 2));
console.log(pows); // [ 1, 9, 25 ]
// 위 예제의 경우
// forEach 메서드는 arrNum 배열의 요소가 3개이므로 콜백 함수도 3번 호출된다.
// 이때 콜백 함수를 호출하는 forEach 메서드는 콜백 함수에 인수를 전달할 수 있다.
// forEach는 원본 배열을 변경하지 않는다.
// 하지만, 콜백 함수를 통해 원본 배열을 변경할 수는 있다.
arrNum.forEach((el, index, arr) => { arr[index] = el ** 2; });
console.log(arrNum); // [ 1, 9, 25 ]
// forEach 메서드는 콜백 함수를 호출하면서 3개(el, index, arr)의 인수를 순차적으로 전달한다.
// (요소값, 인덱스값, 배열) (= map, filter, every, some, find, findIndex)
[1, 2, 3].forEach((el, index, arr) => {
console.log(`요소값: ${el}, 인덱스: ${index}, this: ${JSON.stringify(arr)}`);
});
// 요소값: 1, 인덱스: 0, this: [1,2,3]
// 요소값: 2, 인덱스: 1, this: [1,2,3]
// 요소값: 3, 인덱스: 2, this: [1,2,3]
// for문을 대체할 수 있는 고차함수이다.
// for문과 달리 모든 요소를 순회할 수 밖에 없기에,
// break, continue 문을 사용할 수 없다.
// 하지만 for문에 비해 가독성이 좋다.
const arr = [1, 2, 3];
arr.forEach(el => {
console.log(el);
if (el > 1) break;
}); // SyntaxError: Illegal break statement
arr.forEach(el => {
console.log(el);
if (el > 1) continue;
}); // SyntaxError: Illegal continue statement: no surrounding iteration statement
// forEach 메서드의 반환값은 언제나 undefined 이다.
const result = [1, 2, 3].forEach(console.log);
console.log(result); // undefined
sort
// sort();
// sort((a, b) => a - b);
// 문자열로 변환시켜 오름차순으로 정렬한다.
// (기본적으로 숫자는 낮은 숫자, 영어는 알파벳, 한글은 가나다 순으로 정렬된다.)
// 원본배열을 변경한다.
// 영어
let arr0 = ['melon', 'apple', 'kiwi', 'banana'];
console.log(arr0.sort()); // ['apple', 'banana', 'kiwi', 'melon']
console.log(arr0); // ['apple', 'banana', 'kiwi', 'melon']
// 한글
let arr1 = ['다리미', '개미', '까마귀', '가위', '수박'];
console.log(arr1.sort()); // [ '가위', '개미', '까마귀', '다리미', '수박' ]
// 숫자
// 요소가 문자열로 인식되어 정렬되기 때문에 숫자부분에서 제대로 오름차순이 정렬되지 않는다!!
let arr2 = [22, 11, 2, 9, 1, 5];
console.log(arr2.sort()); // [1, 11, 2, 22, 5, 9]
// 따라서 숫자요소를 정렬할 때에는
// sort 메서드에 정렬 순서를 정의하는 비교 함수를 인수로 전달하여야 한다.
// 비교 함수는 양수나 음수 또는 0을 반환해야한다.
// 비교 함수의 반환값 < 0 : 비교 함수의 첫 번째 인수를 우선하여 정렬,
// 비교 함수의 반환값 = 0 : 정렬하지 않으며,
// 비교 함수의 반환값 > 0 : 두 번째 인수를 우선하여 정렬한다.
// 올바른 오름차순 방법
let arr1 = [22, 11, 2, 9, 1, 5];
console.log(arr1.sort(function (a, b){
return a - b; })); // [1, 2, 5, 9, 11, 22]
let arr2 = [22, 11, 2, 9, 1, 5];
console.log(arr2.sort((a, b) => (a - b))); // [1, 2, 5, 9, 11, 22]
// 최소, 최대값 구하기
console.log(arr1[0], arr1[arr1.length -1]); // 1 22
// 올바른 내림차순 방법
let arr1 = [22, 11, 2, 9, 1, 5];
console.log(arr1.sort(function (a, b){
return b - a; })); // [22, 11, 9, 5, 2, 1]
let arr2 = [22, 11, 2, 9, 1, 5];
console.log(arr2.sort((a, b) => (b - a))); // [22, 11, 9, 5, 2, 1]
// 최소, 최대값 구하기
console.log(arr1[arr1.length -1], arr1[0]); // 1 22
// 문자열 길이가 작은 순서대로 정렬하는 방법
let arr = ['melon', 'apple', 'kiwi', 'banana'];
console.log(arr.sort((a, b) => (a.length - b.length))); // [ 'kiwi', 'melon', 'apple', 'banana' ]
// cf) reverse : 요소의 순서를 뒤집는다.
// reverse도 원본 배열을 변경한다.
console.log(arr.reverse()); // [ 'banana', 'apple', 'melon', 'kiwi' ]
console.log(arr); // [ 'banana', 'apple', 'melon', 'kiwi' ]
고차 함수(1)를 알아보는 시간이었습니다.
틀린 내용은 댓글로 알려주시면 감사하겠습니다.
반응형