I try like a maniac to do some kind of a merge / sort by key on an array of object. Don't know why but I cannot figure how to do that.
My app is in Ionic / Angular.
Here is what I have :
[
{
"2021": {
"a": "aText"
}
},
{
"2021": {
"b": "bText"
}
},
{
"2020": {
"z": "zText"
}
},
{
"2020": {
"y": "yText"
}
},
{
"2020": {
"x": "xText"
}
}
]
My goal is to get this :
[
{
"2021": { "a": "aText", "b": "bText" }
},
{
"2020": { "z": "zText", "y": "yText", "x": "xText" }
}
]
In other words, I'd like to regroup by year and concat them.
Does anybody have an idea on how to do that ?
CodePudding user response:
If you just want a single object with years as keys then it's just a standard 'group by' with a nested loop to iterate the Object.entries()
of each object. If you do want your originally posted output (an array of individual objects) you can just map()
the entires of the returned grouped object and convert each to an object using Object.fromEntries()
const input = [
{ 2021: { a: 'aText' } },
{ 2021: { b: 'bText' } },
{ 2020: { z: 'zText' } },
{ 2020: { y: 'yText' } },
{ 2020: { x: 'xText' } },
];
const grouped_object = input.reduce(
(a, o) => (Object.entries(o).forEach(([y, o]) => (a[y] = { ...(a[y] ?? {}), ...o })), a),
{}
);
// if you just want a single object with years as keys
console.log(grouped_object);
const grouped_array = Object.entries(grouped_object)
.map(([year, data]) => ({[year]: data}));
// the output from your question
console.log(grouped_array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Or refactored to use a for...of
loop and Object.assign()
const input = [
{ 2021: { a: 'aText' } },
{ 2021: { b: 'bText' } },
{ 2020: { z: 'zText' } },
{ 2020: { y: 'yText' } },
{ 2020: { x: 'xText' } },
];
const grouped_object = {};
for (const obj of input) {
for (const [year, data] of Object.entries(obj)) {
grouped_object[year] = Object.assign(grouped_object[year] ?? {}, data);
}
}
// if you just want a single object with years as keys
console.log(grouped_object);
// or avoiding computed properties
const grouped_array = Object.entries(grouped_object)
.map(([year, data]) => (o={}, o[year]=data, o));
// the output from your question
console.log(grouped_array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
It would be better to use an object to group by year. This is an example that uses reduce
to iterate over the array to produce that object.
const data=[{2021:{a:"aText"}},{2021:{b:"bText"}},{2020:{z:"zText"}},{2020:{y:"yText"}},{2020:{x:"xText"}}];
const out = data.reduce((acc, obj) => {
// Get the key and value from the object that in
// the current iteration
const [ [ key, value ] ] = Object.entries(obj);
// If the key doesn't exist on the accumulator (the initial
// object that we passed into the `reduce`) create an empty object
acc[key] = acc[key] || {};
// Update the value of that object property with
// the value of the object
acc[key] = { ...acc[key], ...value };
// Return the updated object for the next iteration
return acc;
// Here's the initial object that
// acts as the accumulator through all the iterations
}, {});
console.log(out);
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Or using an array to hold the information for each year:
const data=[{2021:{a:"aText"}},{2021:{b:"bText"}},{2020:{z:"zText"}},{2020:{y:"yText"}},{2020:{x:"xText"}}];
const out = data.reduce((acc, obj) => {
const [ [ key, value ] ] = Object.entries(obj);
// Use an array instead of an object
acc[key] = acc[key] || [];
// Push the first element of the Object.values
// into the array
acc[key] = [ ...acc[key], Object.values(value)[0] ];
return acc;
}, {});
console.log(out);
<iframe name="sif4" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Additional documentation