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 has
tut, obwohl ich es möglicherweise vermisse. Es ist ähnlich wie _.contains
, wird aber _.isEqual
eher 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 sum
aber 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)"}
]
DataGrouper
selbst 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: key
Ist die Sammlung gruppierter Eigenschaften, vals
ist 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.register
akzeptiert 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 DataGrouper
gemäß 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!