Bien que la réponse linq soit intéressante, elle est également assez lourde. Mon approche est quelque peu différente:
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)});
});
Vous pouvez le voir en action sur JSBin .
Je n'ai rien vu dans Underscore qui fasse ce qui se haspasse, bien que je puisse le manquer. C'est à peu près la même chose _.contains, mais utilise _.isEqualplutôt que=== pour des comparaisons. En dehors de cela, le reste est spécifique au problème, bien qu'avec une tentative d'être générique.
DataGrouper.sum(data, ["Phase"])Retourne maintenant
[
{Phase: "Phase 1", Value: 50},
{Phase: "Phase 2", Value: 130}
]
Et DataGrouper.sum(data, ["Phase", "Step"])retourne
[
{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}
]
Mais ce sumn'est qu'une fonction potentielle ici. Vous pouvez en enregistrer d'autres à votre guise:
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)});
});
et maintenant DataGrouper.max(data, ["Phase", "Step"])je reviendrai
[
{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}
]
ou si vous l'avez enregistré:
DataGrouper.register("tasks", function(item) {
return _.extend({}, item.key, {Tasks: _.map(item.vals, function(item) {
return item.Task + " (" + item.Value + ")";
}).join(", ")});
});
puis appeler DataGrouper.tasks(data, ["Phase", "Step"]) vous
[
{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)"}
]
DataGrouperlui-même est une fonction. Vous pouvez l'appeler avec vos données et une liste des propriétés que vous souhaitez regrouper. Il renvoie un tableau dont les éléments sont des objets avec deux propriétés: keyest la collection de propriétés groupées, valsest un tableau d'objets contenant les propriétés restantes qui ne sont pas dans la clé. Par exemple,DataGrouper(data, ["Phase", "Step"]) donnera:
[
{
"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.registeraccepte une fonction et crée une nouvelle fonction qui accepte les données initiales et les propriétés à regrouper. Cette nouvelle fonction prend ensuite le format de sortie comme ci-dessus et exécute votre fonction contre chacun d'eux à son tour, en retournant un nouveau tableau. La fonction générée est stockée en tant que propriété deDataGrouper selon un nom que vous fournissez et est également renvoyée si vous souhaitez simplement une référence locale.
Eh bien, c'est beaucoup d'explications. Le code est assez simple, j'espère!