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 has
passe, bien que je puisse le manquer. C'est à peu près la même chose _.contains
, mais utilise _.isEqual
plutô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 sum
n'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)"}
]
DataGrouper
lui-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: key
est la collection de propriétés groupées, vals
est 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.register
accepte 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!