Obwohl die Antwort von linq interessant ist, ist sie auch ziemlich schwer. 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 in Aktion auf JSBin sehen .
Ich habe in Underscore nichts gesehen, was das hastut, obwohl ich es möglicherweise vermisse. Es ist ähnlich wie _.contains, wird aber _.isEqualeher als ===für Vergleiche verwendet. Davon abgesehen ist der Rest problemspezifisch, allerdings mit dem Versuch, generisch zu sein.
Kehrt jetzt DataGrouper.sum(data, ["Phase"])zurück
[
{Phase: "Phase 1", Value: 50},
{Phase: "Phase 2", Value: 130}
]
Und DataGrouper.sum(data, ["Phase", "Step"])kehrt 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}
]
Ist sumaber hier nur eine mögliche Funktion. Sie können andere nach Belieben 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ückkehren
[
{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(", ")});
});
dann DataGrouper.tasks(data, ["Phase", "Step"])bringt dich ein Anruf
[
{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)"}
]
DataGrouperselbst ist eine Funktion. Sie können es mit Ihren Daten und einer Liste der Eigenschaften aufrufen, nach denen Sie gruppieren möchten. Es gibt ein Array zurück, dessen Elemente Objekte mit zwei Eigenschaften sind: keyIst die Sammlung gruppierter Eigenschaften, valsist ein Array von Objekten, die die verbleibenden Eigenschaften enthalten, die nicht im Schlüssel enthalten sind. Zum Beispiel DataGrouper(data, ["Phase", "Step"])ergibt sich:
[
{
"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.registerakzeptiert eine Funktion und erstellt eine neue Funktion, die die Anfangsdaten und die Eigenschaften akzeptiert, nach denen gruppiert werden soll. Diese neue Funktion nimmt dann das Ausgabeformat wie oben an und führt Ihre Funktion nacheinander für jedes von ihnen aus, wobei ein neues Array zurückgegeben wird. Die generierte Funktion wird als Eigenschaft von DataGroupergemäß einem von Ihnen angegebenen Namen gespeichert und auch zurückgegeben, wenn Sie nur eine lokale Referenz wünschen.
Das ist eine Menge Erklärung. Der Code ist ziemlich einfach, hoffe ich!