使用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);
答案 0 :(得分:39)
一个简单的递归实现:
_.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;
}
});
答案 1 :(得分:16)
我认为@ 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。
答案 2 :(得分:14)
这个相当简单的黑客怎么样?
console.log(_.groupBy(getProperties(), function(record){
return (record.size+record.category);
}));
答案 3 :(得分:1)
查看Irene Ros的下划线扩展名Underscore.Nest。
此扩展程序的输出与您指定的稍有不同,但模块只有大约100行代码,因此您应该能够扫描以获取方向。
答案 4 :(得分:0)
这是 map-reduce 的 reduce 阶段的一个很好的用例。它不会像多组函数一样具有视觉上的优雅(您不能只传入一组键来分组),但总体而言,这种模式可以让您更灵活地转换数据。 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({
'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;
});
});
答案 5 :(得分:0)
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();
}
}
});
答案 6 :(得分:0)
使用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));
});
}
}
});
答案 7 :(得分:0)
这是一个易于理解的功能。
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);
}
答案 8 :(得分:0)
在大多数情况下,通过复合键进行分组往往对我更有效:
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);
}),
});