Подчеркивать.JS groupBy несколько значений

С Помощью Подчеркивания.js, я пытаюсь сгруппировать список элементов несколько раз, то есть

группа по размеру, то для каждого размера, группа по категориям...

http://jsfiddle.net/rickysullivan/WTtXP/1/

В идеале, я хотел бы иметь функцию или расширить _.groupBy(), так что вы можете бросить массив на него с разгона в группе.

var multiGroup = ['size', 'category'];

вероятно, можно просто сделать миксин...

_.mixin({
    groupByMulti: function(obj, val, arr) {
        var result = {};
        var iterator = typeof val == 'function' ? val : function(obj) {
                return obj[val];
            };
        _.each(arr, function(arrvalue, arrIndex) {
            _.each(obj, function(value, objIndex) {
                var key = iterator(value, objIndex);
                var arrresults = obj[objIndex][arrvalue];
                if (_.has(value, arrvalue))
                    (result[arrIndex] || (result[arrIndex] = [])).push(value);

моя голова больно, но я думаю, нужно еще немного поднажать...

            });
        })
        return result;
    }
});

properties = _.groupByMulti(properties, function(item) {

    var testVal = item["size"];

    if (parseFloat(testVal)) {
        testVal = parseFloat(item["size"])
    }

    return testVal

}, multiGroup);

9 ответов


простая рекурсивная реализация:

_.mixin({
  /*
   * @mixin
   *
   * Splits a collection into sets, grouped by the result of running each value
   * through iteratee. If iteratee is a string instead of a function, groups by
   * the property named by iteratee on each of the values.
   *
   * @param {array|object} list - The collection to iterate over.
   * @param {(string|function)[]} values - The iteratees to transform keys.
   * @param {object=} context - The values are bound to the context object.
   * 
   * @returns {Object} - Returns the composed aggregate object.
   */
  groupByMulti: function(list, values, context) {
    if (!values.length) {
      return list;
    }
    var byFirst = _.groupBy(list, values[0], context),
        rest    = values.slice(1);
    for (var prop in byFirst) {
      byFirst[prop] = _.groupByMulti(byFirst[prop], rest, context);
    }
    return byFirst;
  }
});

демо в вашем jsfiddle


Я думаю, что ответ @Bergi можно немного упростить, используя lo-Dash's mapValues (для отображения функций над значениями объектов). Это позволяет нам группировать записи в массиве несколькими ключами вложенным образом:

_ = require('lodash');

var _.nest = function (collection, keys) {
  if (!keys.length) {
    return collection;
  }
  else {
    return _(collection).groupBy(keys[0]).mapValues(function(values) { 
      return nest(values, keys.slice(1));
    }).value();
  }
};

я переименовал метод nest потому что он в конечном итоге выполняет ту же роль, что и D3 гнездо оператора. См.в этом суть для сведения и эта скрипка за проявленную использования с образец.

лодашь гнездо метода groupBy


Как насчет этого довольно просто взломать?

console.log(_.groupBy(getProperties(), function(record){
    return (record.size+record.category);
}));

проверьте это расширение подчеркивания:подчеркивания.Гнездо!--2-->, Ирен рос.

выход этого расширения будет немного отличаться от того, что вы указываете, но модуль составляет всего около 100 строк кода, поэтому вы должны иметь возможность сканировать, чтобы получить направление.


Это отличный вариант использования для уменьшить этап карта-уменьшить. Он не будет таким визуально элегантным, как функция multi-group (вы не можете просто передать массив ключей для группировки), но в целом этот шаблон дает вам больше гибкости для преобразования ваших данных. пример

var grouped = _.reduce(
    properties, 
    function(buckets, property) {
        // Find the correct bucket for the property
        var bucket = _.findWhere(buckets, {size: property.size, category: property.category});

        // Create a new bucket if needed.
        if (!bucket) {
            bucket = {
                size: property.size, 
                category: property.category, 
                items: []
            };
            buckets.push(bucket);
        }

        // Add the property to the correct bucket
        bucket.items.push(property);
        return buckets;
    }, 
    [] // The starting buckets
);

console.log(grouped)

но если вы просто хотите его в подчеркивании mixin, вот мой удар по нему:

_.mixin({
'groupAndSort': function (items, sortList) {
    var grouped = _.reduce(
        items,
        function (buckets, item) {
            var searchCriteria = {};
            _.each(sortList, function (searchProperty) { searchCriteria[searchProperty] = item[searchProperty]; });
            var bucket = _.findWhere(buckets, searchCriteria);

            if (!bucket) {
                bucket = {};
                _.each(sortList, function (property) { bucket[property] = item[property]; });
                bucket._items = [];
                buckets.push(bucket);
            }

            bucket._items.push(item);
            return buckets;
        },
        [] // Initial buckets
    );

    grouped.sort(function (x, y) {
        for (var i in sortList) {
            var property = sortList[i];
            if (x[property] != y[property])
                return x[property] > y[property] ? 1 : -1;
        }
        return 0;
    });

    return _.map(grouped, function (group) {
        var toReturn = { key: {}, value: group.__items };
        _.each(sortList, function (searchProperty) { toReturn.key[searchProperty] = group[searchProperty]; });
        return toReturn;
    });
});

улучшения joyrexus по методу Берги не используют систему подчеркивания / lodash mixin. Вот он, как миксин:

_.mixin({
  nest: function (collection, keys) {
    if (!keys.length) {
      return collection;
    } else {
      return _(collection).groupBy(keys[0]).mapValues(function(values) {
        return _.nest(values, keys.slice(1));
      }).value();
    }
  }
});

пример с lodash и mixin

_.mixin({
'groupByMulti': function (collection, keys) {
if (!keys.length) {
 return collection;
 } else {
  return _.mapValues(_.groupBy(collection,_.first(keys)),function(values) {
    return _.groupByMulti(values, _.rest(keys));
  });
}
}
});    

вот простая для понимания функция.

function mixin(list, properties){

    function grouper(i, list){

        if(i < properties.length){
            var group = _.groupBy(list, function(item){
                var value = item[properties[i]];
                delete item[properties[i]];
                return value;
            });

            _.keys(group).forEach(function(key){
                group[key] = grouper(i+1, group[key]);
            });
            return group;
        }else{
            return list;
        }
    }

    return grouper(0, list);

}

группировка составным ключом имеет тенденцию работать лучше для меня в большинстве ситуаций:

const groups = _.groupByComposite(myList, ['size', 'category']);

демо с использованием скрипки OP

Mixin

_.mixin({
  /*
   * @groupByComposite
   *
   * Groups an array of objects by multiple properties. Uses _.groupBy under the covers,
   * to group by a composite key, generated from the list of provided keys.
   *
   * @param {Object[]} collection - the array of objects.
   * @param {string[]} keys - one or more property names to group by.
   * @param {string} [delimiter=-] - a delimiter used in the creation of the composite key.
   *
   * @returns {Object} - the composed aggregate object.
   */
  groupByComposite: (collection, keys, delimiter = '-') =>
    _.groupBy(collection, (item) => {
      const compositeKey = [];
      _.each(keys, key => compositeKey.push(item[key]));
      return compositeKey.join(delimiter);
    }),
});