javascript 使用 Lodash 或 Underscore 按多列对对象进行分组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29587488/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 10:45:25  来源:igfitidea点击:

Grouping objects by multiple columns with Lodash or Underscore

javascriptunderscore.jslodash

提问by codebased

I have following object records:

我有以下对象records

 {  
   "notes":[  
      {  
         "id":1,
         "description":"hey",
         "userId":2,
         "replyToId":null,
         "postId":2,
         "parentId":null
      },
      {  
         "id":5,
         "description":"hey test",
         "userId":3,
         "replyToId":null,
         "postId":2,
         "parentId":null
      },
      {  
         "id":2,
         "description":"how are you",
         "userId":null,
         "replyToId":2,
         "postId":2,
         "parentId":null,
         "user":null
      }
   ]
}

I want to output it as:

我想将其输出为:

2 
  object with id 1
  object with id 2 (because replyToId value is same as userId
3
  object with id 5

So basically I want to consider UserId and replyToId value under the same group.

所以基本上我想考虑同一组下的 UserId 和 replyToId 值。

I have build my own mixin under lodash, wrapping groupBy method as:

我在 lodash 下构建了自己的 mixin,将 groupBy 方法包装为:

mixin({
    splitGroupBy: function(list, groupByIter){
        if (_.isArray(groupByIter)) {
            function groupBy(obj) {
                return _.forEach(groupByIter, function (key){
                    if ( !!obj[key] ) return obj[key]
                });

            }
        } else {
            var groupBy = groupByIter;
        }

        debugger;

        var groups = _.groupBy(list, groupBy);

        return groups;
    }
});

Call looks like this:

调用看起来像这样:

_.splitGroupBy(data.notes,['userId', 'replyToId']);

The output is coming without group. Even when I have tried with _.mapinstead _.forEachthe split is not happening correctly.

输出是没有组的。甚至当我试图用_.map,而不是_.forEach分裂没有正确发生。

回答by Gruff Bunny

A solution using underscore:

使用下划线的解决方案:

    var props = ['userId', 'replyToId'];

    var notNull = _.negate(_.isNull);

    var groups = _.groupBy(record.notes, function(note){
        return _.find(_.pick(note, props), notNull);
    });

回答by robertklep

This can probably done much prettier, but it should work:

这可能会做得更漂亮,但它应该有效:

lodash.mixin({
  splitGroupBy: function(list, groupByIter) {
    var _ = this, groupBy;
    if (lodash.isArray(groupByIter)) {
      groupBy = function(obj) {
        return _(obj) .pick(groupByIter)
                      .values()
                      .without(null, undefined)
                      .first();
      };
    } else {
      groupBy = groupByIter;
    }
    var groups = _.groupBy(list, groupBy);
    return groups;
  }
});

回答by nikoshr

You could map your list of attributes to their respective values and pick the first non falsy value as your group key:

您可以将您的属性列表映射到它们各自的值,并选择第一个非虚假值作为您的组键:

_.mixin({
    splitGroupBy: function(list, groupByIter){
        if (!_.isArray(groupByIter))
            return _.groupBy(list, groupByIter);

        return _.groupBy(list, function(o) {
            var values = _.map(groupByIter, function(k) {
                return o[k];
            });
            return _.find(values);
        });
    }
});

var data = {  
   "notes":[  
      {  
         "id":1,
         "userId":2,
         "replyToId":null
      },
      {  
         "id":5,
         "userId":3,
         "replyToId":null
      },
      {  
         "id":2,
         "userId":null,
         "replyToId":2
      }
   ]
};

_.mixin({
    splitGroupBy: function(list, groupByIter){
        if (!_.isArray(groupByIter))
            return _.groupBy(list, groupByIter);

        return _.groupBy(list, function(o) {
            var values = _.map(groupByIter, function(k) {
                return o[k];
            });
            return _.find(values);
        });
    }
});

snippet.log(JSON.stringify(_.splitGroupBy(data.notes,['userId', 'replyToId'])));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>

<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

回答by Retsam

Assuming userIdand replyToIdare mutually exclusive (i.e. you either have a userIdor a replyToId, but never both) as they are in the sample data, then specifying a custom grouping function works:

假设userIdreplyToId是互斥的(即您有 auserId或 a replyToId,但从来没有两者),因为它们在示例数据中,然后指定自定义分组函数有效:

_.groupBy(data.notes, function(note) {
    return note.userId || note.replyToId;
});

回答by YairTawil

You can use stringify object as key.

您可以使用 stringify 对象作为键。

_.groupBy(notes, ({ userId, replyToId }) => JSON.stringify({ userId, replyToId }));

output:

输出:

{
  "{\"userId\":2,\"replyToId\":null}": [
    {
      "id": 1,
      "description": "hey",
      "userId": 2,
      "replyToId": null,
      "postId": 2,
      "parentId": null
    }
  ],
  "{\"userId\":3,\"replyToId\":null}": [
    {
      "id": 5,
      "description": "hey test",
      "userId": 3,
      "replyToId": null,
      "postId": 2,
      "parentId": null
    }
  ],
  "{\"userId\":null,\"replyToId\":2}": [
    {
      "id": 2,
      "description": "how are you",
      "userId": null,
      "replyToId": 2,
      "postId": 2,
      "parentId": null,
      "user": null
    }
  ]
}