[모던 자바스크립트 Deep dive] Study - 27. 배열(3)

n-u·2022년 5월 27일
0
post-thumbnail

27장 배열(3)

배열 고차 함수

고차 함수

: 함수를 인수로 전달받거나 함수를 반환하는 함수

  • 자바스크립트의 함수는 일급 객체이므로 함수를 값처럼 인수로 전달 할 수 있으며 반환 할 수 있다.
  • 고차함수는 외부 상태의 변경이나 가변데이터를 피하고 불변성을 지향하는 함수형 프로그래밍을 기반을 두고 있다.

함수형 프로그래밍

: 순수함수와 보조 함수의 조합을 통해 로직 내에 존재하는 존건문과 반복문을 제거하여 복잡성을 해결하고 변수의 사용을 억제하여 상태 변경을 피하려는 프로그래밍 패러다임

  • 즉, 순수함수를 통해 부수 효과를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이려는 노력의 일환

1. Array.prototype.sort (return : 배열)

배열을 기본적으로 오름차순으로 요소를 정렬하며 원본 배열을 직접 변경하며 정렬된 배열을 반환한다.

  • 한글 문자열 오름차순도 가능
  • 내림차순으로 요소를 정려하고 싶다면
    • sort메서드를 사용한 수 reverse메서드를 사용해 요소의 순서를 뒤집는다.
const fruits = ['Banana','Orange','Apple'];

//오름차순으로 정렬
fruits.sort();
console.log(fruits);	//['Apple','Banana','Orange']
//내림차순으로 정렬
fruits.reverse();
console.log(fruits);	//['Orange','Banana','Apple']
  • 숫자 요소로 이루어진 배열을 정렬할 때는 주의가 필요하다.
    • sort 메서드가 요소를 정렬하는 기준은 유니코드 코드 포인트 순서를 따른다. 하지만 숫자는 유니코드가 일반 숫자 순서와 다르기때문에 예상치 못한 결과를 받을 수 있다.
    • 숫자 요소를 정렬할때 : 정렬 순서를 정의하는 비교 함수를 인수로 전달하여 정렬한다.
      • 비교 함수 반환값 < 0 : 첫번째 인수를 우선 정렬
      • 비교 함수 반환값 = 0 : 정렬 하지 않는다.
      • 비교 함수 반환값 > 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]);	//100

//숫자 배열의 내림차순 정렬
//비교 함수의 반환값이 0보다 작으면 b를 우선하여 정렬한다.
points.sort((a,b) => b - a);
console.log(points);	//[100, 40, 25, 10, 5, 2, 1]

2. Array.prototype.forEach (return : undefined)

for문을 대체할 수 있는 고차함수이며,

반복문을 추상화한 고차함수로서 내부에서 반복문을 통해 자신을 호출한 배열을 순회하면서 수행해야 할 처리를 콜백함수로 전달 받아 호출한다.

  • forEach메서드의 매개변수 (2) : 콜백함수, 콜백함수 내부의 this지정
  • 콜백함수의 매개변수 (3) : 요소값, 인덱스, forEach 메서드를 호출한 배열 자체(this)
const number = [1,2,3];
const pows = [];

number.forEach(item => pow.push(item ** 2));
console.log(pow);	//[2,4,9]
  • 원본 배열을 변경하지 않는다.(콜백함수를 통해서 변경할 수도 있다.)
  • forEach에 반환값은 언제나 undefined이다.
cosnt result = [1,2,3].forEach(console.log);
console.log(result);	//undefined
  • forEach에는 break와 continue문을 사용할 수 없다.
    • 즉, 배열의 모든 요소를 빠짐없이 모두 순환하여 중간에 순회를 중단할 수 없다.
  • 희소배열의 경우 존재하지 않는 요소는 순회대상에서 제외된다.
//희소배열
const arr = [1, ,3];

//for문으로 희소 배열을 순회
for(let i = 0; i < arr.length; i++){
  console.log(arr[i]);	//1 undefined 3
}

//forEach메서드는 희소배열의 존재하지 않는 요소를 순회대상에서 제외한다.
arr.forEach(v => console.log(v));	//1,3
  • forEach는 for문에 비해 성능이 좋지 않지만 가독성이 좋다.
    • 요소가 대단히 많은 배열을 순화하거나, 시간이 많이 걸리는 복잡한 코드 또는 높은 성능이 필요한 경우가 아니면 for문 대신 forEach문을 사용하는 것을 권장한다.

forEach의 두번째 인수

  1. 콜백함수가 일반 함수인 경우
class Numbers{
	numberArray = [];

	multiply(arr){
      arr.forEach(function(item){
        this.numberArray.push(item * item);
      },this);
      //this를 기정하지 않으면 forEach를 통해 호출된 콜백 함수는 일반 함수로 불러오게 되어서 this의 값이 undefined가 된다.
      //그 이유는 클래스 내부의 모든 코드에는 암묵적으로 strict mode가 적용되기 때문이다.
    }
  }
  
const number = new Numbers();
numbers.mutiply([1,2,3]);
conosole.log(number.nuberArray);	//[1,4,9]
         

class인 경우에는 클래스 내부는 strict mode가 되어 this가 undefined가 되어 원하는 값을 도출해 낼 수 없다. 따라서 this를 forEach문의 두번째 인수로 전달해 콜백함수 안에 this를 일치 시켜준다.

  • forEach의 두번째 인수 this는 forEach의 this이다.
  1. 콜백 함수가 화살표 함수인 경우
    forEach의 콜백함수를 화살표 함수로 이용하면 두번째 인수로 this를 지정하지 않아도 되는 방법도 있다.
class Numbers{
	numberArray = [];

	multiply(arr){
      arr.forEach((item)=>this.numberArray.push(item * item);
    }
  }
  
const number = new Numbers();
numbers.mutiply([1,2,3]);
conosole.log(number.nuberArray);	//[1,4,9]

화살표 함수는 this 바인딩을 가지지 않기 때문에 this를 찾기 위해서 상위 스코프의 this를 참조한다.

3. Array.prototype.map (return : 배열)

자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달 받은 콜백 함수를 반복 호출한다. 콜백함수의 반환값들로 구성된 새로운 배열을 반환한다.

  • map의 매개변수(2): 콜백함수, 콜백 함수 내부의 this지정 객체
  • 콜백함수의 매개변수(3) : map메서드를 호출한 배열의 요소값, 인덱스, map메서들 호출한 배열(this)
const numbers = [1,4,9];

const roots = numbers.map(item => Math.sqrt(item));
console.log(roots);		//[1,2,3]
console.log(numbers); 	//[1,4,9]

forEach vs map

forEach와 map은 배열의 모든 요소를 순회하여 인수로 전달받은 콜백함수를 반복 호출하는 것은 같으나, 반환값이 forEach는 undefined이며, map은 콜백함수 반환값인 배열을 반환하는 차이점이 있다.

특징

  • 원본 배열을 변경되지 않는다.
  • map메서드는 요소값을 다른 값으로 매핑(mapping)한 새로운 배열을 생성하기 위한 고차함수이다.
  • map을 호출한 배열.length === map이 반환한 배열.length
    ( map을 호출한 배열.length의 값과 map이 반환한 배열.length값은 같다)
  • map메서드는 호출한 배열과 map메서드가 생성하여 반환한 배열은 1:1 매핑한다.
  • 콜백함수는 화살표 함수를 이용하면 map의 두번째 매개변수 this를 사용하지 않고 사용할 수 있다.(화살표 함수는 this를 바인딩하지 않기때문에 상위 스코프의 this를 참조한다)

4. Array.prototype.fillter (return : 배열)

모든 요소를 순회하면서 콜백함수의 반홤값이 true인 요소로만 구성된 새로운 배열을 반환한다.(값이 true인 결과만 추출하여 배열에 넣는다)

  • filter의 매개변수(2) : 콜백함수, filter메서드의 콜백함수 내부에서 this로 사용할 객체
  • 콜백 함수의 매개변수(3) : filter를 호출한 배열의 요소, 인덱스값, filter를 호출한 배열(this)
const number = [1,2,3,4,5];

const odds = numbers.filter(item => item % 2);
console.log(odds);	//[1,3,5]
//item % 2는 짝수인 요소들은 나머지가 0이 된다.
//0은 false이므로, filter에 의해 걸러져 홀수들이 출력된다.

특징

  • 원본 배열은 변경되지 않는다.
  • filter메서드는 자신을 호출한 배열에서 필터링 조건을 만족하는 특정 요소만 추출하여 새로운 배열을 만들고 싶을 때 사용한다.
  • filter메서드를 호출한 배열.length >= filter메서드가 생성한 배열
  • filter메서드는 자신을 호출한 배열에서 특정 요소를 제거하기 위해 사용할 수도 있다.(중복된 요소가 모두 제거된다.)
class Users{
	constructor(){
      this.users = [
        {id : 1, name : 'kim'},
        {id : 2, name : 'Lee'}
       ];

      //요소 추출
	findById(id){
      //id가 일치하는 사용자만 반환한다.
      return this.users.filter(users => users.id === id);
    }
      
     //요소 제거
      remove(id){
        //id가 일치하면 제거한다.
        return this.users.filter(users => users.id !== id);
      }
    }
  
  const users = new Users();

let user = users.findById(1);
console.log(user);	//[{id : 1, name: 'kim'}];

user = users.remove(1);

user = users.findById(1);
console.log(user);	//[]    

참고 : 특정 요소를 하나만 제거하고 싶다면 indexOf메서드와 splice를 통해 제거하면 된다.

5. Array.prototype.reduce (return : 1개의 결과 값)

모든 요소를 순회며 콜백함수의 반환값을 다음 순회 시에 콜백 함수의 첫번째 인수로 전달하면서 콜백 함수를 호출하여 하나의 결과값을 만들어 반환한다.

  • reduce메서드의 매개변수(2) : 콜백함수, 초기값
  • 콜백함수의 매개변수(4) : 초기값 또는 콜백 함수의 이전 반환값, reduce메서드를 호출한 배열의 요소값, 인덱스, reduce메서드를 호출한 배열자체(this)
//1부터 4까지 누적을 구한다.
const sum = [1,2,3,4].reduce((accumulator, currentValue, index, arr) => accmulator + cuurentValue, 0);

console.log(sum); 	//10

특징

  • 원본 배열을 변경되지 않는다.
  • reduce메서드는 자신을 호출한 배열의 모든 요소를 순회하며 하나의 결과값으르 구해야 하는 경우에 사용한다.
  • map, filter, some, every, find 같은 모든 배열의 고차 함수는 reduce 메서드로 구현할 수 있다.
  • filter메서드의 두번째 매개변수는 옵션이지만, 초기값을 전달하는 것이 안전한다.
    • 빈배열로 reduce메서드를 호출하면 에러가 발생한다.(이 경우에 reduce메서드에 초기값을 전달하면 에러가 발생하지 않는다)
    • 객체의 특정 프로퍼티 값을 합산하는 경우에는 반드시 초기값을 전달해야 한다.
const produce = [
  {id : 1, price : 100},
  {id : 2, price : 200},
  {id : 3, price : 300}
];
//1번 순회시 acc는 {id : 1, price : 100}, cur은 {id : 2, price : 200}이고
//2번 순회시 acc 300이고 cur은 {id : 3, price : 300}dlek.
//2번 순회 시 acc에 함수에 객체가 아닌 숫자값이 전달되는데 이때 acc.price의 값은 undefined가 되어 값이 나오지 않는다.
const priceSum = produce.reduce((acc,cur) => arr.price + cur.price);
console.log(priceSum);	//NaN

따라서 초기값을 세팅해두면 acc의 값은 숫자가 되어 계산 결과를 도출 할 수 있다.

const produce = [
  {id : 1, price : 100},
  {id : 2, price : 200},
  {id : 3, price : 300}
];
//1번 순회 acc : 0 , cur => {id : 1, price : 100}
//2번 순회 acc : 100 , cur => {id : 2, price : 200}
//3번 순회 acc : 300 , cur => {id : 3, price : 300}

const priceSum = produce.reduce((acc,cur) => arr + cur.price, 0);
console.log(priceSum);	//600

활용 예시

  1. 평균 구하기
  2. 최대값 구하기
  3. 요소의 중복 횟수 구하기
  4. 중첩 배열 평탄화
  5. 중복 요소 제거

6. Array.prototype.some (return : boolean)

호출한 배열의 모든 요소를 순회하며, 콜백함수의 반환값이 단 번이라도 참이면 true, 모두 거짓이면 false를 반환한다.

  • 콜백 함수의 매개변수(3) : 배열의 요소 값, 인덱스, some메서드를 호출한 배열(this)

특징

  • 빈 배열은 언제나 false이다.
  • 즉, 콜백 함수를 통해 정의한 조건을 만족하는 요소가 1개 이상 존재하는지 확인하고 그 값을 불리언 타입으로 반환한다.
//배열의 요소 중 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

//some 메서드를 호출한 배열이 빈 배열인 경우 언제나 false를 반환한다.
[].some(item => item > 3);	//false

7. Array.prototype.every (return : boolean)

호출한 배열의 요소를 순회하면서 콜백 함수의 반환값이 모두 참이면 true, 단 한번도 거짓이면 false를 반환한다.

특징

  • 배열의 모든 요소가 콜백함수르르 통해 정의한 조건을 모두 만족하는지 확인한다.
  • 빈 배열인 경우 true를 반환한다.
//배열의 모든 요소가 3보다 큰지 확인
[5, 10, 15].every(item => item >3); //true

//배열의 모든 요소가 10보다 큰지 확인
[5, 10, 15].every(item => item > 10);	//false

//every메서드를 호출한 배열이 빈 배열인 경우 언제나 true
[].every(item => item >3);	//true

8. Array.prototype.find (return : 요소)

호출한 배열의 요소를 순회하며, 콜백함수를 호출하여 반환값이 true인 첫번째 요소를 반환한다.

  • find메서드의 매개변수(2) : 콜백 함수, this

특징

  • true인 요소가 존재하지 않는다면, undefined가 반환한다.
  • 반환값은 배열이 아니면 조건에 참(true)인 요소가 반환된다.
const users = [
  {id : 1 name : 'Lee'},
  {id : 2, name : 'kem'},
  {id : 3 name : 'choi'},
  {id : 4 name : 'Park'}
];

user.find(user => user.id === 2); //{id : 2, name : 'Kim'}

//find메서드는 첫번째인 true 요소를 반환한다.
[1,2,2,3].find(item => item ===2);	//2

9. Array.prototype.findIndex (return : index)

호출한 배열의 요소를 순회하며, 콜백함수를 호출하여 반환값이 true인 첫번째 요소의 인덱스를 반환한다.

  • find메서드의 매개변수(2) : 콜백 함수, this

특징

  • true인 값이 없다면 -1을 반환한다.
const users = [
  {id : 1 name : 'Lee'},
  {id : 2, name : 'kem'},
  {id : 3 name : 'choi'},
  {id : 4 name : 'Park'}
];

user.findIndex(user => user.id === 2); // 1

10. Array.prototype.flatMap (return : 배열)

map메서드를 통해 생성된 새로운 배열을 평탄화한다.

  • map메서드와 flatMap메서드를 순차적으로 실행하는 효과가 있다.

특징

  • flatMap메서드는 flat메서드처럼 인수를 전달하여 평탄화 깊이를 지정할 수 없고 1단계만 평탄화한다.
const arr = ['hello', 'world'];

arr.flatMap(x => x.split(''));
//['h','e','l','l','o','w','o','r','l','d']
profile
기록하며 발전하는 삶

0개의 댓글