VanilaJS-HOF

Javascript

배열고차함수 (Array Higher Order Function in JS)


고차함수(Higher-Order-Function)는 함수를 인수로 전달받거나 반환하는 함수를말한다.

자바스크립트에서 활용도가 매우높은 배열에사용되는 고차함수에 대해 알아보자.


Array.prototype.sort( )

  • 원본배열의 요소를 정렬한다. ( 기본적으로 오름차순으로 요소를 정렬한다. )
  • 정렬된 배열을 반환한다.
  • 원본배열이 변환된다.
1
2
3
4
5
const arr = [1, 3, 5, 20, 11];
const result = arr.sort();

console.log(arr); // [1, 11, 20, 3, 5]
console.log(result); // [1, 11, 20, 3, 5]

필자는 “sort 메서드를 따로 분류해서 페이지를 만들까?” 까지 고민할정도로 중요하고 또 어렵게 느껴졌다. 좀더 자세히 sort 메서드에 대해 알아보자.

sort 메서드는 배열안의 요소에 따라 다르게 동작한다.

  • 문자열로 이루어진 배열의 경우
  • 숫자로 이루어진 배열의 경우

sort 메서드는 숫자로 이루어진 배열의 요소들을 일시적으로 문자열로 변환시켜 유니코드의 순서를 기준으로 정렬한다.

먼저 유니코드(Unicode)에 관해서 간단하게 설명한다면, 컴퓨터에서 사용되는 모든기호나 문자들에 관해서 컴퓨터가 이해할수있게 만들어 놓은 표기방식이라고 할수있겠다.

sort메서드는 유니코드를 기준으로 배열안의 요소들을 정렬하게 되는데 기존에 문자열 같은경우는 우리가 알고있는 알파벳순서에 따라 정상적으로 잘 동작한다.

1
2
3
4
5
const fruits = ['Apple', 'Orange', 'Banana', 'Peach', 'Grape'];
const result = fruits.sort();

console.log(fruits); // ["Apple", "Banana", "Grape", "Orange", "Peach"]
console.log(result); // ["Apple", "Banana", "Grape", "Orange", "Peach"]

하지만, 숫자 같은 경우 조금 다르다.

1
2
3
4
5
const arr = [1, 3, 5, 20, 11];
const result = arr.sort();

console.log(arr);) // [1, 11, 20, 3, 5]
console.log(result); // [1, 11, 20, 3, 5]

위 예시처럼 숫자같은경우는 11과 20이 3과 5보다 먼저 정렬된다. 이는 유니코드 표기법에 의해 정렬되었기 때문이다. 즉, 숫자에서 일시적으로 문자로 변환된 문자열들을 유니코드를 기준으로 비교하면 문자열 ‘11’이 ‘20’, ‘3’, ‘5’ 보다 더 앞서기 때문이다.

비교함수 전달하기

따라서 숫자요소를 정렬할때는 특별히 비교함수를 sort 메서드 안으로 전달해야한다.

0을 기준으로 오름차순과 내림 차순이 결정된다.

  • 반환되는 값이 0 이상이면 오름차순 ( 0 < returnValue ) - 첫번째 인수를 우선으로 하여 정렬
  • 반환되는 값이 0 이면 정렬하지 않음
  • 반환되는 값이 0 이하면 내림차순 ( 0 > returnValue ) - 두번째 인수를 우선으로 하여 정렬

오름차순정렬

1
2
3
4
5
6
7
8
const arr = [3, 10, 21, 14, 5];
const compare = (next, prev) => {
return next - prev;
};
const result = arr.sort(compare);

console.log(arr); // (5) [3, 5, 10, 14, 21]
console.log(result); // (5) [3, 5, 10, 14, 21]

내림차순정렬

1
2
3
4
5
6
7
8
const arr = [3, 10, 21, 14, 5];
const compare = (next, prev) => {
return prev - next;
};
const result = arr.sort(compare);

console.log(arr); // (5) [21, 14, 10, 5, 3]
console.log(result); // (5) [21, 14, 10, 5, 3]

위 코드의 compare 매개 변수의 이름을 잘보면 next, prev 라고 명시했다. 이는 sort 메서드 내부동작의해서 원본배열의 secondIndex가 next로 들어가고 firstIndex가 prev로 들어가기에 이렇게 작성했다. 필자는 이것을 알아내느라 하루를 소모했다..😂

sort 메서드 관련 좀더 자세한 사항은 아래의 이곳, 저곳, 요곳을 참조하면된다!

유니코드 관련 참조사이트


Array.prototype.forEach((currentValue, index, arr), [thisArg])

  • for문을 대체할수 있는 고차함수이다.
  • 자신을 호출한 배열의 모든요소를 순회한다.
  • 콜백함수로 수행해야할 처리를 전달받아 반복호출한다.
  • forEach 문의 반환값은 언제나 undefined이다.
  • 원본배열을 변경하지않는다.
1
2
3
4
5
const numbers = [1, 2, 3, 4, 5];
const result = numbers.forEach((number) => number * 2);

console.log(numbers); // (5) [1, 2, 3, 4, 5]
console.log(result); // undefined

forEach문은 원본 배열을 변경하지 않지만, 콜백함수를 통해 원본 배열을 변경할수는 있다.

1
2
3
4
5
6
7
8
9
10
const numbers = [1, 2, 3, 4, 5];
const result = numbers.forEach((number, index, arr) => {
console.log(number); // 1 2 3 4 5
console.log(index); // 0 1 2 3 4
console.log(arr); // [2, 2, 3, 4, 5] ··· 순회시마다 원본배열은 변화
return (arr[index] = number * 2);
});

console.log(numbers); // (5) [2, 4, 6, 8, 10]
console.log(result); // undefined

콜백함수의 3번째 인수로 전달된 원본배열을 index로 직접 참조해서 값을 변경하는것이 가능하다.

콜백함수안으로 this의 전달과 그 이유

forEach 메서드의 두번째 인수로 this로 사용할 객체를 전달할수있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Numbers {
numberArray = [];

multiply(arr) {
arr.forEach(function (elem) {
this.numberArray.push(elem * elem);
}, this);
console.log(this); // Numbers {numberArray: Array(3)}
}
}

const arr = new Numbers();
arr.multiply([1, 2, 3]);
console.log(arr.numberArray); // (3) [1, 4, 9]

이는 일반함수로 호출한 this의 값은 전역객체(window)를 가르키기에 콜백함수내에서 사용될 this의 값과 고차함수의 몸체안에서의 this와 일치시키기 위함이다.

class함수 내에서 this를 전달하지 않는다면 typeError: Cannot read property ‘numberArray’ of undefined가 발생하는데 이는 클래스 내부의 모든 코드에는 암묵적으로 strictmode가 적용되기 떄문이다.

다시말해, class함수 내에서 strictmode(엄격모드)가 적용될때 this가 전역객체(window)를 가리키는것은 this의 사용이유(자신이 속한 위치를 나타내는것)가 적절하지 못하기때문에 undefined를 반환하게된다.

보통 ES6 이후부터는 화살표함수(자체적인 this를 가지지 않는다.)를 사용함으로써 상위 스코프의 this를 가르키게끔 하여 이러한 문제를 해결한다.

그리고 forEach문은 break, continue문을 사용할수 없으며, 배열의 모든 요소를 순회한다.

Array.prototype.map((currentValue, index, arr), [thisArg])

  • 자신을 호출한 배열의 모든 요소를 순회한다.
  • 콜백함수로 수행해야할 처리를 전달받아 반복호출한다.
  • 콜백함수의 반환값들로 구성된 새로운 배열을 반환한다.
  • 콜백함수의 반환값이 map메서드를 호출한 배열의 요소를 1 : 1 매핑한다.
  • 원본배열이 변환되지 않는다.
1
2
3
4
5
const arr = [1, 2, 3, 4, 5];
const result = arr.map((v, i, arr) => v * 2);

console.log(arr); // (5) [1, 2, 3, 4, 5]
console.log(result); // (5) [2, 4, 6, 8, 10]

Array.prototype.map 메서드는 메서드를 호출한 배열의 각각의 요소값을 다른 값으로 매핑하여 새로운 배열을 생성하기 위해 사용한다.

Array.prototype.filter((currentValue, index, arr), [thisArg])

  • 자신을 호출한 배열의 모든 요소를 순회한다.
  • 콜백함수로 수행해야할 처리를 전달받아 반복호출한다.
  • 콜백함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환한다.
  • 원본배열이 변환되지 않는다.
1
2
3
4
5
const arr = [1, 2, 3, 4, 5];
const result = arr.filter((v, i, arr) => v < 2);

console.log(arr); // (5) [1, 2, 3, 4, 5]
console.log(result); // [1]

Array.prototype.filter 메서드는 메서드를 호출한 각각의 요소값들중 콜백함수로 전달한 조건에 만족하는(반환값이 true) 요소로 이루어진 배열을 반환한다.

Array.prototype.reduce((accumulator, currentValue, index, arr), [InitialValue])

  • 자신을 호출한 배열의 모든 요소를 순회한다.
  • 콜백함수로 수행해야할 처리를 전달받아 반복호출한다.
  • 콜백함수의 반환값을 다음순회시에 콜백함수의 첫째인수로 전달한다.
  • 하나의 결과값을 반환한다.
  • 원본배열이 변환되지 않는다.
1
2
3
4
5
const arr = [1, 2, 3, 4, 5];
const result = arr.reduce((acc, cur, index, arr) => acc + cur, 0);

console.log(arr); // (5) [1, 2, 3, 4, 5]
console.log(result); // 15

Array.prototype.reduce 메서드는 메서드를 호출한 각각의 요소값들중 콜백함수로 전달된 초기값(initialValue)과 계산식을 가지고 배열의 요소를 계산하여 반환되는 값을 콜백함수의 첫번째 인수(acc)로 넘겨주면서 호출한다. 즉, 값을 계속 누산하여 하나의 결과값을 반환한다.

reduce메서드의 초기값 전달과 그 이유

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const arr = [1, 2, 3, 4, 5];
const result = arr.reduce((acc, cur, index, arr) => acc + cur, 0);

/*
acc + cur
0 + 1
1 + 2
3 + 3
6 + 4
10 + 5
*/

console.log(arr); // (5) [1, 2, 3, 4, 5]
console.log(result); // 15

이때, reduce 메서드의 두번째 인수로 전달하는 초기값은 첫번째 순회에 콜백함수의 첫번째 인수로 전달된다. 그래서 위 예제의 첫번째 순환값은 1이다.

reduce메서드의 두번째 인수로 전달되는 초기값은 옵션이지만 생략하지 않는것이 좋다. 이는 reduce메서드를 호출하는 배열이 빈배열일 경우에 에러발생이 일어나는것을 방지할수있다.

1
2
3
4
5
6
const arr = [];
const result = arr.reduce((acc, cur, index, arr) => acc + cur);
/* Uncaught TypeError: Reduce of empty array with no initial value */

console.log(arr);
console.log(result);

또한 객체의 특정 프로퍼티값을 합산하는 경우에는 반드시 초기값을 전달해야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const goods = [
{ id: 1, price: 300 },
{ id: 2, price: 500 },
{ id: 3, price: 700 },
];

/*
acc + cur
300 + 500
800.price? + 700
NaN
*/
const result = goods.reduce((acc, cur, index, arr) => acc.price + cur.price);

console.log(goods); // (3) [{…}, {…}, {…}]
console.log(result); // NaN

위의 식은 acc.price + cur.price 인데 아래의식은 acc + cur.price 인 이유는 위의 식은 값을 숫자값들을 더하기위해 어쩔수 없이 작성하였지만, 아래의 식은 초기값을 전달하면서 첫번째 순회시 acc의 값이 0으로 전달되기에 acc + cur.price 만으로 얻고싶은 값을 얻어낼수있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const goods = [
{ id: 1, price: 300 },
{ id: 2, price: 500 },
{ id: 3, price: 700 },
];

/*
acc + cur
0 + 300
300 + 500
800 + 700
1500
*/
const result = goods.reduce((acc, cur, index, arr) => acc + cur.price, 0);

console.log(goods); // (3) [{…}, {…}, {…}]
console.log(result); // 1500

reduce에 대한 더 많은 활용법과 예제는 이곳을 참고하자😁

Array.prototype.some((currentValue, index, arr), [thisArg])

  • 자신을 호출한 배열의 모든 요소를 순회한다.
  • 콜백함수로 수행해야할 처리를 전달받아 반복호출한다.
  • 콜백함수의 반환값이 단 한번이라도 참이면 true, 모두 거짓이면 false를 반환한다.
1
2
3
4
5
const arr = [1, 2, 3, 4, 5];
const result = arr.some((v, i, arr) => v > 4);

console.log(arr); // (5) [1, 2, 3, 4, 5]
console.log(result); // true

Array.prototype.some메서드는 메서드를 호출한 배열의 요소중 콜백함수에서 정의한 조건을 만족하는 요소가 단 한개라도 존재한다면 true를 반환한다.

단, 메서드를 호출한 배열이 빈배열일 경우는 false를 반환한다.

Array.prototype.every((currentValue, index, arr), [thisArg])

  • 자신을 호출한 배열의 모든 요소를 순회한다.
  • 콜백함수로 수행해야할 처리를 전달받아 반복호출한다.
  • 콜백함수의 반환값이 모두 참이면 true, 단 하나라도 거짓이면 false를 반환한다.
1
2
3
4
5
const arr = [1, 2, 3, 4, 5];
const result = arr.every((v, i, arr) => v > 4);

console.log(arr); // (5) [1, 2, 3, 4, 5]
console.log(result); // false

Array.prototype.every메서드는 메서드를 호출한 배열의 모든 요소들이 콜백함수에서 정의한 조건을 만족한다면 true를 반환한다.

Array.prototype.find((currentValue, index, arr), [thisArg])

  • 자신을 호출한 배열의 모든 요소를 순회한다.
  • 콜백함수로 수행해야할 처리를 전달받아 반복호출한다.
  • 콜백함수의 반환값이 true인 첫번째 요소를 반환한다.
  • true인 요소가 존재하지 않는경우 undefined를 반환한다.
1
2
3
4
5
6
7
8
9
10
11
const ranking = [
{ id: 1, color: 'green' },
{ id: 2, color: 'red' },
{ id: 3, color: 'blue' },
{ id: 4, color: 'yellow' },
{ id: 5, color: 'blue' },
];

const result = ranking.find((v, i, arr) => v.color === 'blue');

console.log(result); // {id: 3, color: "blue"}

Array.prototype.find메서드는 메서드를 호출한 배열의 요소들중 콜백함수로 정의한 조건에 만족하는 첫번째 요소를 반환한다.

Array.prototype.findIndex((currentValue, index, arr), [thisArg])

  • 자신을 호출한 배열의 모든 요소를 순회한다.
  • 콜백함수로 수행해야할 처리를 전달받아 반복호출한다.
  • 콜백함수의 반환값이 true인 첫번째 요소의 인덱스를 반환한다.
  • 반환값이 true인 요소가 없다면 -1을 반환한다.
1
2
3
4
5
6
7
8
9
10
11
const ranking = [
{ id: 1, color: 'green' },
{ id: 2, color: 'red' },
{ id: 3, color: 'blue' },
{ id: 4, color: 'yellow' },
{ id: 5, color: 'blue' },
];

const result = ranking.findIndex((v, i, arr) => v.color === 'blue');

console.log(result); // 2

Array.prototype.findIndex메서드는 메서드를 호출한 배열의 요소들중 콜백함수로 정의한 조건에 만족하는 첫번째 요소의 인덱스를 반환한다.

추가적인 배열 메서드와 관련해서는 이곳을 참고하길 바란다.

Author

YoungSang Lee

Posted on

2021-04-02

Updated on

2021-04-14

Licensed under

댓글