【问题标题】:Underscore.js groupBy multiple valuesUnderscore.js groupBy 多个值
【发布时间】:2012-04-18 19:30:03
【问题描述】:

使用 Underscore.js,我正在尝试多次对项目列表进行分组,即

按 SIZE 分组,然后对每个 SIZE,按 CATEGORY 分组...

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

理想情况下,我希望有一个函数或扩展_.groupBy(),这样您就可以向它抛出一个数组,其中包含要分组的参数。

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

大概可以做一个mixin...

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

【问题讨论】:

标签: arrays underscore.js javascript-objects


【解决方案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;
  }
});

Demo in your 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 运算符所扮演的角色大致相同。有关详细信息,请参阅 this gist,有关您的示例的演示用法,请参阅 this fiddle

    【讨论】:

    • 这是最简单最好的答案。
    【解决方案3】:

    这个相当简单的 hack 怎么样?

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

    【讨论】:

    • 最好在两个字段之间添加一些内容,例如:record.size + "`"+record.category(任何不寻常的符号或 char(0))以防止出现配对的情况: “12”+“3”和“1”+“23”
    • 对我来说这是史诗般的回应。请确保您将其作为@agershun 状态的字符串返回
    【解决方案4】:

    查看这个下划线扩展名:Underscore.Nest,作者是 Irene Ros。

    这个扩展的输出会和你指定的略有不同,但是这个模块只有大约 100 行代码,所以你应该可以扫描得到方向。

    【讨论】:

      【解决方案5】:

      这是 map-reducereduce 阶段的一个很好的用例。它不会像多组功能那样在视觉上优雅(您不能只传递一组键来分组),但总体而言,这种模式为您转换数据提供了更大的灵活性。 EXAMPLE

      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 对 bergi 方法的改进没有利用下划线/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();
            }
          }
        });
        

        【讨论】:

          【解决方案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']);
              

              Demo using OP's fiddle

              混音

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

              【讨论】:

                猜你喜欢
                • 2014-11-28
                • 1970-01-01
                • 2021-04-09
                • 1970-01-01
                • 1970-01-01
                • 2016-04-16
                • 2018-08-20
                • 2016-03-17
                • 1970-01-01
                相关资源
                最近更新 更多