Подтвердить что ты не робот

Группа Underscore.jsВ нескольких значениях

Используя Underscore.js, я пытаюсь группировать список элементов несколько раз, т.е.

Группируйте по SIZE, затем для каждого SIZE, group by CATEGORY...

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);
4b9b3361

Ответ 1

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

_.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

Ответ 2

Я думаю, что ответ @Bergi можно немного упростить, используя Lo-Dash 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 nest. Подробнее см. эту тему и эту скрипту для показанного использования с вашим примером.

Ответ 3

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

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

Ответ 4

Посмотрите на это подчеркивание: Underscore.Nest, автор Irene Ros.

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

Ответ 5

Это большой прецедент для фазы уменьшения сокращения карты. Это не будет столь же визуально элегантным, как многогрупповая функция (вы не можете просто передать массив ключей для группировки), но в целом этот шаблон дает вам больше гибкости для преобразования ваших данных. ПРИМЕР

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;
    });
});

Ответ 6

Усовершенствования метода joyrexus on bergi не используют преимущества системы подчеркивания /lodash mixin. Здесь это как 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();
    }
  }
});

Ответ 7

Пример с 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));
  });
}
}
});    

Ответ 8

Вот прост в понимании функции.

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);

}

Ответ 9

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

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);
    }),
});