[Underscore.js] 03. Collection Function

Collection Functions

1. each _.each(list, iteratee, [context])

  • list의 각 원소들을 반복하여 iteratee함수에 대입하여 그 결과값을 반환해주는 함수이다.
  • list: collection으로써, 배열이나 객체가 될 수 있다.
  • iteratee: iteratee 함수는 (element, index, list)를 인자로 받지만, list가 object로 주어질 경우에는 (value, key, list)를 받게된다.
  • 만약 context가 주어진다면 iteratee 함수는 context 객체에 binding 된다.(이는 iteratee 함수에 있는 this로 바인딩 되는 것)

  • each 함수 예재
    _.each([1, 2, 3], function(element, index) {
      var result = 'Element: ' + element + ', Index: ' + index;
      console.log(result);
      });
    // Element: 1, Index: 0
    // Element: 2, Index: 1
    // Element: 3, Index: 3
    _.each({One: 1, Two: 2, Three: 3}, function(value, key) {
      var result = The value is + value +  where the key is  + key;
      console.log(result);
      });
    // The value is 1 where the key is One
    // The value is 2 where the key is Two
    // The value is 3 where the key is Three
    
  • each 함수 구현
    _.each = function(collection, iterator) {
      if (Array.isArray(collection)) {
     for (var i = 0; i < collection.length; i++) {
       iterator(collection[i], i, collection);
     }
      } else {
     for (var i in collection) {
       iterator(collection[i], i, collection);
     }
      }
    };
    // each 함수는 collection이 array인지 object인지에 따라 iteratee 함수에 다른 argument를 대입시켜야 하기 때문에
    // each 함수를 직접 구현할 때에도 collection이 array인지 object인지 구분해서 인자를 대입해줘야 한다.
    


    2. filter _.filter(list, predicate, [context])

  • list의 원소 중에서 predicate함수의 truth 테스트를 통과한 원소들만 반환해주는 함수이다
  • predicate: list의 각 element에 대하여 그 결과값이 true인지, false인지 확인하는 일종의 test함수이다.
  • 만약 context가 주어진다면 iteratee 함수는 context 객체에 binding 된다.(이는 iteratee 함수에 있는 this로 바인딩 되는 것)

  • filter 함수 예재
    _.filter({a:1, b:2, c:3, d:4}, function(num){ return num % 2 == 0; });
     => [2, 4]
    _.filter([0,1,2,3], function(item) { return this[item] % 5 == 0 ;}, [5, 7, 10, 15])
     => [0, 2, 3]
    
  • filter 함수 구현
    _.filter = function(collection, test) {
      var result = [];
      _.each(collection, function(element) {
     if(test(element)) {
       result.push(element);
     }
      });
      return result;
    };
    // 최종 결과값은 array로 반환되기 때문에 새로운 result 값으로 empty array를 선언해준 뒤,
    // each함수로 collection의 원소가 test함수에 대입되어 true를 반환하는 경우에만 새로운 array에 push해준다.
    


    3. reject _.reject(list, predicate, [context])

  • filter 함수를 반대로 생각하면 된다. 즉, predicate 함수에서 false를 반환해주는 원소들만 반환해주는 함수이다.

  • filter 함수 예재
    _.reject({a:1, b:2, c:3, d:4}, function(num){ return num % 2 == 0; });
     => [1, 3]
    _.reject([0,1,2,3], function(item) { return this[item] % 5 == 0 ;}, [5, 7, 10, 15])
     => [1]
    
  • filter 함수 구현
    _.reject = function(collection, test) {
      var result = [];
      _.each(collection, function(element) {
     if(!test(element)) {
       result.push(element);
     }
      });
      return result;
    };
    // test함수에서 false를 반환하는 경우에만 새로운 array에 push해주는 것으로 filter 구현 함수를 수정하면 된다.
    


    4. map _.map(list, iteratee, [context])

  • list의 모든 원소들을 iteratee함수에 대입하여 반환된 값들로 구성된 새로운 array를 만드는 함수이다.
  • iteratee 함수는 element(or value), index(or key), list의 3가지 인자를 받는다.

  • map 함수 예재
    _.map({one: 1, two: 2, three: 3}, function(num, key){ return num * 3; });
     => [3, 6, 9]
    _.map([0,1,2,3], function(item) { return this[item] * 3 ;}, [5, 7, 10, 15]);
     => [15, 21, 30, 45]
    
  • map 함수 구현
    _.map = function(collection, iterator) {
      var result = [];
      _.each(collection, function(item, index, collection) {
     result.push(iterator(item));
      })
      return result;
    };
    // 주어진 collection의 모든 원소들을 iterator에 대입하여 반환된 값을 새로 선언한 result array에 push해 줄 수 있다.
    // map 함수는 새로운 array를 만들고, 새로운 array의 원소는 기존의 list에서 element(or value)인 값들 이기에 iterator가 첫 번째 item 인자만 받아도 된다.
    


    5. pluck _.pluck(list, propertyName)

  • list에서 propertyName이 가지고 있는 모든 value 값을 반환해주는 함수이다.

  • pluck 함수 예재
    var stooges = [{name: 'moe', age: 40}, {name: 'larry', age: 50}, {name: 'curly', age: 60}];
    _.pluck(stooges, 'name');
    => ["moe", "larry", "curly"]
    var stooges = {'first': {name: 'moe', age: 40}, 'second': {name: 'larry', age: 50}, 'third': {name: 'curly', age: 60}};
    _.pluck(stooges, 'name');
    => ["moe", "larry", "curly"]
    
  • pluck 함수 구현
    _.pluck = function(collection, key) {
      return _.map(collection, function(item) {
     return item[key];
      });
    };
    // 주어진 collection의 각 원소들에 대하여 찾고자하는 key값에 대응하는 value를 찾기 위하여 item[key]를 리턴해준다.
    // underscore 페이지의 설명에 따르면, pluck함수는 map함수의 흔한 예시라고 한다.
    


    6. reduce _.reduce(list, iteratee, [memo], [context])

  • list의 각 원소들을 iteratee 함수에 대입하여 하나의 값을 리턴해주는 함수이다.
  • memo: iteratee 함수의 초기 값을 의미하는 인자로 생략 가능하다.
  • iteratee 함수는 memo, element(or value), index(or key), list를 인자로 받게 된다.
  • memo가 생략된 경우에는 list의 첫번째 원소가 memo로 사용되지만, memo가 입력된 경우에는 입력된 memo를 사용하게 된다.

  • reduce 함수 예재
    var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
    => 6
    var sum = _.reduce([1, 2, 3, 4], function(memo, num){ return memo + num; });
    => 10
    
  • reduce 함수 구현
    _.reduce = function(collection, iterator, accumulator) {
      if (accumulator === undefined) {
     accumulator = collection[0];
     for (var i = 1; i < collection.length; i++) {
       accumulator = iterator(accumulator, collection[i]);
     }
      } else {
     for (var i = 0; i < collection.length; i++) {
       accumulator = iterator(accumulator, collection[i]);
     }
      }
      return accumulator;
    };
    // accumulator 즉, 초기값(memo)이 주어진 경우와 생략된 경우로 나누어서 구현 함수를 작성해야 한다.
    // 함수 자체에 초기값 인자가 주어지지 않았다면, collection의 첫번째 원소가 초기값이 되고 iterator함수는 collection의 2번째 원소부터 인자로 받게 된다.
    // 만약 초기값이 주어졌다면, iterator함수는 collection의 첫번째 원소부터 인자로 받게 된다.
    


    7. contains _.contains(list, value, [fromIndex])

  • list안에 value가 있는지 여부를 확인하는 함수이다. 만약 value가 있으면 true를 리턴한다.
  • fromIndex가 주어진다면, value가 있는지 찾기 시작하는 기준 index가 된다.

  • contains 함수 예재
    _.contains([1, 2, 3], 3);
    => true
    
  • contains 함수 구현
    _.contains = function(collection, target) {
      collection = Object.values(collection);
      return _.reduce(collection, function(wasFound, item) {
     if (wasFound) {
       return true;
     }
     return item === target;
      }, false);
    };
    // array의 경우에도 Object.values() 메소드가 value를 반환해주기 때문에 array와 object를 나누어서 구현할 이유가 사라졌다.
    // contains 함수는 결과적으로 collection에 target 원소가 있으면 true, 없으면 false의 하나의 값 만을 반환하기 때문에 reduce함수로 각 원소를 확인할 수 있다.
    // reduce함수를 적용하여, 각 원소를 하나씩 돌아가면서 만약 target 원소가 있으면 return으로 바꿔주고, 일치하는 값이 없으면 초기값인 false를 반환한다.
    


    8. every _.every(list, [predicate], [context])

  • list의 모든 원소들이 predicate함수의 truth 테스트를 통과할 경우에만 true를 반환하는 함수이다.
  • 만약에 확인하는 원소들 중에서 predicate 함수를 통과하지 못하는 경우, 그 즉시 false를 반환해준다.

  • every 함수 예재
    _.every([2, 4, 5], function(num) { return num % 2 == 0; });
    => false
    
  • every 함수 구현
    _.every = function(collection, iterator) {
      var testFunc = iterator || _.identity;
      return _.reduce(collection, function(checks, item) {
     if (checks === false) {
       return false;
     } else if (testFunc(item)) {
       checks = true;
     } else {
       checks = false;
     }
      return checks;
      }, true)
    };
    // iterator가 주어지는 경우와 주어지지 않는 경우(iteratee가 적용됨)가 있기 때문에 새로운 testFunc를 선언하여 각 원소의 true/fase를 확인한다.
    // reduce 함수를 시행하여 초기값을 true로 설정한 뒤에 원소가 false인 경우를 발견하면 바로 false를 반환해줄 수 있도록 한다.
    // 즉, 최우선적으로 checks 값에 false가 들어가면 every 함수가 false를 리턴해줄 수 있도록 하고,
    // checks 값이 true라면 각 원소를 testFunc에 대입하여 checks 값을 판단해주면 된다.
    


    9. some _.some(list, [predicate], [context])

  • list의 모든 원소들이 predicate함수의 truth 테스트를 모두 통과하지 못는 경우에만 false를 반환하는 함수이다.

  • some 함수 예재
    _.some([2, 4, 5], function(num) { return num % 2 == 0; });
    => true
    
  • some 함수 구현
    _.some = function(collection, iterator) {
      var testFunc = iterator || _.identity;
      return _.reduce(collection, function(checks, item) {
     if (checks === true) {
       return true;
     } else if (!testFunc(item)) {
       checks = flase;
     } else {
       checks = true;
     }
      return checks;
      }, false)
    };
    // reduce 함수를 시행하여 초기값을 false로 설정한 뒤에 원소가 true인 경우를 발견하면 바로 true를 반환하도록 every를 수정한다.
    


    10. shuffle _.shuffle(list)

  • 주어진 list를 임의로 섞은 새로운 list를 반환하는 함수이다. 이때 임의로 섞는 방식은 ‘Fisher-Yates shuffle’(https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)을 이용한다.

  • shuffle 함수 예재
    _.shuffle([1, 2, 3, 4, 5, 6]);
    => [4, 1, 6, 3, 5, 2]
    
  • shuffle 함수 구현
    _.shuffle = function(array) {
      var randomIndex = Math.floor(Math.random() * array.length);
      var shuffledArray = [];
      for (var i = 0; i < array.length; i++) {
     shuffledArray.splice(randomIndex, 0, array[i])
      }
      return shuffledArray;
    };
    // shuffle 함수는 기존의 array를 임의로 섞어야 하기 때문에 임의의 index 값을 지정해줄 수 있도록 정수인 난수를 생성한다.
    // 처음부터 randomIndex의 최대값을 array의 길이만큼 정했기 때문에 for loop을 돌면서도 사용할 수 있다.
    // 기존의 array에서 순서대로 원소를 불러올 때, 새로운 array의 임의의 인덱스를 지정하여 값을 할당할 수 있다.
    // splice메소드는 지정한 index 값이 주어진 array의 length보다 클 경우 가장 마지막에 원소를 배치하기 때문에 난수의 값이 길이보다 커지는 것은 문제없어진다.
    


© 2018. by Jae