221215 TIL

오늘 한 일

  • 자바스크립트 완벽가이드 7장 배열 정리

배열 메서드

배열 메서드는 다음과 같이 분류할 수 있다.

  • 배열 요소를 순회하는 이터레이터 메서드
    • 일반적으로 각 요소에 대해 함수를 호출한다.
  • 배열의 앞이나 뒤에 요소를 추가하거나 제거하는 스택, 큐 메서드
  • 큰 배열의 추출, 삭제, 삽입, 충당(fill), 복사하는 하위 배열 메서드
  • 배열을 검색하고 정렬하는 메서드

1. 배열 이터레이터 메서드

  • 배열 요소를 순서대로 함수에 전달하는 방식으로 요소를 순회, 변환, 필터, 체크, 축소(reduce)할 수 있다.
  • 대부분의 이터레이터 메서드는
    • 첫번째 인자로 함수를 받으며 각 배열 요소(또는 일부 요소)에 함수를 한번씩 호출한다.
    • 성긴 배열이라면 존재하지 않는 값에는 함수를 호출하지 않는다.
    • 함수는 배열 요소의 값, 인덱스, 배열 자체 세 가지 인자를 받는다. (첫번째 인자만 사용하고 2, 3번째 무시될 때 많음)
    • 이터레이터 메서드는 선택 사항으로 두 번째 인자를 받는데, 첫번째 인자인 함수는 자신이 그 두번째 인자의 메서드인 것처럼 호출된다. (두번째 인자가 함수의 this가 된다.)

1-1. forEach()

  • 배열을 순회하며 각 요소에서 함수를 호출한다.
  • forEach()의 첫 번째 인자는 함수이다.
  • 배열 요소의 값, 배열 요소의 인덱스, 배열 자체를 인자로 전달해 함수를 호출한다.
let data = [1,2,3,4,5], sum = 0;
// 배열 요소의 합을 계산한다
data.forEach(value => { sum += value; }); // sum == 15

// 배열 요소를 각각 증가시킨다.
data.forEach(function(v, i, a) => { a[i] = v + 1; }); // data == [2,3,4,5,6]
  • forEach는 모든 요소를 함수에 전달하기 전에 반복을 멈추는 방법이 없다.
    • for 루프에 사용하는 break 문과 같이 동등한 수단이 없다.

1-2. map()

  • 각 배열 요소를 함수에 전달해 호출하며, 그 함수가 반환한 값으로 이루어진 배열을 반환한다.
let a = [1, 2, 3];
a.map(x => x*x); // => [1, 4, 9]: 함수는 x를 받아 x*x를 반환한다.
  • map은 새 배열을 반환하고 기존 배열을 수정하지 않는다.
  • 성긴 배열이라면? 존재하지 않는 요소에 대해 함수를 호출하지 않지만, 반환된 배열 역시 같은 위치에 갭이 있고 길이 또한 같다.

1-3. filter()

  • 기존 배열의 일부만 포함하는 부분 집합을 반환한다.
  • 전달하는 함수는 true 혹은 false를 반환한다.
    • 반환 값이 true이거나 true로 변환될 수 있는 값이면 해당 요소는 반환되는 배열에 포함된다.
let a = [5, 4, 3, 2, 1];
a.filter(x => x < 3); // => [2, 1]; 3미만인 값
a.filter((x, i) => i%2 === 0); // => [5, 3, 1]; 인덱스가 짝수인 값
  • 성긴 배열이라면? 존재하지 않는 값은 건너뛰고 반환하는 배열은 항상 빽빽한 배열이다.
  • 성긴 배열에서 갭을 제거하는 방법
let dense = sparse.filter(() => true);
  • filter()를 사용하면 갭과 함께 undefined, null 요소를 제거할 수 있다.
a = a.filter(x => x !== undefined && x !== null);

1-4. find()와 findIndex()

  • 메서드 판별 함수에서 true와 같은 값을 반환하는 요소를 찾기 때문에 filter()와 비슷한 역할이다.
  • 만족하는 요소를 찾으면 find()는 요소를 반환한다.
    • findIndex()는 인덱스를 반환한다.
  • 만족하는 요소를 찾지 못하면 find()는 undefined를 반환한다.
    • findIndex()는 -1을 반환한다.

every()와 some()

  • 배열 요소에 판별 함수를 적용하고 결과에 따라 true 혹은 false를 반환한다.
  • every() 메서드는 판별 함수가 배열의 모든 요소에 대해 true를 반환할 때만 true 반환한다.
let a = [1,2,3,4,5];
a.every(x => x < 10) // => true: 모든 값이 10 미만이다.
a.every(x => x % 2 === 0) // => false: 짝수가 아닌 값이 있다.
  • some() 메서드는 판별 함수가 true를 반환하는 것이 하나라도 있으면 true 반환한다.
let a = [1,2,3,4,5];
a.some(x => x % 2 === 0) // => true: a에는 짝수가 있다.
a.some(isNaN) // => false: a에 NaN은 없다.
  • every()와 some()은 어떤 값을 반환할지 확실해지는 순간 순회를 멈춘다.

reduce()와 reduceRight()

  • 제공하는 함수를 사용해 배열 요소를 값 하나로 만든다.
  • 함수형 프로그래밍에서 흔히 쓰는 방법이다.
let a = [1,2,3,4,5];
a.reduce((x,y) => x+y, 0)          // => 15: 값의 합계
a.reduce((x,y) => x*y, 1)          // => 120: 값의 곱
a.reduce((x,y) => (x > y) ? x : y) // => 5: 가장 큰 값
  • reduce()는 인자 두 개를 받는다.
  • 첫번째 인자는 축소 동작을 행하는 함수이다.
    • 어떤 방식으로든 값 두개를 받아 하나를 반환한다.
  • 두번째 인자는 선택 사항이며 함수에 전달할 초깃값이다.

  • reduce()의 첫번째 인자는 축소 작업의 결과이다.
  • 함수를 처음 호출할 때는 그동안 행한 작업이 없기 때문에 두번째 인자로 전달한 초깃값을 사용한다.
  • 두번째 이후부터 이전 호출에서 반환한 값을 사용한다.
    • 초깃값이 없을 경우 배열의 첫번째 요소를 초깃값으로 사용한다.
  • 빈 배열에 초깃값 없이 reduce()를 호출하면 TypeError가 발생한다.
  • 값이 하나 있는 배열에서 reduce()를 호출하면 그 값을 그대로 반환하고 함수도 호출하지 않는다.

  • reduceRight()는 reduce()와 마찬가지이지만 축소 작업을 오른쪽에서 왼쪽으로 진행한다.

flat()과 flatMap()을 사용한 배열 평탄화

  • ES2019에서 도입한 flat() 메서드는 기존 배열과 같은 요소로 이루어진 평탄한(flat), 중첩되지 않은 새 배열을 반환한다.
[1, [2, 3]].flat()   // => [1, 2, 3]
[1, [2, [3]]].flat() // => [1, 2, [3]]
  • 인자 없이 flat()을 호출하면 한 단계만 평탄화 한다.
  • 레벨을 늘리려면 flat()에 숫자를 전달한다.
let a = [1, [2, [3, [4]]]];
a.flat(1) // => [1, 2, [3, [4]]];
...
a.flat(4) // => [1, 2, 3, 4];
  • flatMap() 메서드는 map() 메서드와 똑같이 동작하지만 반환하는 배열이 flat()에 전달한 것처럼 자동으로 평탄화 된다.

    ⇒ a.flatMap(f)는 a.map(f).flat()과 동일하게 작동하며, 더욱 효율적이다.

let phrases = ["안녕 겨울아", "나는 겨울이 좋아"];
let words = phrases.flatMap(phrase => phrase.split(" "));
words // ['안녕', '겨울아', '나는', '겨울이', '좋아']

concat()으로 배열 병합

  • 기존 배열의 요소를 포함하고 그 뒤에 concat()의 인자를 포함하는 새 배열을 만들어 반환한다.
  • concat()은 배열의 배열을 재귀적으로 평탄화하지는 않는다.
let a = [1,2,3];
a.concat(4,5) // => [1,2,3,4,5]
a.concat([4,5],[6,7]) // => [1,2,3,4,5,6,7]