코드스테이츠 7주차 / Underbar

support·2021년 12월 4일
0
post-thumbnail

내장 메소드 라이브러리 구현

예전에는 배열 메소드가 브라우저에서 지원되지 않았다고 한다
이때 배열이나 객체를 다루기 위한 라이브러리 도구 모음집을 만들었는데
이번 스프린트에서는 배열, 객체를 다루는 라이브러리 Underbar를 구현하며 자바스크립트 내장 메소드가 어떻게 callback 함수를 활용하는지 원리를 알 수 있었다

each

_.each
// _.each는 collection의 각 데이터에 반복적인 작업을 수행합니다.
//  1. collection(배열 혹은 객체)과 함수 iteratee(반복되는 작업)를 인자로 전달받아 
//    (iteratee는 함수의 인자로 전달되는 함수이므로 callback 함수)
//  2. collection의 데이터(element 또는 property)를 순회하면서
//  3. iteratee에 각 데이터를 인자로 전달하여 실행합니다.
// _.each는 명시적으로 어떤 값을 리턴하지 않습니다.

 * iteratee는 차례대로 데이터(element 또는 value), 
   접근자(index 또는 key), collection을 다룰 수 있어야 합니다.
 *  배열 arr을 입력받을 경우, iteratee(ele, idx, arr)
 *  객체 obj를 입력받을 경우, iteratee(val, key, obj)
 

each 메소드는 각 데이터에 대한 반복적인 작업을 한다
for문과 while과 같은 작업을 한다고 생각하면 된다

collection에 배열 혹은 객체가 들어올 수 있고
배열과 객체가 들어왔을 경우에 받는 데이터가 다르기 때문에 조건을 다르게 줘야한다
foreach() 를 생각해보면 더 쉽게 문제를 풀 수 있다

_.each = function (collection, iteratee) {
  // 배열일경우
  if(Array.isArray(collection)){
    for(let i = 0; i < collection.length; i++){
      iteratee(collection[i], i, collection)
    }   
  // 객체일경우
  }else if(typeof(collection) === 'object'){
    for(let i in collection){
      iteratee(collection[i], i, collection)
    }
  }
};
// 배열과 객체의 타입은 전부 다 object이므로 else가 아닌
// else if로 배열일때 위에서 걸러지게 만들어 줘야한다

reduce

제일 어려웠던 reduce 구현

// _.reduce는
//  1. 배열을 순회하며 각 요소에 iteratee 함수를 적용하고,
//  2. 그 결과값을 계속해서 누적(accumulate)합니다.
//  3. 최종적으로 누적된 결과값을 리턴합니다.

// iteratee는 차례대로 데이터(element 또는 value), 접근자(index 또는 key), collection을 다룰 수 있어야 합니다.
//  배열 arr을 입력받을 경우, iteratee(ele, idx, arr)


// _.reduce는 반복해서 값을 누적하므로 이 누적되는 값을 관리해야 합니다.
// 따라서 _.reduce의 iteratee는 인자가 하나 더 추가되어 최종 형태는 아래와 같습니다.
//  iteratee(acc, ele, idx, arr)


// 여기까지 내용을 정리하면 다음과 같습니다.
//  _.reduce(arr, iteratee)
//  iteratee(acc, ele, idx, arr)
// 그런데 사실 누적값에 대해서 빠뜨린 게 하나 있습니다.
// 바로 '누적값은 어디서부터 시작하는가'라는 의문에 대한 대답을 하지 않았습니다.
// 이를 해결하는 방법은 초기 값을 직접 설정하거나 자동으로 설정하는 것입니다.
// _.reduce는 세 번째 인자로 초기 값을 전달받을 수 있습니다.
// 이 세 번째 인자로 초기 값이 전달되는 경우, 그 값을 누적값의 기초(acc)로 하여 배열의 '첫 번째' 요소부터 반복 작업이 수행됩니다.
// 반면 초기 값이 전달되지 않은 경우, 배열의 첫 번째 요소를 누적값의 출발로 하여 배열의 '두 번째' 요소부터 반복 작업이 수행됩니다.

// 따라서 최종적인 형태는 아래와 같습니다.
//  _.reduce(arr, iteratee, initVal)
//  iteratee(acc, ele, idx, arr)

참고하면 좋은 예제

//  const numbers = [1,2,3];
//  const sum = _.reduce(numbers, function(total, number){
//    return total + number;
//  }); // 초기 값이 주어지지 않았으므로, 초기 값은 배열의 첫 요소인 1입니다. 
       // 두 번째 요소부터 반복 작업이 시작됩니다.
//      // 1 + 2 = 3; (첫 작업의 결과가 누적되어 다음 작업으로 전달됩니다.)
//      // 3 + 3 = 6; (마지막 작업이므로 최종적으로 6이 리턴됩니다.)
//
//  const identity = _.reduce([3, 5], function(total, number){
//    return total + number * number;
//  }, 2); // 초기 값이 2로 주어졌습니다. 첫 번째 요소부터 반복 작업이 시작됩니다.
//         // 2 + 3 * 3 = 11; (첫 작업의 결과가 누적되어 다음 작업으로 전달됩니다.)
//         // 11 + 5 * 5 = 36; (마지막 작업이므로 최종적으로 36이 리턴됩니다.)

each 메소드 활용 풀이


_.reduce = function (arr, iteratee, initVal) {
  let accumulator = initVal;

  _.each(arr, function (item, idx, src) {
    // initVal이 없고 인덱스가 0번째이면 
    // 초기값을 넣어준다
    if (initVal === undefined && idx === 0) {
      accumulator = item;
    } else {
      accumulator = iteratee(accumulator, item, idx, src);
    }
  });

  return accumulator;
};

메소드 활용하지 않고 풀어본 풀이

_.reduce = function (arr, iteratee, initVal) {

  let accumulate = initVal;
  if(accumulate === undefined){
    // accumulator에 초기값이 지정되지 않는 경우 
    // 초기값은 collection의 0번째 인덱스 값
    accumulate = arr[0];
    // collection[0]은 이미 초기값으로 사용했으니 
    // for문은 i를 1부터 시작하도록 설정. 
  for(let i = 1; i < arr.length; i++){
    accumulate = iteratee(accumulate, arr[i]);
  }}else{
    // accumulator에 초기값이 지정되는 경우
    for(let j = 0; j < arr.length; j++){
      accumulate = iteratee(accumulate, arr[j])
    }
  }
  // accumulator에 누적된 값을 출력 
  return accumulate
};

1) _.each 함수를 사용해야 합니다
2) iteratee는 차례대로 누적값, 요소, 인덱스, 전체 배열을 입력받아야 합니다
메소드를 활용하지 않고 풀면 두가지 테스트 케이스는 통과되지 않는다

0개의 댓글