본문 바로가기

JavaScript

20210712 JavaSciprt DeepDive 10 : 배열 고차 함수, sort, forEach, map, filter, reduce, some, every, find, findIndex, flatMap

JavaScript Deep Dive 10



용어 및 중요사항 정리


배열 고차 함수 표시 Callback return
sort Array.prototype.sort(comparisonCallback) 음수(첫 인수),
양수(두번째 인수),
0(정렬X)
정렬된 배열을 반환
forEach Array.prototype.forEach(Callback, bindThis) Callback(value, index, array) undefined
map Array.prototype.map(Callback, bindThis) Callback(value, index, array 새로운 배열을 반환
filter Array.prototype.filter(Callback, bindThis) Callback(value, index, array) 콜백함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환
reduce Array.prototype.reduce(Callback, FirstAccumulator) Callback(accumulator, currentValue, index, array) 제일 마지막 Callback 결과값을 만들어 반환
some Array.prototype.some(Callback, bindThis) Callback(value, index, array) 콜백함수의 반환값이 한번이라도 참이면 true, 모두 거짓이면 false 반환
every Array.prototype.every(Callback, bindThis) Callback(value, index, array) 콜백함수의 반환값이 모두 참이면 true 반환, 한번이라도 거짓이면 false
find Array.prototype.find(Callback, bindThis) Callback(value, index, array) Callback 반환값이 true인 첫번째 요소를 반환
findIndex Array.prototype.findIndex(Callback, bindThis) Callback(value, index, array) Callback 반환값이 true인 첫번째 요소의 인덱스를 반환
flatMap Array.prototype.flatMap(Callback, bindThis) Callback(value, index, array) Callback으로 처리된 map 배열에서 평탄화된 새로운 배열을 반환



배열


배열 고차 함수


  • 고차 함수: 외부 상태의 변경이나 가변 데이터를 피하고 불변성을 지향하는 함수형 프로그래밍 기반
  • 함수형 프로그래밍 : 순수 함수와 보조 함수를 통해 조건문과 반복문을 제거하여 복잡성 해결하고 변수의 사용을 억제하여 상태 변경을 피하려는 프로그래밍 패러다임

  • Array.prototype.sort
    • Array.prototype.sort(comparisonCallback)
    • 배열의 요소를 콜백 비교함수 기준으로 정렬함 (한글 문자열도 가능)
    • return : 정렬된 배열을 반환
    • comparisonCallback : 요소들의 비교 기준이 되는 콜백함수 (숫자의 경우 유니코드 코드 포인트로 정렬시 문제가 생겨서 비교함수를 사용)
      • 비교함수의 return 값이 음수 -> 비교함수의 첫번째 인수를 우선 정렬
      • 비교함수의 return 값이 0 -> 정렬하지 않음
      • 비교함수의 return 값이 양수 -> 비교함수의 두번째 인수를 우선 정렬
        • a, b => a - b : 오름차순
        • a, b => b - a : 내림차순
      • 객체 요소의 정렬은, 숫자 연산자가 작용되지 않으므로 삼항연산자로 음수, 양수, 0을 지정해줘야 함
    • 비교함수 콜백이 생략된 경우, 기본적인 오름차순으로 정렬
    • 문자 정렬
      • 내림차순 정렬 : sort 후 reverse 메서드 사용
      • 원본 배열을 직접 변경
    • 유니코드 코드 포인트 순서를 따름 (숫자의 경우 일시적으로 문자열로 바꾸어 정렬하여 비교함수 필요)
    • 내부적으로 Timsort 알고리즘을 사용함(Mergesort의 최적화 버전)
// 문자 정렬
const strings = ["C", "B", "A"];

// 오름차순: sort
strings.sort();
// ['A', 'B', 'C']

// 내림차순: sort 후 reverse
strings.revese();
// ['C', 'B', 'A']

// 숫자 정렬
const points = [40, 100, 1, 5, 2, 25, 10];

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

points.sort((a, b) => b - a);
console.log(points); // [100, 40, 25, 10, 5, 2, 1]
console.log(`최대값 ${points[0]}, 최소값 ${points[points.length - 1]}`); // 최대값 100, 최소값 1

// 객체 정렬
const todos = [
  { id: 4, content: "JS" },
  { id: 1, content: "HTML" },
  { id: 2, content: "CSS" },
];

function compare(key) {
  return (a, b) => (a[key] > b[key] ? 1 : a[key] < b[key] ? -1 : 0);
}

todos.sort(compare("id"));
console.log(todos);

/* 
[
  { id: 1, content: "HTML" },
  { id: 2, content: "CSS" },
  { id: 4, content: "JS" },
] 
*/

todos.sort(compare("content"));
console.log(todos);

/* 
[
  { id: 2, content: "CSS" },
  { id: 1, content: "HTML" },
  { id: 4, content: "JS" },
] 
*/

  • Array.prototype.forEach
    • Array.prototype.forEach(Callback, bindThis)
    • 내부에서 반복문을 통해 자신을 호출한 배열을 순회하면서 수행해야 할 처리를 콜백 함수로 전달받아 반복 호출
    • return : undefined
      • Callback(value, index, array) : 요소의 값, 인덱스, 배열 자체(this)를 인수로 전달 받아 작업을 처리
        • break, continue 문을 사용할 수 없음
        • 희소 배열의 경우 존재하지 않는 요소는 순회 대상에서 제외 (for문은 희소배열을 undefined로 순회함)
      • bindThis : 콜백함수 내부에서 사용할 This를 특정하여 객체 전달할 수 있음
    • for문을 대체할 수 있는 고차 함수
    • 원본 배열을 변경하지 않음
      • array인 this 인수를 활용하면 원본 배열을 직접 변경할 수 있음

  • 콜백 함수 내부에서 this 사용시

    • 클래스의 forEach 콜백에서 사용한 경우 -> this = undefined (콜백 함수가 일반함수로 호출되고 strict mode이기 때문)
    • 생성자 함수, 함수 표현식인 콜백에서 사용 -> this = window 전역 객체 (콜백 함수가 일반 함수로 호출되기 때문)
    • 화살표 함수인 콜백 내부에서 사용 -> this = 상위 스코프의 this

  • 폴리필 polyfill : 최신 사양의 기능을 지원하지 않는 브라우저를 위해 누락된 최신 사양의 기능을 구현하여 추가하는 것
if (!Array.prototype.forEach) {
  Array.prototype.forEach = function (callback, thisArg) {
    if (typeof callback !== "function") {
      throw new TypeError(callback + "is not a function");
    }

    thisArg = thisArg || window;

    for (var i = 0; i < this.length; i++) {
      callback.call(thisArg, this[i], i, this);
    }
  };
}

  • for vs forEach
비교 for forEach
break, continue 문 O X
가독성 forEach보다 낮음 높음
성능 높음 for보다 낮음
희소배열 undefined로 순회 순회대상에서 제외

  • Array.prototype.map
    • Array.prototype.map(Callback, bindThis)
    • 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출
    • return : 콜백 함수의 반환값들로 구성된 새로운 배열을 반환
      • Callback(value, index, array) : 요소의 값, 인덱스, 배열 자체(this)를 인수로 전달 받아 작업을 처리
      • bindThis : 콜백함수 내부에서 사용할 This를 특정하여 객체 전달할 수 있음
    • 원본 배열은 변경되지 않음
    • 원본 배열의 length 프로퍼티 값 === 새로운 배열의 length 프로퍼티 값

  • 콜백 함수 내부에서 this 사용시(forEach와 동일함)
    • 클래스의 map 콜백에서 사용한 경우 -> this = undefined (콜백 함수가 일반함수로 호출되고 strict mode이기 때문)
    • 생성자 함수, 함수 표현식인 콜백에서 사용 -> this = window 전역 객체 (콜백 함수가 일반 함수로 호출되기 때문)
    • 화살표 함수인 콜백 내부에서 사용 -> this = 상위 스코프의 this

  • Array.prototype.filter
    • Array.prototype.filter(Callback, bindThis)
    • 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출
      • Callback(value, index, array) : 요소의 값, 인덱스, 배열 자체(this)를 인수로 전달 받아 작업을 처리
      • bindThis : 콜백함수 내부에서 사용할 This를 특정하여 객체 전달할 수 있음
    • return : 콜백함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환
    • 사용되는 상황
      • 필터링 조건을 만족하는 특정 요소만 추출하여 새로운 배열을 만들고 싶을 때 사용
      • 원본 배열에서 특정 요소를 제거하기 위해 사용 가능 (중복 요소들 제거 가능)
        • 특정요소가 아닌것을 조건으로 넣어 특정 요소가 아닌것만 모아 원본 배열에 재할당 -> 특정요소가 없는 원본배열
        • 특정요소가 하나만 제거시 -> indexOf + splice
    • 원본 배열은 변경되지 않음
    • 원본 배열의 length 프로퍼티 값 >= 새로운 배열의 length 프로퍼티 값

  • 콜백 함수 내부에서 this 사용시(forEach, map와 동일함)
    • 클래스의 map 콜백에서 사용한 경우 -> this = undefined (콜백 함수가 일반함수로 호출되고 strict mode이기 때문)
    • 생성자 함수, 함수 표현식인 콜백에서 사용 -> this = window 전역 객체 (콜백 함수가 일반 함수로 호출되기 때문)
    • 화살표 함수인 콜백 내부에서 사용 -> this = 상위 스코프의 this (추천)

  • Array.prototype.reduce
    • Array.prototype.reduce(Callback, FirstAccumulator)
    • 자신을 호출한 배열을 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출
      • 콜백 함수의 반환값(Callback Return)을 다음 순회시에 콜백 함수의 첫번째 인수(accumulator)로 전달하여 콜백함수 호출함
      • Callback(accumulator, currentValue, index, array)
    • return : 제일 마지막 Callback 결과값을 만들어 반환
    • FirstAccumulator를 항상 명시적으로 적어 초기값을 전달해 주는게 안전함
      • 빈 배열인 경우에도 Error를 발생시키지 않음
    • 원본배열 변경 없음
// 평균 구하기
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);

// 최대값 구하기
const max = values.reduce((acc, cur) => (acc > cur ? acc : cur), 0);
const mathMax = Math.max(...values);

// 요소별 중복 횟수 구하기
const alphabet = ["a", "c", "k", "c", "d", "l", "c", "a", "h", "g"];

const count = alphabet.reduce((acc, cur) => {
  // acc에 cur에 해당하는 프로퍼티가 없으면 동적으로 생성해서 1을 할당
  // cur에 해당하는 프로퍼티가 있으면 거기에 1을 더함
  acc[cur] = (acc[cur] || 0) + 1;
  return acc;
}, {});

// 중첩 배열 평탄화
const nestingArray = [1, [2, 3], 4, [5, 6]];

const flatten = nestingArray.reduce((acc, cur) => acc.concat(cur), []);
const flat = nestingArray.flat(1);

// 중복 요소 제거
const duplicateValues = [1, 2, 1, 3, 5, 4, 5, 3, 4, 4];

// reduce 방식
const result = duplicateValues.reduce((acc, cur, i, arr) => {
  // 앞에서 부터 처음에 찾은 값이면 넣고, 아니면 안넣고
  if (arr.indexOf(cur) === i) acc.push(cur);
  return acc;
}, []);

// filter 방식
const result = duplicateValues.filter((v, i, arr) => arr.indexOf(v) === i);
// Set 집합 방식
const result = [...new Set(duplicateValues)];

  • Array.prototype.some
    • Array.prototype.some(Callback, bindThis)
    • 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출
    • Callback(value, index, array)
    • return : 콜백함수의 반환값이 한번이라도 참이면 true, 모두 거짓이면 false 반환
      • 원본 배열이 빈 배열인 경우 언제나 false 반환

  • Array.prototype.every
    • Array.prototype.every(Callback, bindThis)
    • 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출
    • Callback(value, index, array)
    • return : 콜백함수의 반환값이 모두 참이면 true 반환, 한번이라도 거짓이면 false
      • 원본 배열이 빈 배열인 경우 언제나 true 반환

  • Array.prototype.find
    • Array.prototype.find(Callback, bindThis)
    • 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출
    • Callback(value, index, array)
    • return : Callback 반환값이 true인 첫번째 요소를 반환
      • 콜백 함수의 반환값이 true가 없으면 undefined 반환
      • find의 결과는 항상 요소값

  • Array.prototype.findIndex
    • Array.prototype.findIndex(Callback, bindThis)
    • 자신을 호출한 배열의 요소를 순회하면서 인수로 전달된 콜백 함수를 호출
    • Callback(value, index, array)
    • return : Callback 반환값이 true인 첫번째 요소의 인덱스를 반환
      • 콜백 함수의 반환값이 true가 없으면 -1 반환

  • Array.prototype.flatMap
    • Array.prototype.flatMap(Callback, bindThis)
    • map메서드를 통해 생성된 새로운 배열을 평탄화함 (map + flat)
    • Callback(value, index, array)
    • return : Callback으로 처리된 map 배열에서 평탄화된 새로운 배열을 반환
    • 1단계 평탄화만 함