배열 함수 (forEach, map, reduce)

YunKuk Park·2021년 11월 10일
0

👣 하나씩 꾸준히

목록 보기
2/15
post-thumbnail

forEach() 함수


  • forEach() Interface 공식문서
    /**
    * 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;
}

map() 함수


  • map() Interface 공식문서
    /**
    * 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() 함수 [배열을 특정 자료형으로 변환 시 사용]


앞에서 작성한 코드로 얻은 결과값은 객체가 아닌 배열입니다.
만약 배열을 객체로 변환하고 싶다면 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()
    => 배열을 특정 자료형으로 변환하는 데 사용

참고

  • [서적] Do it! 리액트 프로그래밍 정석
profile
( • .̮ •)◞⸒⸒

0개의 댓글