Reputation: 141
I have an array of objects. Basically, each object has 2 keys named id
and value
. Now I have to convert this array into an another array but with respect to ids.
Eg
const arr1 = [{
"month": "April",
"values": [{
"id": "rice",
"value": 10
},
{
"id": "wheat",
"value": 20
},
{
"id": "corn",
"value": 30
}]
},{
"month": "May",
"values": [{
"id": "rice",
"value": 40
},
{
"id": "wheat",
"value": 50
},
{
"id": "corn",
"value": 60
}]
},{
"month": "June",
"values": [{
"id": "rice",
"value": 70
},
{
"id": "wheat",
"value": 80
},
{
"id": "corn",
"value": 90
}]
}]
I want to take out values and make it something like
const arr2 = [{
id: "rice",
values: [10,40,70],
},{
id: "wheat",
values: [20,50,80],
},{
id: "corn",
values: [30,60,90],
}]
arranged in order of dates.
What I have tried
const ids = arr1[0].values.map(value => value.id);
const arr2 = ids.map(id => {
return {
id,
values:[]
}
}
I am unable to think of how to fill the values. The most obvious way will require 3 loops which should not be a good idea.
What should i do?
Upvotes: 0
Views: 62
Reputation: 12919
This really just a 'group by' operation with a nested array. As such, you can handle it in a single reduce()
call, with a nested loop over each values
array.
const arr1 = [ { month: 'April', values: [ { id: 'rice', value: 10, }, { id: 'wheat', value: 20, }, { id: 'corn', value: 30, }, ], }, { month: 'May', values: [ { id: 'rice', value: 40, }, { id: 'wheat', value: 50, }, { id: 'corn', value: 60, }, ], }, { month: 'June', values: [ { id: 'rice', value: 70, }, { id: 'wheat', value: 80, }, { id: 'corn', value: 90, }, ], }, ];
const result = Object.values(
arr1.reduce((a, { values }) => {
for (const { id, value } of values) {
(a[id] ??= { id, values: [] }).values.push(value);
}
return a;
}, {})
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Or using nested reduce()
calls passing a single Map
instance to both.
const arr1 = [ { month: 'April', values: [ { id: 'rice', value: 10, }, { id: 'wheat', value: 20, }, { id: 'corn', value: 30, }, ], }, { month: 'May', values: [ { id: 'rice', value: 40, }, { id: 'wheat', value: 50, }, { id: 'corn', value: 60, }, ], }, { month: 'June', values: [ { id: 'rice', value: 70, }, { id: 'wheat', value: 80, }, { id: 'corn', value: 90, }, ], }, ];
const result = Array.from(
arr1.reduce(
(a, { values }) =>
values.reduce((b, { id, value }) => b.set(id, [...(b.get(id) ?? []), value])
, a)
, new Map()
),
(e) => Object.fromEntries([e])
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 0
Reputation: 191976
Get an array of values using Array.flatMap()
, and then reduce to a Map, and transform to an array of object by converting the Map using Array.from()
:
const fn = months =>
Array.from(months
.flatMap(({ values }) => values)
.reduce(
(acc, o) => acc.set(o.id, [...(acc.get(o.id) ?? []), o.value]),
new Map()
),
([id, values]) => ({ id, values })
)
const months = [{month: "April", values: [{id: "rice", value: 10}, {id: "wheat", value: 20}, {id: "corn", value: 30}]}, {month: "May", values: [{id: "rice", value: 40}, {id: "wheat", value: 50}, {id: "corn", value: 60}]}, {month: "June", values: [{id: "rice", value: 70}, {id: "wheat", value: 80}, {id: "corn", value: 90}]}]
console .log (fn (months))
.as-console-wrapper {
max-height: 100% !important;
top: 0
}
Upvotes: 0
Reputation: 50797
Here's one approach:
const extractValues = months =>
Object .entries (months
.flatMap (({values}) => values)
.reduce ((a, {id, value}) => ({...a, [id]: [...(a [id] || []), value]}), {})
) .map (([id, values]) => ({id, values}))
const months = [{month: "April", values: [{id: "rice", value: 10}, {id: "wheat", value: 20}, {id: "corn", value: 30}]}, {month: "May", values: [{id: "rice", value: 40}, {id: "wheat", value: 50}, {id: "corn", value: 60}]}, {month: "June", values: [{id: "rice", value: 70}, {id: "wheat", value: 80}, {id: "corn", value: 90}]}]
console .log (extractValues (months))
.as-console-wrapper {max-height: 100% !important; top: 0}
We start by flat-mapping the original array to combine all the value
nodes in a single array, so it will contain rice/10
, wheat/20
, corn/30
, rice/40
, ..., corn/90
, in objects like your original: {id: 'rice', value: 10}
.
Then with reduce
, we combine those into
{
rice: [10, 40, 70],
wheat: [20, 50, 80],
corn: [30, 60, 90]
}
We use Object.entries
to extract that into
[
['rice', [10, 40, 70]],
['wheat', [20, 50, 80]],
['corn', [30, 60, 90]]
]
and then we map
over them, converting this into your expected output structure.
We retain the order of the inputs, and don't try to resort by your month. That would not be much more work, but it's a reasonable guess that the data will already be sorted that way.
I was curious about the possibility that the data is less consistent, and thought I'd rewrite in a way that handled that. In this version, we also have one month that has soybean
records, and June
is in the array before May
. So we need to sort on the months, and capture all the ids we might need in any of them. It looks like this:
const byMonth = ((months) => (a, b) =>
(months .indexOf (a .month) || 13) - (months .indexOf (b .month) || 13)
)(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'])
const extract = (
xs,
months = [... xs] .sort (byMonth),
ids = [... new Set (months .flatMap (({values}) => values .map (({id}) => id)))]
) => ids .map ((id) => ({
id,
values: months .map (m => (m .values .find (v => v.id == id) || {value: null}) .value)
}))
const months = [{month: "April", values: [{id: "rice", value: 10}, {id: "wheat", value: 20}, {id: "corn", value: 30}]}, {month: "June", values: [{id: "rice", value: 40}, {id: "wheat", value: 50}, {id: "corn", value: 60}]}, {month: "May", values: [{id: "rice", value: 70}, {id: "wheat", value: 80}, {id: "soybeans", value: 42}, {id: "corn", value: 90}]}]
console .log (extract (months))
.as-console-wrapper {max-height: 100% !important; top: 0}
Note that the output now has some nulls
to note that there were no soybean
records in April or June, only in May:
{
id: "soybeans",
values: [null, 42, null]
}
This sort of working around inconsistent data is a large part of my job, and of many developers I know.
Upvotes: 1
Reputation: 730
const arr2 = arr1.flatMap(x => x.values);
arr2.map(x => ({
id: x.id,
value: arr2
.filter(y => y.id === x.id)
.map(y => y.value)
}));
Upvotes: 0