하얀 코딩

[JavaScript - 17] (내장) 고차 함수 본문

JavaScript

[JavaScript - 17] (내장) 고차 함수

whitecoding 2023. 1. 11. 20:05

JavaScript에는 기본적으로 내장된 고차 함수가 여럿 있습니다.

그중에서 배열 메서드들 중 일부가 대표적인 고차 함수에 해당합니다.

 

본 게시글에서는 다음과 같이

자주 사용하는 고차함수를 다루어 보도록 하겠습니다.

 

  • Array.prototype.filter()
  • Array.prototype.map()
  • Array.prototype.reduce()
  • Array.prototype.forEach()

Array.prototype.filter()

 

filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환합니다.

 

filter()는 배열 내 각 요소에 대해 한 번 제공된 callback 함수를 호출해,

callback이 true로 강제하는 값을 반환하는 모든 값이 있는 새로운 배열을 생성합니다.

callback은 할당된 값이 있는 배열의 인덱스에 대해서만 호출됩니다.

삭제됐거나 값이 할당된 적이 없는 인덱스에 대해서는 호출되지 않습니다.

callback 테스트를 통과하지 못한 배열 요소는 그냥 건너뛰며 새로운 배열에 포함되지 않습니다.

arr.filter(callback(element[, index[, array]])[, thisArg])

1. callback

각 요소를 시험할 함수. true를 반환하면 요소를 유지하고, false를 반환하면 버립니다. 다음 세 가지 매개변수를 받습니다.

 

  • element : 처리할 현재 요소
  • index (Optional) : 처리할 현재 요소의 인덱스
  • array (Optional) : filter()를 호출한 배열

2. thisArg (Optional)

callback을 실행할 때 this로 사용하는 값.

 

3. return 

테스트를 통과한 요소로 이루어진 새로운 배열. 어떤 요소도 테스트를 통과하지 못했으면 빈 배열을 반환합니다.

const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];

const result = words.filter(word => word.length > 6);

console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
const arr = [
  { id: 15 },
  { id: -1 },
  { id: 0 },
  { id: 3 },
  { id: 12.2 },
  { },
  { id: null },
  { id: NaN },
  { id: 'undefined' }
];

let invalidEntries = 0;

function isNumber(obj) {
  return obj !== undefined && typeof(obj) === 'number' && !isNaN(obj);
}

function filterByID(item) {
  if (isNumber(item.id) && item.id !== 0) {
    return true;
  }
  invalidEntries++;
  return false;
}

const arrByID = arr.filter(filterByID);

console.log('Filtered Array\n', arrByID);
// Filtered Array
// [{ id: 15 }, { id: -1 }, { id: 3 }, { id: 12.2 }]

console.log('Number of Invalid Entries = ', invalidEntries);
// Number of Invalid Entries = 5

Array.prototype.map()

map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환합니다.

 

map은 callback 함수를 각각의 요소에 대해 한번씩 순서대로 불러 그 함수의 반환값으로 새로운 배열을 만듭니다. 

callback 함수는 (undefined도 포함해서) 배열 값이 들어있는 인덱스에 대해서만 호출됩니다. 

즉, 값이 삭제되거나 아직 값이 할당/정의되지 않은 인덱스에 대해서는 호출되지 않습니다.

arr.map(callback(currentValue[, index[, array]])[, thisArg])

1. callback

새로운 배열 요소를 생성하는 함수. 다음 세 가지 인수를 가집니다.

 

  • currentValue : 처리할 현재 요소
  • index (Optional) : 처리할 현재 요소의 인덱스
  • array (Optional) : map()을 호출한 배열

2. thisArg (Optional)

callback을 실행할 때 this로 사용하는 값.

 

3. return 

배열의 각 요소에 대해 실행한 callback의 결과를 모은 새로운 배열.

const array1 = [1, 4, 9, 16];

// Pass a function to map
const map1 = array1.map(x => x * 2);

console.log(map1);
// expected output: Array [2, 8, 18, 32]
const kvArray = [{key:1, value:10},
               {key:2, value:20},
               {key:3, value: 30}];

const reformattedArray = kvArray.map(function(obj){
   const rObj = {};
   rObj[obj.key] = obj.value;
   return rObj;
});
// reformattedArray는 [{1:10}, {2:20}, {3:30}]

Array.prototype.reduce()

reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서 (reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.

 

콜백의 최초 호출 때 accumulator와 currentValue는 다음 두 가지 값 중 하나를 가질 수 있습니다. 

만약 reduce() 함수 호출에서 initialValue를 제공한 경우, accumulator는 initialValue와 같고 currentValue는 배열의 첫 번째 값과 같습니다. initialValue를 제공하지 않았다면, accumulator는 배열의 첫 번째 값과 같고 currentValue는 두 번째와 같습니다.

initialValue 를 제공하지 않으면, reduce()는 인덱스 1부터 시작해
콜백 함수를 실행하고 첫 번째 인덱스는 건너 뜁니다. 
initialValue 를 제공하면 인덱스 0에서 시작합니다.

 

배열이 비어있는데 initialValue도 제공하지 않으면 TypeError가 발생합니다. 

배열의 요소가 (위치와 관계없이) 하나 뿐이면서 initialValue를 제공되지 않은 경우, 또는 initialValue는 주어졌으나 

배열이 빈 경우엔 그 단독 값을 callback 호출 없이 반환합니다.

arr.reduce(callback[, initialValue])

1. callback

배열의 각 요소에 대해 실행할 함수. 다음 네 가지 인수를 받습니다.

 

  • accumulator : 배열의 0번째 요소를 받아 초기값이 됩니다. initialValue를 제공한 경우 accumulator은 initialValue값을 초기값을 사용합니다. callback 결과로 인해 계속 누적 되는 변수 입니다.
  • currentValue : 처리할 현재 요소
  • currentIndex (Optional) : 처리할 현재 요소의 인덱스. initialValue를 제공한 경우 0, 아니면 1부터 시작합니다.
  • array (Optional) : reduce()를 호출한 배열

2. initialValue (Optional)

callback의 최초 호출에서 첫 번째 인수에 제공하는 값. 초기값을 제공하지 않으면 배열의 첫 번째 요소를 사용합니다. 

빈 배열에서 초기값 없이 reduce()를 호출하면 오류가 발생합니다.

 

3. return 

누적 계산의 결과 값.

function getLengthOfLongestElement(arr) {
  if (arr.length === 0){return 0;}
  return arr.reduce((acc,cur) => acc.length >= cur.length ? acc : cur, 0).length
}

let output = getLengthOfLongestElement(['one', 'two', 'three']);
console.log(output); // -->
function joinArrayOfArrays(arr) {
  return arr.reduce((acc, cur) => acc.concat(cur))
}

let output = joinArrayOfArrays([
  [1, 4],
  [true, false],
  ['x', 'y'],
]);

console.log(output); // --> [1, 4, true, false, 'x', 'y']
function findShortestWord(arr) {
  const strArr = arr.filter(el => typeof el === 'string')
  if(strArr.length !== 0){
    return strArr.reduce((acc, cur) => acc.length <= cur.length ? acc : cur)
  } else return ''
}

// 배열을 입력받아 배열에서 가장 짧은 길이를 가진 문자열 요소를 리턴해야 합니다.

let output = findShortestWord([4, 'two', 2, 'three']);
console.log(output); // --> 'two'

Array.prototype.forEach()

forEach() 메서드는 주어진 함수를 배열 요소 각각에 대해 실행합니다.

 

forEach()는 주어진 callback을 배열에 있는 각 요소에 대해 오름차순으로 한 번씩 실행합니다. 

삭제했거나 초기화하지 않은 인덱스 속성에 대해서는 실행하지 않습니다. (예: 희소 배열)

 

forEach()는 각 배열 요소에 대해 한 번씩 callback 함수를 실행합니다. map()과 reduce()와는 달리 undefined를 반환하기 때문에 메서드 체인의 중간에 사용할 수 없습니다. 대표적인 사용처는 메서드 체인 끝에서 부작용(side effect)을 실행하는 겁니다.

 

forEach()는 배열을 변형하지 않습니다. 그러나 callback이 변형할 수는 있습니다.

arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])

1. callback

새로운 배열 요소를 생성하는 함수. 다음 세 가지 인수를 가집니다.

 

  • currentValue : 처리할 현재 요소
  • index : 처리할 현재 요소의 인덱스
  • array  : forEach()을 호출한 배열

2.thisArg (Optional)

callback을 실행할 때 this로 사용하는 값.

 

3. return 

undefined

예외를 던지지 않고는 forEach() 를 중간에 멈출 수 없습니다.
중간에 멈춰야 한다면  forEach() 가 적절한 방법이 아닐지도 모릅니다.
다음 방법으로는 조기에 반복을 종료할 수 있습니다.

 

  • for...of, for...in 반복문
  • Array.prototype.every()
  • Array.prototype.some()
  • Array.prototype.find()
  • Array.prototype.findIndex()

every, some, find, findIndex는 배열 요소를 판별 함수에 전달하고, 그 결과의 참/거짓 여부에 따라 반복의 종료 여부를 결정합니다.

 

// for 반복문을 forEach()로 바꾸기
const items = ['item1', 'item2', 'item3'];
const copy = [];

// for문
for (let i=0; i<items.length; i++) {
  copy.push(items[i]);
}

// forEach()
items.forEach(function(item){
  copy.push(item);
});
const arraySparse = [1,3,,7]
let numCallbackRuns = 0

arraySparse.forEach(function(element){
  console.log(element)
  numCallbackRuns++
})

console.log("numCallbackRuns: ", numCallbackRuns)

// 1
// 3
// 7
// numCallbackRuns: 3