web-dev-qa-db-ja.com

Underscore.js groupBy複数の値

Underscore.jsを使用して、アイテムのリストを複数回グループ化しようとしています。

次に、SIZEでグループ化し、各SIZEについて、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);
20
rickysullivan

単純な再帰的な実装:

_.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のデモ

40
Bergi

@ -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 を参照してください。

lodashnestgroupby

17
joyrexus

このかなり単純なハックはどうですか?

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

下線の拡張子 nderscore.Nest をIrene Rosが確認してください。

この拡張機能の出力は指定したものとは少し異なりますが、モジュールはコードが約100行しかないため、方向を調べるためにスキャンできるはずです。

1
Jacob Brown

ロダッシュとミックスインの例

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

Bergiのメソッドに対するjoyrexusによる改善は、アンダースコア/ロダッシュミックスインシステムを利用していません。これはミックスインです:

_.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();
    }
  }
});
0
Vedant Misra

これは、map-reducereduceフェーズの優れた使用例です。 。これは、マルチグループ関数ほど視覚的に洗練されているわけではありません(グループ化するキーの配列を渡すことはできません)が、このパターン全体では、データをより柔軟に変換できます。 [〜#〜]例[〜#〜]

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({
'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;
    });
});
0
Tyler

こちらがわかりやすい機能です。

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

}
0
Kashif Nazar

複合キーによるグループ化は、ほとんどの状況で私にとってよりうまく機能する傾向があります。

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

OPのフィドルを使用したデモ

混入します

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