Auch wenn diese Frage alt ist, möchte ich eine elegantere Lösung anbieten:
/**
* Groups an array of objects by one or more keys
*
* @param array arr The array of objects to group
*
* @param string|function A string representing the child property to group by
* or a function that returns an array of one or more properties.
*
* @returns An object with keys representing the grouping properties,
* finally holding an array of records that fell into
* those groups.
*/
var group = function( items, by ) {
var groups = {},
group,
values,
i = items.length,
j,
key,
group_keys;
// make sure we specified how we want it grouped
if( !by ) { return items; }
while( i-- ) {
// find out group values for this item
values = ( typeof(by) === "function" && by( items[i] ) ||
typeof items[i] === "object" && items[i][by] ||
items[i] );
// make sure our group values are an array
values = values instanceof Array && values || [ values ];
// recursively group
group = groups;
for( j = 0; j < values.length; j++ ) {
key = values[j];
group = ( group [key] || ( group [key] = j === values.length - 1 && [] || {} ) );
}
// for the last group, push the actual item onto the array
group = ( group instanceof Array && group || [] ).push( items[i] );
}
return groups;
};
Ich rufe es hiermit an:
var items = [
{ "id" : 1, "name" : "foo", "category" : "a" },
{ "id" : 2, "name" : "foo", "category" : "a" },
{ "id" : 3, "name" : "bar", "category" : "b" },
{ "id" : 4, "name" : "free", "category" : "a" },
{ "id" : 5, "name" : "beer", "category" : "b" },
{ "id" : 6, "name" : "foo", "category" : "b" }
];
var groups = group( items, function( item ) { return [ item.category, item.name ]; } );
Ergibt dies:
{
b: {
foo: [
{
id: 6
name: foo
category: b
}
]
beer: [
{
id: 5
name: beer
category: b
}
]
bar: [
{
id: 3
name: bar
category: b
}
]
}
a: {
free: [
{
id: 4
name: free
category: a
}
]
foo: [
{
id: 2
name: foo
category: a
}
{
id: 1
name: foo
category: a
}
]
}
}
Wie auch immer, ich hoffe, das hilft jemandem.