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);
単純な再帰的な実装:
_.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;
}
});
@ -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 を参照してください。
このかなり単純なハックはどうですか?
console.log(_.groupBy(getProperties(), function(record){
return (record.size+record.category);
}));
下線の拡張子 nderscore.Nest をIrene Rosが確認してください。
この拡張機能の出力は指定したものとは少し異なりますが、モジュールはコードが約100行しかないため、方向を調べるためにスキャンできるはずです。
ロダッシュとミックスインの例
_.mixin({
'groupByMulti': function (collection, keys) {
if (!keys.length) {
return collection;
} else {
return _.mapValues(_.groupBy(collection,_.first(keys)),function(values) {
return _.groupByMulti(values, _.rest(keys));
});
}
}
});
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();
}
}
});
これは、map-reduceのreduceフェーズの優れた使用例です。 。これは、マルチグループ関数ほど視覚的に洗練されているわけではありません(グループ化するキーの配列を渡すことはできません)が、このパターン全体では、データをより柔軟に変換できます。 [〜#〜]例[〜#〜]
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;
});
});
こちらがわかりやすい機能です。
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);
}
複合キーによるグループ化は、ほとんどの状況で私にとってよりうまく機能する傾向があります。
const groups = _.groupByComposite(myList, ['size', 'category']);
_.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);
}),
});