Группировка объектов по нескольким столбцам с помощью Lodash или подчеркивания
у меня есть следующий объект 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
      }
   ]
}
Я хочу вывести его как:
2 
  object with id 1
  object with id 2 (because replyToId value is same as userId
3
  object with id 5
поэтому в основном я хочу рассмотреть значение UserId и replyToId в той же группе.
Я построил свой собственный миксин под lodash, обернув метод 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;
    }
});
вызов выглядит так:
_.splitGroupBy(data.notes,['userId', 'replyToId']);
выход идет без группы. Даже когда я пробовал с _.forEach раскол не происходит правильно.
4 ответов
решение с использованием подчеркивания:
    var props = ['userId', 'replyToId'];
    var notNull = _.negate(_.isNull);
    var groups = _.groupBy(record.notes, function(note){
        return _.find(_.pick(note, props), notNull);
    });
Это, вероятно, может быть сделано намного красивее, но это должно работать:
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;
  }
});
вы можете сопоставить свой список атрибутов с их соответствующими значениями и выбрать первое неверное значение в качестве ключа группы:
_.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>предполагая, что userId и replyToId являются взаимоисключающими (т. е. у вас либо есть userId или replyToId, но никогда оба), поскольку они находятся в образце данных, то указание пользовательской функции группировки работает:
_.groupBy(data.notes, function(note) {
    return note.userId || note.replyToId;
});