Obwohl die linq Antwort ist interessant, aber auch ziemlich schwerfällig. Mein Ansatz ist etwas anders:
var DataGrouper = (function() {
var has = function(obj, target) {
return _.any(obj, function(value) {
return _.isEqual(value, target);
});
};
var keys = function(data, names) {
return _.reduce(data, function(memo, item) {
var key = _.pick(item, names);
if (!has(memo, key)) {
memo.push(key);
}
return memo;
}, []);
};
var group = function(data, names) {
var stems = keys(data, names);
return _.map(stems, function(stem) {
return {
key: stem,
vals:_.map(_.where(data, stem), function(item) {
return _.omit(item, names);
})
};
});
};
group.register = function(name, converter) {
return group[name] = function(data, names) {
return _.map(group(data, names), converter);
};
};
return group;
}());
DataGrouper.register("sum", function(item) {
return _.extend({}, item.key, {Value: _.reduce(item.vals, function(memo, node) {
return memo + Number(node.Value);
}, 0)});
});
Sie können es sehen in Aktion auf JSBin .
Ich habe in Underscore nichts gesehen, was das tut has
tut, obwohl ich es vielleicht übersehen habe. Es ist fast dasselbe wie _.contains
, sondern verwendet _.isEqual
statt ===
für Vergleiche. Abgesehen davon ist der Rest des Textes problemspezifisch, auch wenn versucht wurde, ihn allgemein zu halten.
Jetzt DataGrouper.sum(data, ["Phase"])
gibt zurück.
[
{Phase: "Phase 1", Value: 50},
{Phase: "Phase 2", Value: 130}
]
Und DataGrouper.sum(data, ["Phase", "Step"])
gibt zurück.
[
{Phase: "Phase 1", Step: "Step 1", Value: 15},
{Phase: "Phase 1", Step: "Step 2", Value: 35},
{Phase: "Phase 2", Step: "Step 1", Value: 55},
{Phase: "Phase 2", Step: "Step 2", Value: 75}
]
Aber sum
ist hier nur eine mögliche Funktion. Sie können nach Belieben weitere registrieren:
DataGrouper.register("max", function(item) {
return _.extend({}, item.key, {Max: _.reduce(item.vals, function(memo, node) {
return Math.max(memo, Number(node.Value));
}, Number.NEGATIVE_INFINITY)});
});
und jetzt DataGrouper.max(data, ["Phase", "Step"])
wird zurückgegeben
[
{Phase: "Phase 1", Step: "Step 1", Max: 10},
{Phase: "Phase 1", Step: "Step 2", Max: 20},
{Phase: "Phase 2", Step: "Step 1", Max: 30},
{Phase: "Phase 2", Step: "Step 2", Max: 40}
]
oder wenn Sie dies registriert haben:
DataGrouper.register("tasks", function(item) {
return _.extend({}, item.key, {Tasks: _.map(item.vals, function(item) {
return item.Task + " (" + item.Value + ")";
}).join(", ")});
});
und ruft dann DataGrouper.tasks(data, ["Phase", "Step"])
erhalten Sie
[
{Phase: "Phase 1", Step: "Step 1", Tasks: "Task 1 (5), Task 2 (10)"},
{Phase: "Phase 1", Step: "Step 2", Tasks: "Task 1 (15), Task 2 (20)"},
{Phase: "Phase 2", Step: "Step 1", Tasks: "Task 1 (25), Task 2 (30)"},
{Phase: "Phase 2", Step: "Step 2", Tasks: "Task 1 (35), Task 2 (40)"}
]
DataGrouper
ist selbst eine Funktion. Sie können sie mit Ihren Daten und einer Liste der Eigenschaften, nach denen Sie gruppieren möchten, aufrufen. Sie gibt ein Array zurück, dessen Elemente Objekte mit zwei Eigenschaften sind: key
ist die Sammlung der gruppierten Eigenschaften, vals
ist ein Array von Objekten, das die übrigen Eigenschaften enthält, die nicht im Schlüssel enthalten sind. Zum Beispiel, DataGrouper(data, ["Phase", "Step"])
ergeben wird:
[
{
"key": {Phase: "Phase 1", Step: "Step 1"},
"vals": [
{Task: "Task 1", Value: "5"},
{Task: "Task 2", Value: "10"}
]
},
{
"key": {Phase: "Phase 1", Step: "Step 2"},
"vals": [
{Task: "Task 1", Value: "15"},
{Task: "Task 2", Value: "20"}
]
},
{
"key": {Phase: "Phase 2", Step: "Step 1"},
"vals": [
{Task: "Task 1", Value: "25"},
{Task: "Task 2", Value: "30"}
]
},
{
"key": {Phase: "Phase 2", Step: "Step 2"},
"vals": [
{Task: "Task 1", Value: "35"},
{Task: "Task 2", Value: "40"}
]
}
]
DataGrouper.register
nimmt eine Funktion an und erstellt eine neue Funktion, die die Ausgangsdaten und die Eigenschaften, nach denen gruppiert werden soll, annimmt. Diese neue Funktion nimmt dann das oben beschriebene Ausgabeformat und führt Ihre Funktion nacheinander für jede dieser Eigenschaften aus und gibt ein neues Array zurück. Die erzeugte Funktion wird als eine Eigenschaft von DataGrouper
nach einem von Ihnen angegebenen Namen und auch zurückgegeben, wenn Sie nur eine lokale Referenz wünschen.
Nun, das ist eine Menge an Erklärungen. Ich hoffe, der Code ist einigermaßen einfach!