Est-ce que quelqu'un connaît un moyen (lodash si possible aussi) de grouper un tableau d'objets par une clé d'objet puis de créer un nouveau tableau d'objets basé sur le regroupement? Par exemple, j'ai un tableau d'objets car:
var cars = [
{
'make': 'audi',
'model': 'r8',
'year': '2012'
}, {
'make': 'audi',
'model': 'rs5',
'year': '2013'
}, {
'make': 'ford',
'model': 'mustang',
'year': '2012'
}, {
'make': 'ford',
'model': 'fusion',
'year': '2015'
}, {
'make': 'kia',
'model': 'optima',
'year': '2012'
},
];
Je souhaite créer un nouveau tableau d'objets car regroupé par make
:
var cars = {
'audi': [
{
'model': 'r8',
'year': '2012'
}, {
'model': 'rs5',
'year': '2013'
},
],
'ford': [
{
'model': 'mustang',
'year': '2012'
}, {
'model': 'fusion',
'year': '2015'
}
],
'kia': [
{
'model': 'optima',
'year': '2012'
}
]
}
La réponse de Timo est comment je le ferais. _.groupBy
simple, et autoriser certaines duplications dans les objets de la structure groupée.
Cependant, le PO a également demandé que les clés make
en double soient supprimées. Si vous vouliez aller jusqu'au bout:
var grouped = _.mapValues(_.groupBy(cars, 'make'),
clist => clist.map(car => _.omit(car, 'make')));
console.log(grouped);
Rendements:
{ audi:
[ { model: 'r8', year: '2012' },
{ model: 'rs5', year: '2013' } ],
ford:
[ { model: 'mustang', year: '2012' },
{ model: 'fusion', year: '2015' } ],
kia: [ { model: 'optima', year: '2012' } ] }
Si vous souhaitez utiliser Underscore.js, notez que sa version de _.mapValues
s’appelle _.mapObject
.
En clair, vous pouvez utiliser Array#reduce
avec un objet
var cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }],
result = cars.reduce(function (r, a) {
r[a.make] = r[a.make] || [];
r[a.make].Push(a);
return r;
}, Object.create(null));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Vous recherchez _.groupBy()
.
Supprimer la propriété que vous regroupez des objets doit être trivial si nécessaire:
var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'},];
var grouped = _.groupBy(cars, function(car) {
return car.make;
});
console.log(grouped);
<script src='https://cdn.jsdelivr.net/lodash/4.17.2/lodash.min.js'></script>
En prime, vous obtenez une syntaxe encore plus agréable avec les fonctions de flèche ES6:
const grouped = _.groupBy(cars, car => car.make);
La version courte pour regrouper un tableau d'objets par une certaine clé dans es6:
result = array.reduce((h, obj) => Object.assign(h, { [obj.key]:( h[obj.key] || [] ).concat(obj) }), {})
La version la plus longue:
result = array.reduce(function(h, obj) {
h[obj.key] = (h[obj.key] || []).concat(obj);
return h;
}, {})
Il semble que la question initiale demande comment grouper les voitures par marque, mais omettre la marque dans chaque groupe. Donc, la réponse ressemblerait à ceci:
result = cars.reduce((h, {model,year,make}) => {
return Object.assign(h, { [make]:( h[make] || [] ).concat({model,year})})
}, {})
Vous pouvez essayer de modifier l'objet dans la fonction appelée par itération par _.groupBy func. Notez que le tableau source change ses éléments!
var res = _.groupBy(cars,(car)=>{
const makeValue=car.make;
delete car.make;
return makeValue;
})
console.log(res);
console.log(cars);
Voici votre propre fonction groupBy
qui est une généralisation du code de: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore
function groupBy(xs, f) {
return xs.reduce((r, v, i, a, k = f(v)) => ((r[k] || (r[k] = [])).Push(v), r), {});
}
const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];
const result = groupBy(cars, (c) => c.make);
console.log(result);
C'est également possible avec une simple boucle for
:
const result = {};
for(const {make, model, year} of cars) {
if(!result[make]) result[make] = [];
result[make].Push({ model, year });
}
Créer une méthode pouvant être réutilisée
Array.prototype.groupBy = function(prop) {
return this.reduce(function(groups, item) {
const val = item[prop]
groups[val] = groups[val] || []
groups[val].Push(item)
return groups
}, {})
};
Ensuite, ci-dessous, vous pouvez regrouper par tous les critères
const groupByMake = cars.groupBy('make');
console.log(groupByMake);
var cars = [
{
'make': 'audi',
'model': 'r8',
'year': '2012'
}, {
'make': 'audi',
'model': 'rs5',
'year': '2013'
}, {
'make': 'ford',
'model': 'mustang',
'year': '2012'
}, {
'make': 'ford',
'model': 'fusion',
'year': '2015'
}, {
'make': 'kia',
'model': 'optima',
'year': '2012'
},
];
//re-usable method
Array.prototype.groupBy = function(prop) {
return this.reduce(function(groups, item) {
const val = item[prop]
groups[val] = groups[val] || []
groups[val].Push(item)
return groups
}, {})
};
// initiate your groupBy. Notice the recordset Cars and the field Make....
const groupByMake = cars.groupBy('make');
console.log(groupByMake);
//At this point we have objects. You can use Object.keys to return an array
Je laisserais REAL GROUP BY
pour JS Arrays exemple exactement le même cette tâche ici
const inputArray = [
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
];
var outObject = inputArray.reduce(function(a, e) {
// GROUP BY estimated key (estKey), well, may be a just plain key
// a -- Accumulator result object
// e -- sequentally checked Element, the Element that is tested just at this itaration
// new grouping name may be calculated, but must be based on real value of real field
let estKey = (e['Phase']);
(a[estKey] ? a[estKey] : (a[estKey] = null || [])).Push(e);
return a;
}, {});
console.log(outObject);
Да здравствуют высокие показатели мастерства программистов Ра, товарищи!
var cars = [{
make: 'audi',
model: 'r8',
year: '2012'
}, {
make: 'audi',
model: 'rs5',
year: '2013'
}, {
make: 'ford',
model: 'mustang',
year: '2012'
}, {
make: 'ford',
model: 'fusion',
year: '2015'
}, {
make: 'kia',
model: 'optima',
year: '2012'
}].reduce((r, a) => {
r[a.make] = [...r[a.make] || [], a];
return r;
}, {});
console.log(cars);
function groupBy(data, property) {
return data.reduce((acc, obj) => {
const key = obj[property];
if (!acc[key]) {
acc[key] = [];
}
acc[key].Push(obj);
return acc;
}, {});
}
groupBy(people, 'age');
Pour les cas où la clé peut être nulle et que nous voulons en regrouper d'autres
var cars = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'},
{'make':'kia','model':'optima','year':'2033'},
{'make':null,'model':'zen','year':'2012'},
{'make':null,'model':'blue','year':'2017'},
];
result = cars.reduce(function (r, a) {
key = a.make || 'others';
r[key] = r[key] || [];
r[key].Push(a);
return r;
}, Object.create(null));
Vous pouvez également utiliser la méthode array#forEach()
comme ceci:
const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];
let newcars = {}
cars.forEach(car => {
newcars[car.make] ? // check if that array exists or not in newcars object
newcars[car.make].Push({model: car.model, year: car.year}) // just Push
: (newcars[car.make] = [], newcars[car.make].Push({model: car.model, year: car.year})) // create a new array and Push
})
console.log(newcars);
const reGroup = (list, key) => {
const newGroup = {};
list.forEach(item => {
const newItem = Object.assign({}, item);
delete newItem[key];
newGroup[item[key]] = newGroup[item[key]] || [];
newGroup[item[key]].Push(newItem);
});
return newGroup;
};
const animals = [
{
type: 'dog',
breed: 'puddle'
},
{
type: 'dog',
breed: 'labradoodle'
},
{
type: 'cat',
breed: 'siamese'
},
{
type: 'dog',
breed: 'french bulldog'
},
{
type: 'cat',
breed: 'mud'
}
];
console.log(reGroup(animals, 'type'));
const cars = [
{
'make': 'audi',
'model': 'r8',
'year': '2012'
}, {
'make': 'audi',
'model': 'rs5',
'year': '2013'
}, {
'make': 'ford',
'model': 'mustang',
'year': '2012'
}, {
'make': 'ford',
'model': 'fusion',
'year': '2015'
}, {
'make': 'kia',
'model': 'optima',
'year': '2012'
},
];
console.log(reGroup(cars, 'make'));
Construire sur le réponse par @Jonas_Wilms si vous ne voulez pas taper dans tous vos champs:
var result = {};
for ( let { first_field, ...fields } of your_data )
{
result[first_field] = result[first_field] || [];
result[first_field].Push({ ...fields });
}
Je n'ai pas fait de repère, mais je crois qu'utiliser une boucle for serait plus efficace que tout ce qui est suggéré dans cette réponse également.
Avec lodash/fp, vous pouvez créer une fonction avec _.flow()
qui groupe le premier par une clé, puis mappez chaque groupe et omet une clé de chaque élément:
const { flow, groupBy, mapValues, map, omit } = _;
const groupAndOmitBy = key => flow(
groupBy(key),
mapValues(map(omit(key)))
);
const cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }];
const groupAndOmitMake = groupAndOmitBy('make');
const result = groupAndOmitMake(cars);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>
J'ai bien aimé la réponse de @metakunfu, mais elle ne fournit pas exactement le résultat attendu.
var cars = [
{
'make': 'audi',
'model': 'r8',
'year': '2012'
}, {
'make': 'audi',
'model': 'rs5',
'year': '2013'
}, {
'make': 'ford',
'model': 'mustang',
'year': '2012'
}, {
'make': 'ford',
'model': 'fusion',
'year': '2015'
}, {
'make': 'kia',
'model': 'optima',
'year': '2012'
},
];
result = cars.reduce((h, car) => Object.assign(h, { [car.make]:( h[car.make] || [] ).concat({model: car.model, year: car.year}) }), {})
console.log(JSON.stringify(result));
Sortie:
{
"audi":[
{
"model":"r8",
"year":"2012"
},
{
"model":"rs5",
"year":"2013"
}
],
"ford":[
{
"model":"mustang",
"year":"2012"
},
{
"model":"fusion",
"year":"2015"
}
],
"kia":[
{
"model":"optima",
"year":"2012"
}
]
}