12월 29일 복기

Ji Taek Lim·2020년 12월 29일
2

오늘은 언더바에 대해서 해보았다. 이것은 고차함수에 대해서 학습을 하는것이였는데 고차함수와 forEach 등에 대해서 학습했습니다.


// _.take는 배열의 처음 n개의 element를 담은 새로운 배열을 리턴합니다.
// n이 undefined이거나 음수인 경우, 빈 배열을 리턴합니다.
// n이 배열의 길이를 벗어날 경우, 전체 배열을 shallow copy한 새로운 배열을 리턴합니다.
_.take = function (arr, n) {
  let newArr = [];

  if(n > arr.length) {
    n= arr.length 
  }

  for (let i=0; i < n ; i++) {
    newArr.push(arr[i]);
  }

  return newArr;
};
// _.drop는 _.take와는 반대로, 처음 n개의 element를 제외한 새로운 배열을 리턴합니다.
// n이 undefined이거나 음수인 경우, 전체 배열을 shallow copy한 새로운 배열을 리턴합니다.
// n이 배열의 길이를 벗어날 경우, 빈 배열을 리턴합니다.
_.drop = function (arr, n) {
  let newArr = [];

  if(n === undefined || n <0) {
    n=0;
  }
  for(let i =n; i < arr.length; i++) {
    newArr.push(arr[i]);
  }
  return newArr;
};
// _.last는 배열의 마지막 n개의 element를 담은 새로운 배열을 리턴합니다.
// n이 undefined이거나 음수인 경우, 배열의 마지막 요소만을 담은 배열을 리턴합니다.
// n이 배열의 길이를 벗어날 경우, 전체 배열을 shallow copy한 새로운 배열을 리턴합니다.
// _.take와 _.drop 중 일부 또는 전부를 활용할 수 있습니다.
_.last = function (arr, n) {
  
  let newArr = [];

  if(n > arr.length) {
    n=arr.length;
  }
  if( n === undefined || n < 0) {
    n=1;
  }
  for (let i= arr.length-n ; i < arr.length; i++) {
    newArr.push(arr[i]);
  }
  return newArr;
};

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

// iteratee에는 테스트 케이스에 따라서 다양한 함수가 할당됩니다.
// Array.prototype.forEach 메소드를 사용할 때, 다양한 형태의 callback 함수를 사용할 수 있었던 걸 기억하시나요?
// 우리가 만드는 _.each 함수도 그렇게 잘 작동하게 하기 위한 방법을 고민해 봅시다.

/*
 * SpecRunner를 열고 each의 네 번째 테스트 케이스를 눌러 보시기 바랍니다.
 * 이 테스트 케이스의 collection은 letters이고,
 * iteratee는 익명함수 function(letter) { iterations.push(letter); }); 입니다.
 *
 *  const letters = ['a', 'b', 'c'];
 *  const iterations = [];
 *  _.each(letters, function(letter) {
 *   iterations.push(letter);
 *  });
 *  expect(iterations).to.eql(['a', 'b', 'c']);
 *
 * iteratee는 차례대로 데이터(element 또는 value), 접근자(index 또는 key), collection을 다룰 수 있어야 합니다.
 *  배열 arr을 입력받을 경우, iteratee(ele, idx, arr)
 *  객체 obj를 입력받을 경우, iteratee(val, key, obj)
 * 이처럼 collection의 모든 정보가 iteratee의 인자로 잘 전달되어야 모든 경우를 다룰 수 있습니다.
 * 실제로 전달되는 callback 함수는 collection의 모든 정보가 필요하지 않을 수도 있습니다.
 */

// _.each는 명시적으로 어떤 값을 리턴하지 않습니다.
_.each = function (collection, iteratee) {
  
  if(Array.isArray(collection)) {
    for (let i =0; i < collection.length ; i++) {
      iteratee(collection[i], i , collection); /// 배열 일때
    }
  }else { /// 객체 일때
    for (let key in collection) {
      iteratee(collection[key], key, collection);
    }
  } 
};
// _.indexOf는 target으로 전달되는 값이 arr의 요소인 경우, 배열에서의 위치(index)를 리턴합니다.
// 그렇지 않은 경우, -1을 리턴합니다.
// target이 중복해서 존재하는 경우, 가장 낮은 index를 리턴합니다.
_.indexOf = function (arr, target) {
  // 배열의 모든 요소에 접근하려면, 순회 알고리즘(iteration algorithm)을 구현해야 합니다.
  // 반복문을 사용하는 것이 가장 일반적이지만, 지금부터는 이미 구현한 _.each 함수를 활용하여야 합니다.
  // 아래 _.indexOf의 구현을 참고하시기 바랍니다.
  let result = -1;

  _.each(arr, function (item, index) { //// each 가 for 문이고  [2,4,6,4] result =1 target=4
    if (item === target && result === -1) {
      result = index;
    }
  });

  return result;
};
// _.filter는 test 함수를 통과하는 모든 요소를 담은 새로운 배열을 리턴합니다.
// test(element)의 결과(return 값)가 truthy일 경우, 통과입니다.
// test 함수는 각 요소에 반복 적용됩니다.
_.filter = function (arr, test) {
  // TODO: 여기에 코드를 작성합니다.
  // filter가 test 가 펑션인가?
  let newArr = [];

  _.each(arr, function(el){
    if(test(el)) {
      newArr.push(el)
    }    
  });
  return newArr;
};
// _.reject는 _.filter와 정반대로 test 함수를 통과하지 않는 모든 요소를 담은 새로운 배열을 리턴합니다.
_.reject = function (arr, test) {
  let newArr =[];

  _.filter(arr, function(el) {
    if(!(test(el)===true)) {
      newArr.push(el)
    }
  });
  return newArr;
};
// _.uniq는 주어진 배열의 요소가 중복되지 않도록 새로운 배열을 리턴합니다.
// 중복 여부의 판단은 엄격한 동치 연산(strict equality, ===)을 사용해야 합니다.
// 입력으로 전달되는 배열의 요소는 모두 primitive value라고 가정합니다.
_.uniq = function (arr) {
  let newArr =[];
  for(let i=0; i <arr.length; i++) {
    if(!(newArr.includes(arr[i]))) {
      newArr.push(arr[i])
    }
  }
  return newArr;
};
_.uniq = function (arr) {
  let newArr =[];

  _.each(arr, function(el) {

    if(!newArr.includes(el)) {
      newArr.push(el);
    }
  })
  return newArr;
};

_.uniq = function (arr) {
  let newArr =[];
  _.each(arr, function(el) {

    for(let i =0; i < newArr.length; i++) {
      if(el === newArr[i]) {
        return;
      }
    }
    newArr.push(el)
  });
  return newArr;
};
// _.map은 iteratee(반복되는 작업)를 배열의 각 요소에 적용(apply)한 결과를 담은 새로운 배열을 리턴합니다.
// 함수의 이름에서 드러나듯이 _.map은 배열의 각 요소를 다른 것(iteratee의 결과)으로 매핑(mapping)합니다.
_.map = function (arr, iteratee) {
  let newArr =[];

  _.each(arr, function(el) {
    newArr.push(iteratee(el));
  })
  return newArr;
};
// _.pluck은
//  1. 객체 또는 배열을 요소로 갖는 배열과 각 요소에서 찾고자 하는 key 또는 index를 입력받아
//  2. 각 요소의 해당 값 또는 요소만을 추출하여 새로운 배열에 저장하고,
//  3. 최종적으로 새로운 배열을 리턴합니다.
// 예를 들어, 각 개인의 정보를 담은 객체를 요소로 갖는 배열을 통해서, 모든 개인의 나이만으로 구성된 별도의 배열을 만들 수 있습니다.
// 최종적으로 리턴되는 새로운 배열의 길이는 입력으로 전달되는 배열의 길이와 같아야 합니다.
// 따라서 찾고자 하는 key 또는 index를 가지고 있지 않은 요소의 경우, 추출 결과는 undefined 입니다.

_.pluck = function (arr, keyOrIdx) {
  // _.pluck을 _.each를 사용해 구현하면 아래와 같습니다.
  let newArr = [];
  _.each(arr, function (item) {
    newArr.push(item[keyOrIdx]);
  });
  return newArr;
};


_.pluck = function (arr, keyOrIdx) {

  return _.map(arr, function(el) {
    return el[KeyOrIdx]
  });
};


/ 2번째 return--> el[KeyOrIdx] --> iterateee
/ 1번째는 newArr; 인자를 받아와서 새로 배열을 만들어 줘라

-map함수가 newArr을 포함하고 있기 때문에 따로 newArr을 써줄 필요는 없다 하짐나

return 되는 값이 el[KeyOrIdx]가 어렵다..

그리고 리턴을 2번해주게 되는데 왜 그럴까?

/ 2번째 return은--> el[KeyOrIdx] --> iterateee
/ 1번째는 newArr; 인자를 받아와서 새로 배열을 만들어 줘라


_.reduce는
 1. 배열을 순회하며 각 요소에 iteratee 함수를 적용하고,
 2. 그 결과값을 계속해서 누적(accumulate)합니다.
 3. 최종적으로 누적된 결과값을 리턴합니다.
예를 들어, 배열 [1, 2, 3, 4]를 전부 더해서 10이라는 하나의 값을 리턴합니다.
각 요소가 처리될 때마다 누적되는 값은 차례대로 1, 1+2, 1+2+3, 1+2+3+4 입니다.
이처럼 _.reduce는 배열이라는 다수의 정보가 하나의 값으로 축소(응축, 환원, reduction)되기 때문에 reduce라는 이름이 붙게 된 것입니다.

_.reduce는 위에서 구현한 많은 함수처럼, 입력으로 배열과 각 요소에 반복할 작업(iteratee)을 전달받습니다.
iteratee에 대해서 복습하면 아래와 같습니다. (일반적으로 객체를 reduce 하지는 않으므로, 배열 부분만 복습합니다.)
iteratee는 차례대로 데이터(element 또는 value), 접근자(index 또는 key), collection을 다룰 수 있어야 합니다.
 배열 arr을 입력받을 경우, iteratee(ele, idx, arr)

_.reduce는 반복해서 값을 누적하므로 이 누적되는 값을 관리해야 합니다.
따라서 _.reduce의 iteratee는 인자가 하나 더 추가되어 최종 형태는 아래와 같습니다.
 iteratee(acc, ele, idx, arr)
누적되는 값은 보통 tally, accumulator(앞글자만 따서 acc로 표기하기도 함)로 표현하거나
목적을 더 분명하게 하기 위해 sum(), prod(), total 등으로 표현하기도 합니다.
이때, acc는 '이전 요소까지'의 반복 작업의 결과로 누적된 값입니다.
ele는 잘 아시다시피 반복 작업을 수행할(아직 수행하지 않은) 현재의 요소입니다.

여기까지 내용을 정리하면 다음과 같습니다.
 _.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이 리턴됩니다.)

_.reduce = function (arr, iteratee, initVal) {
  let result = initVal; //undefined
  //arr = [1,2,3]
  _.each(arr, function(el, idx){
    if(result === undefined){
      result = el; //arr[0]
    }else{
      result = iteratee(result, el, idx, arr); //el=1, result=iteratee(0, 1, 0) //초기값 0
                                              //el=2, result = iteratee(iteratee(0,1,0), 2, 1)
    }
  });
  return result;
};
_.reduce = function (arr, iteratee, initVal) {
  let total; 
  _.each(arr, function(item, index) {
    if(total === undefined && initVal === undefined) {
      total = item;
    } else if (total === undefined && initVal !== undefined) {
      total = iteratee(initVal, item, index, arr);
    } else {
      total = iteratee(total, item, index, arr);
    }
  });
  return total; 
}

_.include


_.includes는 배열이 주어진 값을 포함하는지 확인합니다.
일치 여부의 판단은 엄격한 동치 연산(strict equality, ===)을 사용해야 합니다.
입력으로 전달되는 배열의 요소는 모두 primitive value라고 가정합니다.
_.includes = function (arr, target) {

  for(let i =0; i < arr.length ; i++) {
    if (arr[i] === target) {
      return true;
    }
  }
  return false;
};

_.includes = function (arr, target) {
  let result = false;
  _.each(arr,function(el) {
    if(el===target) {
      result= true;
    }
  })
  return result;
};

_.includes = function (arr, target) {
  let newArr = [];
  _.each(arr, function(item){
    if(item === target){
      newArr.push(item);
    }
  })
  if(newArr.length > 0){
    return true;
  }else{
    return false;
  }
}

reduce

mdn

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

_.reduce = function (arr, iteratee, initVal) {
  let result = initVal; //undefined
  //arr = [1,2,3]
  _.each(arr, function(el, idx){
    if(result === undefined){
      result = el; //arr[0]
    }else{
      result = iteratee(result, el, idx, arr); //el=1, result=iteratee(0, 1, 0) //초기값 0
                                              //el=2, result = iteratee(iteratee(0,1,0), 2, 1)
    }
  });
  return result;
};


_.includes = function (arr, target) {
//2. reduce
  //[1,2,3,4]  target = 3
  //1 !==3 -> result = false
  //2 !==3 -> result = false
  //3 ===3 -> result = true
  //4 !==3, result = true -> result =true
  return _.reduce(arr, function(result, el){
    if(result === true || el === target){
      return true;
    }
    else{
      return false;
    }
  }, false);
}

_.every


_.every = function (arr, iteratee) { /// result 값을 

  let result = true;

  for(let i=0; i < arr.length ; i++) { /// 안되는것을 찾아라
    if(iteratee(arr[i]) === false) {
      result = false;
    }
  }
  return result;
};

falsy


_.extend = function (...arg) {
  // TODO: 여기에 코드를 작성합니다.

  //1. target 객체를 정한다.
  //2. target 객체 외의 나머지 객체에 대해서
  //3. target 객체에 동일한 key가 있다면 overwirte;

  // let target = arg[0]; target의 객체가 새롭게 등장하면 안된다. 첫번째 객체를 리턴할 것이니까.

  1. forfor(let i=0; i<arg.length ; i++ ) {
    
    // for(let j =0; j < arg[i].length; j++)   ///// for in 을 써준다.

    for(let key in arg[i]) {
      arg[0][key] = arg[i][key];
    }    
  }
  
  2. _.each 함수
    _.each(arg, function(el) {
    for(let key in el) {
      arg[0][key] =el[key];
    }
  });

  // arg객체가 있고 key가 있고 value ; arg[0] = {key1: value1}
  // arg[1] = {key2; value2};
  // arg[2] = {key3: value3, key1: value4}
  /// 첫번째 arg[i]포문을 만들고 두번째 객체에 접근하는 for문을 만든다

  return arg[0];

};

key에 대한 값만 가져오는 함수


// array-like object
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

// array-like object with random key ordering
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']

// _.defaults는 _.extend와 비슷하게 동작하지만, 이미 존재하는 속성(key)을 덮어쓰지 않습니다.
_.defaults = function (...arg) {
  _.each(arg, function(el){
    for(let key in el){
      //조건: arg[0]에 존재하지 않는 속성인 경우
      if(!_.includes(Object.keys(arg[0]), key)){//1. arg[0]의 속성 배열   2. _.include(arr, target)함수
        arg[0][key] = el[key];
      }
    }
  });
  return arg[0];
};


js
// 아래 예제를 참고하시기 바랍니다.
//  const arr1 = ['a','b','c'];  arr[3] = undefined 접근 X  //maxLength = 4
//  const arr2 = [1,2];
//  const arr3 = [1,2,3,4]
//  const result = _.zip(arr1, arr2)
//result = [] -> result = [['a'], ['b'], ['c'], [undefined]]  //arr1  result.push()
//               result = [['a', 1], ['b', 2], ['c', undefined], [undefined,undefined]] //arr2  result[0].push
//               result = [['a', 1, 1], ['b', 2,2], ['c', undefined,3], [undefined,undefined,4]] //arr3 result[0].push
//  console.log(result); // --> [['a',1], ['b',2], ['c', undefined]
//                              [['a',1,1], ['b',2,2], ['c', undefined,3], [undefined, undefined, 4]]
_.zip = function (...arg) {
  let result = [];
  let maxLength = 0;
  for(let i = 0; i < arg.length; i++){
    if(maxLength < arg[i].length){
      maxLength = arg[i].length;
    }
  }
  //[] ->result.push()
  //[[1],[2],[3],[4]] -> result[0].push()
  for(let i = 0; i < arg.length; i++){
    if(i === 0){ //arg의 첫번째 요소 일때 = 반복문이 처음 반복되었을 때 -> result.push()
      //arg[i] = arg[0] = ['a','b','c']
      for(let j = 0; j < maxLength; j++){
        if(j < arg[i].length){ //배열 범위 안에 있는 경우
          result.push([arg[i][j]])
        }else{ //배열범위를 넘는 경우
          result.push([undefined]);
        }
      }
    }else{ //result[0].push()
      for(let j = 0; j < maxLength; j++){
        if(j < arg[i].length){
          result[i].push(arg[i][j]);
        }else{
          result[i].push(undefined);
        }
      }
    }
  }
  return result;
};

_.zip = function (...arg) {
  let result = [];
  let maxLength = 0;
  for(let i = 0; i < arg.length; i++){
    if(maxLength < arg[i].length){
      maxLength = arg[i].length;
    }
  }
  //[] ->result.push()
  //[[1],[2],[3],[4]] -> result[0].push()
  _.each(arg, function(ele, i){
    if(i === 0){ //arg의 첫번째 요소 일때 = 반복문이 처음 반복되었을 때 -> result.push()
      //arg[i] = arg[0] = ['a','b','c']
      for(let j = 0; j < maxLength; j++){
        if(j < ele.length){ //배열 범위 안에 있는 경우
          result.push([ele[j]])
        }else{ //배열범위를 넘는 경우
          result.push([undefined]);
        }
      }
    }else{ //result[0].push()
      for(let j = 0; j < maxLength; j++){
        if(j < ele.length){
          result[j].push(ele[j]);
        }else{
          result[j].push(undefined);
        }
      }
    }
  });
  return result;
};

profile
임지택입니다.

0개의 댓글