BEB 07 2-3-sprint

Donghun Seol·2022년 9월 21일
0

BEB 07 2-3-sprint

이번 시간의 스프린트인 고차함수 코플릿과 커스텀 lodash 구현인 Underbar에 대해 포스팅한다.

고차함수 코플릿

compose 함수

function compose (func1, func2) {
  return val => func1(func2(val))
}

compose 함수에 reduce를 응용하면 pipe 패턴이 된다.

function composeReduced (...funcArgs) {
  return initVal => funcArgs.reduce((acc, cur) => cur(acc), initVal)
}

특정 배열에서 최솟값, 최대값을 찾을때 reduce 활용하면 편리하다. 리듀스의 콜백에서 리턴해주는 값이 다음 이터레이션의 acc가 되는걸 잘 기억하자.

2차원 배열을 flatten 할때도 reduce 활용한다.

function getLongestElement (arr) {
  // TODO: 여기에 코드를 작성합니다.
  return arr.reduce((acc, cur) => {
    if (cur.length > acc.length) {
      return cur 
    }
    return acc
  }, '')
}


function flatArr (arr) {
  return arr.reduce((acc, cur) => [...acc, ...cur], []);
}

Underbar

이번 스프린트의 하이라이트.
기본 제공되는 arr관련 메서드를 직접 구현해보는 스프린트.
재밌었다.

bareMinimum

_.slice = function (arr, start, end) {
  let _start = start || 0;
  let _end = end;
  
  if (start < 0) _start = Math.max(0, arr.length + start);
  if (end < 0) _end = Math.max(0, arr.length + end);
  
  if (_end === undefined || _end > arr.length) _end = arr.length;
  
  let result = [];
  
  for (let i = _start; i < _end; i++) {
    result.push(arr[i]);
  }
  
  return result;
}

_.take = function (arr, n) {
  let _n = n;
  if (_n === undefined || _n < 0) _n = 0;
  return _.slice(arr, 0, _n);
}

_.drop = function (arr, n) {
  let _n = n;
  if (_n === undefined || _n < 0) _n = 0;
  return _.slice(arr, _n, arr.length)
}

_.last = function (arr, n) {
  let _n = n;
  if (_n === undefined || _n < 0) _n = 1
  return _slice(arr, arr.length - _n)
}

_.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 = function (arr, target) {
  let result = -1
  _.each(arr, fuction (item, index) {
    if (item === target && result == -1) {
      result = index;
    }
  });
  return result;
}

// _.filter, _.reject는 생략

_uniq = function (arr) {
  let result = [];
  
  _.each(arr, item => {
    _indexof(arr, item) === -1 && result.push(item)
  });
  
  return result;
};

_map = function (arr, iteratee) => {
  let result = [];
  _.each(arr, item => {
    result.push(iteratee(item));
  });
};

// 끝판대장 reduce 구현하기
// 안보고 다시 구현해보자. FAIL.. 😞...
// 여러 번 보고 안보고 구현할 수 있도록 외워보자 🤔🤔🤔
_reduce = function (arr, iteratee, initVal) => {
  let acc = initVal;
  _.each(arr, function (item, idx, src) {
    if (initVal === undefined && idx === 0) {
      acc = item;
    } else {
      acc = iteratee(acc, item, idx, src);
    }
  });
  
  return result;
}
         

advance 구현 하기

다 풀었는데, 마지막 sort를 구현 못해서 레퍼런스를 봄.. 😞
sort의 콜백은 반환값이 1, 0, -1 중 하나가 되어야 한다

'use strict'

// _.once는 callback 함수를 한 번만 호출하는 '함수'를 리턴합니다.
// _.once가 리턴하는 함수를 여러 번 호출해도 callback 함수는 한 번 이상 호출되지 않습니다.
_.once = function (func) {
  let isCalled = false;
  let result = '';
  return function (...args) {
    if (!isCalled) {
      result = func(...args);
      isCalled = !isCalled;
    }
    return result;
  };
};


_.delay = function (func, wait, ...args) {
  return setTimeout(func, wait, ...args);
 
_.includes = function (arr, target) {
  try {
    _.each(arr, item => {
      if (item === target) {
        throw new Error('found!');
      };
    });
  } catch {
    return true;
  }
  return false;
 
};


_.every = function (arr, iteratee) {
  if (arr.length === 0) return true;
  if (iteratee === undefined) {
    return _.reduce(arr, (acc, cur) => !!cur && acc, true);
  }
  return _.reduce(arr, (acc, cur) => !!iteratee(cur) && acc, true);
 
};


_.some = function (arr, iteratee) {
  if (arr.length === 0) return false;
  if (iteratee === undefined) {
    return _.reduce(arr, (acc, cur) => !!cur || acc, false);
  }
  return _.reduce(arr, (acc, cur) => !!iteratee(cur) || acc, false);
 
};

_.extend = function (obj, ...args) {
  _.each(args, elem => {
    for (let key in elem) {
      obj[key] = elem[key];
    }
  });
  return obj;
  
};

// _.defaults는 _.extend와 비슷하게 동작하지만, 이미 존재하는 속성(key)을 덮어쓰지 않습니다.
_.defaults = function (obj, ...args) {
  _.each(args, elem => {
    for (let key in elem) {
      if (!obj.hasOwnProperty(key)) {
        obj[key] = elem[key];
      }
    }
  });
  return obj;
 
};

// _.zip은 여러 개의 배열을 입력받아, 같은 index의 요소들을 묶어 배열로 만듭니다.
// 각 index 마다 하나의 배열을 만들고, 최종적으로 이 배열들을 요소로 갖는 배열을 리턴합니다.
// _.zip의 입력으로 전달되는 배열이 수는 정해져 있지 않고, 각 배열의 길이는 다를 수 있습니다.
// 최종적으로 리턴되는 배열의 각 요소의 길이는 입력으로 전달되는 배열 중 가장 '긴' 배열의 길이로 통일됩니다.
// 특정 index에 요소가 없는 경우, undefined를 사용합니다.
// 반복문(for, while)을 사용할 수 있습니다.
// _.each, _.reduce, _.pluck 중 하나 이상을 반드시 사용하여야 합니다.

// 아래 예제를 참고하시기 바랍니다.
//  const arr1 = ['a','b','c'];
//  const arr2 = [1,2];
//  const result = _.zip(arr1, arr2)
//  console.log(result); // --> [['a',1], ['b',2], ['c', undefined]]
_.zip = function (...args) {
  const maxLength = _.reduce(args, (acc, cur) => {
    if (cur.length > acc) {
      return cur.length;
    }
    return acc;
  }, 0);

  return _.reduce(args, (acc, cur) => {
    // console.log({ acc, cur, idx, maxLength });
    for (let i = 0; i < maxLength; i++) {
      if (acc[i] === undefined) {
        acc[i] = [];
      }
      acc[i].push(cur[i]);
    }
    return acc;
  }, []);
  // TODO: 여기에 코드를 작성합니다.
};

// _.zipStrict은 _.zip과 비슷하게 동작하지만,
// 최종적으로 리턴되는 배열의 각 요소의 길이는 입력으로 전달되는 배열 중 가장 '짧은' 배열의 길이로 통일됩니다.
// 그 외 조건은 앞서 _.zip과 동일합니다.
_.zipStrict = function (...args) {
  const minLength = _.reduce(args, (acc, cur) => {
    if (cur.length < acc) {
      return cur.length;
    }
    return acc;
  }, Infinity);
  return _.reduce(args, (acc, cur) => {
    // console.log({ acc, cur, idx, maxLength });
    for (let i = 0; i < minLength; i++) {
      if (acc[i] === undefined) {
        acc[i] = [];
      }
      acc[i].push(cur[i]);
    }
    return acc;
  }, []);
  // TODO: 여기에 코드를 작성합니다.
};

// _.intersection은 여러 개의 배열을 입력받아, 교집합 배열을 리턴합니다.
// 교집합 배열은 모든 배열에 공통으로 등장하는 요소들만을 요소로 갖는 배열입니다.
// 교집합 배열의 요소들은 첫 번째 입력인 배열을 기준으로 합니다.
// 교집합이 없는 경우 빈 배열을 리턴합니다.
// 아래 예제를 참고하시기 바랍니다.

_.intersection = function (...args) {
  return _.reduce(args, (acc, cur) => {
    return _.filter(acc, elem => _.indexOf(cur, elem) !== -1);
  });
};

// _.difference는 여러 개의 배열을 입력받아, 차집합 배열을 리턴합니다.

_.difference = function (...args) {
  return _.reduce(args, (acc, cur) => {
    return _.filter(acc, elem => _.indexOf(cur, elem) === -1);
  });
};

// _.sortBy는 배열의 각 요소에 함수 transform을 적용하여 얻은 결과를 기준으로 정렬합니다.
// transform이 전달되지 않은 경우, 배열의 요소 값 자체에 대한 비교 연산자의 결과를 따릅니다.
// 예를 들어, number 타입간 비교는 대소 비교이고 string 타입간 비교는 사전식(lexical) 비교입니다.
// 세 번째 인자인 order는 정렬의 방향을 나타냅니다. 생략되거나 1을 입력받은 경우 오름차순, -1을 입력받은 경우 내림차순으로 정렬합니다.
// 아래 예제를 참고하시기 바랍니다.
//  const people = [
//    { id: 1, age: 27 },
//    { id: 2, age: 24 },
//    { id: 3, age: 26 },
//  ];
//  function byAge(obj) {
//    return obj.age;
//  };
//  const result = _.sortBy(people, byAge);
//  console.log(result); // --> [{ id: 2, age: 24 }, { id: 3, age: 26 }, { id: 1, age: 27 }]

// 한편, 'undefined'는 비교 연산은 가능하지만 사실 비교가 불가능한(비교의 의미가 없는) 데이터입니다.
// 아래 예제를 참고하시기 바랍니다.
//  console.log(undefined > 0); // --> false
//  console.log(undefined < 0); // --> false
//  console.log(undefined == 0); // --> false
//  console.log(undefined === 0); // --> false
//  console.log(undefined > 'hello'); // --> false
//  console.log(undefined < 'hello'); // --> false
// 이러한 이유로 정렬하려는 데이터들 중 'undefined'가 있는 경우,
//  1) 'undefined' 값을 제외(filter)하고 비교하거나
//  2) 'undefined' 값을 어떤 다른 값으로 간주하여 비교해야 합니다.
// 이번 스프린트에서는 2)번의 방식을 적용하였습니다.
// 마지막 테스트 케이스의 transform 함수(byHeightAsc)를 확인하시기 바랍니다.

// 힌트
//  1. Array.prototype.sort를 사용할 수 있습니다.
//    참고 문서: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
//    예제를 통해 내장 메소드 sort의 특성을 꼭 확인하시기 바랍니다.
//  2. _.identity를 사용할 수 있습니다.

// 이번 스프린트는 정렬 자체를 다루지 않으니 스프린트 이후에 스스로 학습하시기 바랍니다.
//  학습 우선순위: bubble sort, insertion sort, quick sort, merge sort, radix sort
_.sortBy = function (arr, transform, order) {
  order = order || 1;
  transform = transform || _.defaults;
  const arrCloned = [...arr];
  return arrCloned.sort((a, b) => {
    if (transform(a) < transform(b)) {
      return -1 * order;
    }
    return order;
  });
};

// _.shuffle은 배열 요소의 순서가 랜덤하게 변경된 새로운 배열을 리턴합니다.
// 다양한 상황(예. 비디오 또는 음악 재생의 순서를 섞을 때)에서 유용하게 쓰일 수 있습니다.
// _.shuffle의 동작을 이해하는 것이 목적이므로, 구현할 필요는 없습니다.
// 아래에 이미 구현된 코드를 이해하시고, 직접 테스트해 보시기 바랍니다.

// 직접 도전을 하고 싶은 경우, 아래 사이트에서 테스트 해볼 수 있습니다.
//  https://bost.ocks.org/mike/shuffle/compare.html
// 단, 해당 사이트의 shuffle 함수는 입력으로 전달되는 array의 요소들의 위치를 '직접' 변경해야 합니다.
_.shuffle = function (arr) {
  let arrCloned = arr.slice();
  for (let fromIdx = 0; fromIdx < arr.length; fromIdx++) {
    const toIdx = Math.floor(Math.random() * arr.length);
    // 아래 코드는 두 변수의 값을 교환합니다.
    let temp = arrCloned[fromIdx];
    arrCloned[fromIdx] = arrCloned[toIdx];
    arrCloned[toIdx] = temp;
  }
  return arrCloned;
};

lodash tutorial 사용법 익히기

lodash tutorial
beginner's guide to lodash

profile
I'm going from failure to failure without losing enthusiasm

0개의 댓글