이 게시글을 통해 얕게나마 배열 API에 대해 정리해나가고자 합니다. 최근 자바스크립트로 기본적인 코딩 문제[1]를 풀다가 막히기도 했고, 실제 프로젝트에서 통신 중 JSONArray 등의 자료구조를 다룰 일이 많기에 더 늦기 전에 공부하고자 합니다. 특히 자바스크립트의 배열은 자바와는 다르게 굉장히 유연해 그 활용도가 더 높다고 할 수 있습니다[2][3].
또한 관련 글들을 보다 놀랐던 것은 reduce, map, filter와 같은 함수형 프로그래밍과 관련[4]된 최신 메서드들이 사실은 ES5부터 사용 가능한 메서드였다는 점이었습니다. 게시글의 틀린 부분 혹은 피드백이 있으시면 댓글로 남겨주세요. 목차는 다음과 같습니다.
1. 개요
2. filter
3. map
4. reduce
5. 기타
_____
개요
이 포스트에서는 배열 관련 메소드 중 filter, map, reduce에 대해 살펴보고자 한다. 이들은 모두 함수형 프로그래밍을 지원하는 순수 함수라는 공통점이 있다. 그렇다면 순수 함수란 무엇일까?
- 동일한 인수에 대해 항상 동일한 결과를 반환(인자 외에 다른 값 이용 불가)
- 부수 효과(side effects) 없음(외부 변수 변경 불가)
따라서 배열 메소드가 순수 함수(메소드...)가 되기 위해서는 원본 배열을 변화시키면 안 되며 항상 새로운 배열을 반환해야 한다. 다음은 이 메소드들에 대해 이해하기 쉬운 그림이다.
// map, filter, reduce example by steven luscher
// https://twitter.com/steveluscher/status/741089564329054208?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E741089564329054208%7Ctwgr%5E%7Ctwcon%5Es1_&ref_url=https%3A%2F%2Fwww.freecodecamp.org%2Fnews%2Fjavascript-map-reduce-and-filter-explained-with-examples%2F
['🌽', '🐂', '🐔'].map(cook); // ['🍿', '🍔', '🍳']
['🍿', '🍔', '🍳'].filter(isVegetarian); // ['🍿', '🍳']
['🍿', '🍳'].reduce(eat); // 💩
filter
const new_array = arr.filter(function callback(element, index, array) { // [5]
// Return true or false
}[, thisArg])
보통은 콜백으로 람다 형식을 사용하는 경우가 많고 인자로 element만 이용하곤 한다. MDN의 간단한 예를 인용한다.
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"]
map
const new_array = arr.map(function callback(element, index, array) {
// Return value for new_array
}[, thisArg])
MDN의 예를 살펴보자.
const squareNums = [1, 4, 9, 16];
const result = squareNums.map(x => x * 2);
console.log(result);
// expected output: Array [2, 8, 18, 32]
reduce
기타
이 외에도 다양한 배열 API와 함수형 프로그래밍에 대해 공부할 수 있는 게시글을 남긴다.
- LichKing] ES5 Array method
- yunzema] JavaScript Array 주요 메서드 정리(ES5 주의)
- armadillo] 지연 평가를 이용한 성능 개선
_____
1. 문제는 아래와 같다. 반복문을 이용해도 되지만, 내부 반복자를 사용하는 배열 API를 이용하는 것이 좋아 보인다.
// 아래와 같이 과일들이 있다. 각 과일을 3개씩 사려고 한다.
// (bought: 구매한 개수, total: 총금액, dcamt: 할인금액, payamt: 결제액)
const fruits = [
{ id: 1, name: '사과', price: 100, left: 10, dc: 10 },
{ id: 2, name: '감', price: 400, left: 1 },
{ id: 3, name: '배', price: 700, left: 2, dc: 20 }
];
// 재고가 부족할 경우 남은 재고 만큼 구매한다고 했을 때, 아래 결과와 같이 출력하는 코드를 작성하시오.
// (price는 가격, left는 판매가능-재고-개수, dc는 할인율%)
/*
[{ name: '사과', bought: 3, total: 300, dcamt: 30, payamt: 270 },
{ name: '감', bought: 1, total: 400, dcamt: 0, payamt: 400 },
{ name: '배', bought: 2, total: 1400, dcamt: 280, payamt: 1120 }]
*/
2. PoiemaWeb] 자바스크립트 배열은 배열이 아니다
3. 배열과 이터레이터의 차이점을 언급하는 Taehoon님의 좋은 영상이 있어 남긴다. 아래 코드를 보자.
const array = [1, 2, 3, 4, 5, 6, 7];
const iterator = (function() {
let num = 1;
return {
next: function() {
return (
num > 7 ?
{ done: true } :
{ done: false, value: num++ }
);
}
};
})();
// only "next()" is supported
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
보면 이터레이터는 num이 7 이하일 때 오브젝트에 숫자를 반환한다. 차이점은 이터레이터는 배열과 다르게 next() 함수 하나만 제공되므로 이전에 얻어온 값 바로 다음 값만 가져올 수 있다. 반면 배열의 경우 인덱스를 통한 랜덤 엑세스가 가능하다는 점이다. 즉 이터레이터는 배열의 부분 집합, 즉 배열의 기능은 이터레이터의 기능을 포함한다고 할 수 있다. 그럼에도 불구하고 이터레이터가 필요한 이유가 무엇일까? 첫째, 메모리 절감이다. 배열의 경우 모든 요소를 메모리 공간에 올려 놓아야 하지만 이터레이터의 경우 변수 하나만큼의 공간만 필요하다. 둘째, 외부 데이터를 이용하는 경우 유용하다. 한편 앞에서 얘기한 filter, map, reduce와 같은 함수형 프로그래밍에 사용되는 메소드 역시 배열에 붙어 있지만 사실은 이터레이터만 가지고도 구현가능하다(시간 되면 직접 구현해보기).
4. 해당 메소드들은 배열 관련 순수 함수들이다. 관련하여 상세한 내용은 아래 링크를 참고한다.
- maddevs] Functional Programming Principles in JavaScript
- James Jeffery] JavaScript: What Are Pure Functions And Why Use Them?
- zerocho] 함수형 프로그래밍
5. 상세한 API는 다음과 같다.
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.
*/
filter<S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[];
/**
* Returns the elements of an array that meet the condition specified in a callback function.
* @param predicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.
*/
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
6.
_____
참고
1. gitconnected] Manipulate JavaScript Arrays the Functional Way
2. freeCodeCamp] JavaScript Map, Reduce, and Filter - JS Array Functions Explained with Code Examples
3. poka-techblog] Simplify your JavaScript - Use .map(), .reduce(), and .filter()
4. MDN
5.
'공부 > JavaScript' 카테고리의 다른 글
JavaScript 참고자료 (0) | 2021.07.31 |
---|---|
클로저 (0) | 2021.07.24 |
Date validation (0) | 2021.04.10 |
JSON (0) | 2021.01.31 |
번역] 11가지 까다로운 자바스크립트 질문 (0) | 2021.01.23 |
댓글