/**
* Performs the specified action for each element in an array.
* @param callbackfn A function that accepts up to **three arguments**. forEach **calls the callbackfn** function **one time for each element** in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
forEach(callbackfn: (value: T, index: number, array: readonly T[]) => void, thisArg?: any): void;
forEach() 함수의 유용성을 알아보기 위해 기존 자바스크립트로 문자열을 먼저 분리한 다음 비교 해보겠습니다.
여기서 분리하려는 문자열은 쿼리 스트링(Query String)이라 불리는 것으로 '웹 주소에 포함시키는 문자열'을 의미합니다.
// 예제에서 사용할 쿼리 스트링
const QUERY_STRING = 'banana=10&apple=20&orange=30'
위의 문자열을 & 문자를 기준으로 분리하여 객체에 담아 반환하는 parse() 함수를 선언 할 것입니다.
function parse(qs) {
const queryString = qs.substr(1);
const chunck = qs.split('&');
const result = {};
for(let i = 0; i < chunck.length; i++){
var parts = chunck[i].split('=');
var key = parts[0]; // key = 'banana'
var value = parts[1]; // value = '10'
result[key] = value; // result = { banana: '10'}
}
return result;
}
코드를 보면 for문을 이용하여 banana, apple, orange와 10,20,30을 분리합니다.
만약 10, 20, 30을 문자열이 아닌 숫자로 변환하려면 다음과 같이 코드를 작성하면 됩니다.
function parse(qs) {
const queryString = qs.substr(1);
const chunck = qs.split('&');
const result = {};
for(let i = 0; i < chunck.length; i++){
var parts = chunck[i].split('=');
var key = parts[0]; // key = 'banana'
var value = Number.isNaN(Number(parts[1])) ? parrts[1] : Number(parts[1]); // value = '10'
result[key] = value; // result = { banana: '10'}
}
return result;
}
위 코드는 forEach() 함수를 사용하면 반복문의 순번과 배열의 크기를 따로 변수에 저장하는 과정을 생략할 수 있습니다.
function parse(qs) {
const queryString = qs.substr(1);
const chuncks = qs.split('&');
let result = {};
chuncks.forEach((chunk) => {
const parts = chunk.split('=');
const key = parts[0];
const value = Number.isNaN(Number(parts[1])) ? parts[1] : Number(parts[1]);
result[key] = value;
});
return result;
}
여기서 키와 키값을 구조 분해 할당 방식으로 변환하면 더 간결하게 바꿀 수 있습니다.
function parse(qs) {
const queryString = qs.substr(1);
const chuncks = qs.split('&');
let result = {};
chuncks.forEach((chunck) => {
const [key, value] = chunck.split('='); // key = 'banana', value = '10'
result[key] = value;
});
return result;
}
/**
* Calls a defined **callback function on each element** of an array, and **returns an array** that contains the results.
* @param callbackfn A function that accepts up to **three arguments**. The map method calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
map<U>(callbackfn: (value: T, index: number, array: readonly T[]) => U, thisArg?: any): U[];
forEach 예제에서는 가변변수(let)를 사용했습니다. 만약 불변변수(const) 만을 사용하려면 map() 함수를 사용하면 됩니다.
map()함수는 각 배열 요소를 정의된 함수를 통해 변환한 결괏값들로 `새 배열을 반환`합니다.
⇒ 배열을 가공하여 새 배열을 만든 함수입니다.
function parseMap(qs) {
const queryString = qs.substr(1);
const chuncks = queryString.split('&');
const result = chuncks.map((chunck) => {
const [key, value] = chunck.split('=');
return { key, value };
});
return result;
}
result =
[
{ key: 'anana', value: '10' },
{ key: 'apple', value: '20' },
{ key: 'orange', value: '30' }
]
// console.log(key, value) 했을 때
$ node 02-9.js
banana 10
apple 20
orange 30
// console.log( { key, value } ) 했을 때
$ node 02-9.js
{ key: 'banana', value: '10' }
{ key: 'apple', value: '20' }
{ key: 'orange', value: '30' }
👨🏻💻 forEach() 함수를 이용했을 때와 같은 결과값을 출력하지만 map() 함수는 결괏값을 바로 반환하므로 가변 변수를 사용하지 않아도 된다.
앞에서 작성한 코드로 얻은 결과값은 객체가 아닌 배열입니다.
만약 배열을 객체로 변환하고 싶다면 reduce()
함수를 사용하면 됩니다.
function sum(number) {
return console.log(number.reduce(**(total, num) => total + num**, 0));
}
sum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
reduce() 함수를 보면 첫 번째 인자에는 변환함수 ((total, num) => total + num),
두 번째 인자에는 초깃값(0) 을 전달합니다. 그러면 reduce() 함수는 변환 함수의 첫 번째 인자를 이전 결괏값, 두 번째 인자를 배열(numbers) 의 각 요솟값(1,2,3...) 으로 생각하여 순환 할당 하면서 함수를 실행합니다.
초깃값으로 전달한 0은 이전 결괏값이 total 에 할당됩니다.
하지만, 실무에서 reduce() 함수는 보통 배열을 특정 자료형으로 변환하는 데 사용합니다.
즉, 배열의 총합을 구하는 예제는 '단순히 합을 구하는 예제'가 아니라 '배열을 숫자로 변환한 예제'로 이해 해야 합니다.
이 방법을 응용하면 배열을 얼마든지 다른 형태로 변환할 수 있습니다. 앞에서 map()함수를 사용한 예제에 reduce() 함수를 응용하여 배열을 객체로 변환해 보겠습니다. 배열을 객체로 변환하기 위해 reduce() 함수의 두 번째 매개변수인 초깃값을 원하는 자료형인 빈 객체 {} 를 입력했습니다.
function parseMapReduce(qs) {
const queryString = qs.substr(1);
const chuncks = qs.split('&');
const result = chuncks
.map((chunck) => {
const [key, value] = chunck.split('=');
return { key, value };
})
.reduce((result, item) => {
result[item.key] = item.value;
return result;
}, {});
return result;
}
map() 함수가 반환한 배열에는 { key: ..., value: ... }의 구조로 구성된 객체들이 들어있을 것입니다.
reduce() 함수는 key를 키값으로, value를 값으로 하는 하나의 개체로 반환합니다.
즉 아래와 같은 연산과정이 진행됩니다.
순환 01회차 result: {} item: {key: 'banana', value: '10'}
순환 02회차 result: {banana: '10'} item: {key: 'apple', value: '20'}
순환 03회차 result: {banana: '10', apple: '20'} item: {key: 'orange', value: '30'}
최종 반환값 result: {banana: '10', apple: '20', orange: '30'}
forEach()
=> Array에서의 for문
=> 왜 쓰니? 반복문의 순번과 배열의 크기를 따로 변수에 저장하는 과정 생략 & 가독성 증가map()
=> Array에서의 for문. 단, 결괏값을 바로 반환하므로 가변 변수(let 등)를 사용하지 않아도 된다.
=> 배열을 가공하여 새 배열을 만드는 함수reduce()
=> 배열을 특정 자료형으로 변환하는 데 사용