Underbar

이제 3주차 막바지이다. 🤔
앞의 koanstestbuilder가 쉬운 것은 아니지만....
Underbar와는 차이가 꽤 나므로, 여긴 집중해서 풀어봐야겠다:)


PART. 1

_.iterator

주석의 내용은 아래를 풀다보면 알게되지 않을까 싶다.
말그대로 받은 값을 그대로 리턴한다.

// 만약 함수에 iterator가 필요하고,뭐라도 넘겨줘야 하는 상황에는 이 함수가 유용할 것이다.
  _.identity = (val) => {
    return val;
  };

_.first

적응을 위해 초기 답안이 주어져있다.
일관성을 위해 Arrow function으로 바꾸었다.

// 배열의 처음 n개의 element를 담은 배열을 리턴하세요.
// 만일 n이 undefined일 경우, 단순히 첫번째 element를 리턴하세요.
  _.first = (array, n) => {
    return n === undefined ? array[0] : array.slice(0, n);
  };

_.last

_.first와 비슷하게 작성하였다.
뒤 코드가 좀 억지스럽지만 후에 문제가 있을 경우 변경해야겠다.

// first와 비슷하게, 마지막 n개의 element를 담은 배열을 리턴하세요.
// 만일 n이 undefined일 경우, 단순히 마지막 element를 리턴하세요.
  _.last = (array, n) => {
    return n === undefined ? array.unshift() : array.reverse().slice(0, n).reverse();
  };

_.each

velog에 적어야겠다고 결심한 순간은 _.each부터이다.
배열과 배열이 아닐 때를 분리하여 반복문을 작성하면 된다.

// iterator(value, key, collection)를 collection의 각각의 key-value pair에 대해 호출하세요.
// iterator는 함수로 전달되며, 쉽게 말해 반복해서 실행하는 함수입니다.
// collection으로 배열과 객체를 다 받을 수 있어야 합니다.
_.each = (collection, iterator) => {
    if(Array.isArray(collection)) {
      for(let i = 0; i < collection.length; i += 1) {
        iterator(collection[i], i, collection);
      }
    } else {
      for(let i in collection) {
        iterator(collection[i], i, collection);
      }
    }
  };

_.indexOf

앞의 _.each를 활용해야한다.
만약 일치하는 index를 발견하면 더 이상 진행하면 안되므로 resultIndex === -1 조건을 추가하였다.
break는 금지되어있다...

// target으로 전달되는 값이 array에서 발견되면, 그 index를 리턴하세요.
// 만일 array에서 발견할 수 없다면 -1을 리턴하세요.
  _.indexOf = (array, target) => {
    let resultIndex = -1;
    _.each(array, (item, index) => {
      if (item === target && resultIndex === -1) {
        resultIndex = index;
      }
    }); 
    return resultIndex;
    /* TEST with map
    const res = array.map((val, i) => val === target ? i : undefined);
    return res === undefined ? -1 : res;
    */
  };

_.filter

두번째 인자가 test라고 되어있어서 헷갈려서 f로 변경하였다.
collection을 돌면서 true일 경우 result에 추가한다.

// 테스트 함수를 통과하는 모든 element를 담은 배열을 리턴하세요.
_.filter = (collection, f) => {
    let result = []
    _.each(collection, (num) => {
      f(num) && result.push(num);
    })
    return result;
 };

_.reject

_.filter와 정반대이다.
_.each를 사용해도 별 차이없다. 잘못푼것일수도...
문제가 생기면 수정해야겠다!

// 테스트 함수를 통과하지 않는 모든 element를 담은 배열을 리턴하세요.
_.reject = function(collection, f) {
    // TIP: _filter()를 사용하여, 변경하세요..!
    let res = []
    _.filter(collection, (num) => {
      !f(num) && res.push(num);
    })
    return res;
 };

_.uniq

값이 없으면 빈 배열에 push하여 값을 구한다.
_.eachforeach 느낌으로 사용하면 된다.

// element가 중복되지 않는 새로운 array를 만드세요.
// ex: [1,2,4,4,3,3] -> [1,2,4,3] 
_.uniq = (array) => {
    let res = [];
    _.each(array, (item) => {
      !res.includes(item) && res.push(item);
    });
    return res;
 };

_.map

iterator를 함수라 생각하고 풀면 간단하다.
배열을 돌면서 함수의 결과값을 넣어준다.

// iterator를 각 element에 적용한 결과를 담은 새로운 array를 리턴하세요.
_.map = (collection, iterator) => {
    let res = [];
    _.each(collection, (item) => {
      res.push(iterator(item))
    })
    return res;
 };

_.pluck

_.map적응을 위하여 주어지는 코드이다.
객체로 구성된 배열에서 키로 접근하여 값을 배열로 리턴한다.

// 객체의 배열을 가져와서, 그 안에 있는 특정 속성의 값의 배열을 리턴하세요.
/* TEST EX
  var people = [
  { name: 'moe', age: 30 },
  { name: 'curly', age: 50 }
  ];
  expect(_.pluck(people, 'name')).to.eql(['moe', 'curly']);
*/
_.pluck = (collection, key) => {
    return _.map(collection, (item) => {
      return item[key];
    });
 };

_.reduce

Part.1 마지막 문제다운 문제다.
collectioniteratoriter로 가져오고,
조건에 맞게 accumulatorundefined이면 배열의 첫 요소를 전달하고, iter를 진행한다.
undefined가 아니라면 그대로 accmulatoriterator를 집어넣고,
undefined라면 한번 진행한 iter에서 돌게되어 두번째 요소부터 반복한다.

// 각 항목에 대해 iterator(accumulator, item)를 반복적으로 호출하여, 
// Reduces an array to a single value by repetitively calling
// 하나의 값으로 줄입니다. accumulator는 누적값으로, 이전 iterator 호출의 반환값이어야 합니다.
//
// reduce에 대한 세번째 argument로 초기값을 전달 할 수 있습니다.
// 만일 초기값이 전달되지 않으면, 첫번재 element가 accumulator로 사용되며, iterator에 전달되지 않습니다.
// 즉, 초기값이 전달되지 않은 경우, iterator는 두번째 element로부터 시작합니다.
_.reduce = (collection, iterator, accumulator) => {
    const iter = collection[Symbol.iterator]();
    if(accumulator === undefined){
      accumulator = collection[0];
      iter.next();
    }
    for(const i of iter) {
      accumulator = iterator(accumulator, i);
    }
    return accumulator;
};