본문 바로가기

JavaScript

[Deep dive] 27장 배열(3)

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