I have an array of objects, where each object has a property whose value is another array, like this
[
{
location: 'somewhere',
location_id: 1,
parts: [
{
part_id: 1,
description: 'foo',
qty: 1,
}
]
}
]
I need to map these arrays into a single array like this
[
{
part_id: 1,
description: 'foo',
qty: 1
},
{
part_id: 2,
description: 'bar',
qty: 1
}
]
I tried using reduce like
newArr: arr.reduce((a,b) => a.parts.concat(b.parts))
but get the error 'Cannot read property concat of undefined.' I also tried providing the initialValue argument of reduce as an empty array but same error.
As a bonus, I will eventually need the final array to not contain duplicates of parts: I.e. if a part is in two locations, it would just bine the quantity.
Open to solutions using es6 but needs to not modify original array
I have an array of objects, where each object has a property whose value is another array, like this
[
{
location: 'somewhere',
location_id: 1,
parts: [
{
part_id: 1,
description: 'foo',
qty: 1,
}
]
}
]
I need to map these arrays into a single array like this
[
{
part_id: 1,
description: 'foo',
qty: 1
},
{
part_id: 2,
description: 'bar',
qty: 1
}
]
I tried using reduce like
newArr: arr.reduce((a,b) => a.parts.concat(b.parts))
but get the error 'Cannot read property concat of undefined.' I also tried providing the initialValue argument of reduce as an empty array but same error.
As a bonus, I will eventually need the final array to not contain duplicates of parts: I.e. if a part is in two locations, it would just bine the quantity.
Open to solutions using es6 but needs to not modify original array
Share Improve this question edited Mar 25, 2018 at 2:06 quirky purple asked Mar 25, 2018 at 0:08 quirky purplequirky purple 2,2194 gold badges35 silver badges57 bronze badges 10-
2
You will need to specify an initial value. The first parameter to
reduce
callback is the accumulator array, so it won't have aparts
property. – castletheperson Commented Mar 25, 2018 at 0:13 -
1
[].concat(...arr.map(item => item.parts))
Doesn't create that many intermediate Arrays as the approach withreduce
– Thomas Commented Mar 25, 2018 at 0:21 -
How does the result include this:
part_id: 2, description: 'bar'
when there's nothing in the input that resembles it? – zer00ne Commented Mar 25, 2018 at 0:49 - I just gave one object in the array as an example to show the structure, I didn't think it would be necessary to list out multiple objects. – quirky purple Commented Mar 25, 2018 at 0:50
- Possible duplicate of Merge/flatten an array of arrays in JavaScript? – BlackICE Commented Mar 25, 2018 at 0:58
6 Answers
Reset to default 2This solution uses reduce and forEach to bine all objects in sub-arrays into a Map, with part_d
as key, and the qty
the result of the bined quantities. When a new part_id
is found, a new object is created by spreading the part into a new object, and overriding qty
with 0.
The Map is then converted back to an array by spreading the Map.values()
iterator:
const data = [{"location":"somewhere","location_id":1,"parts":[{"part_id":1,"description":"foo","qty":1}]},{"location":"somewhere2","location_id":2,"parts":[{"part_id":1,"description":"foo","qty":3},{"part_id":2,"description":"bar","qty":1}]}];
const result = [...
data.reduce((r, { parts }) => {
(parts || []).forEach((o) => {
r.has(o.part_id) || r.set(o.part_id, { ...o, qty: 0 });
r.get(o.part_id).qty += o.qty;
});
return r;
}, new Map())
.values()];
console.log(result);
This adds all part objects into the base and removes the parts array. It will modify the old object.
let arr = [
{
location: 'somewhere',
location_id: 1,
parts: [
{
part_id: 1,
description: 'something'
}
]
}
]
arr.map(m => {
m.parts.forEach(p => Object.assign(m, p))
delete m.parts;
})
console.log(arr);
If you're sure you want to map the arrays and not the objects inside:
const input = [
{
location: 'somewhere',
location_id: 1,
parts: [
{
part_id: 1,
description: 'something'
}
]
},
{
location: 'somewhere2',
location_id: 22,
parts: [
{
part_id: 2,
description: 'something'
}
]
}
];
const mapped = input.map(outerObj => outerObj.parts[0]);
console.log(mapped);
You can use array#reduce
to first join all your parts
array of each object. Then use array#reduce
to group each object in the result array on part_id
and description
and sum up the qty
for each unique object. Then get all the values from this object.
const input = [ { location: 'somewhere', location_id: 1, parts: [ { part_id: 1, description: 'foo', qty: 1 } ] }, { location: 'somewhere2', location_id: 22, parts: [ { part_id: 2, description: 'something', qty: 3 } ] }, { location: 'somewhere2', location_id: 22, parts: [ { part_id: 2, description: 'something', qty: 4 } ] } ],
result = Object.values(input.reduce((r, {parts}) => r.concat(parts),[])
.reduce((r,o) => {
r[`${o.part_id}_${o.description}`] = r[`${o.part_id}_${o.description}`] || {...o, qty: 0};
r[`${o.part_id}_${o.description}`].qty += o.qty;
return r;
},{}));
console.log(result);
This question may have two scenarios :
each location have single object under parts array.
DEMO
let jsonObj = [
{
location: 'somewhere',
location_id: 1,
parts: [
{
part_id: 1,
description: 'foo',
qty: 1,
}
]
},
{
location: 'somewhere',
location_id: 2,
parts: [
{
part_id: 2,
description: 'foo',
qty: 1,
}
]
}
];
let res = jsonObj.map(obj => obj.parts[0]);
console.log(res);
Single location have multiple objects under parts array.
DEMO
let jsonObj = [
{
location: 'somewhere',
location_id: 1,
parts: [
{
part_id: 1,
description: 'foo',
qty: 1,
},
{
part_id: 2,
description: 'foo',
qty: 1,
}
]
}
];
let res = jsonObj[0].parts;
console.log(res);
I had a similar situation. What worked for me is defining a new array and then array.push(part)
from inside a double map function:
const input = [{
location: 'somewhere',
location_id: 1,
parts: [{
part_id: 1,
description: 'something'
},
{
part_id: 2,
description: 'something'
}
]
},
{
location: 'somewhere2',
location_id: 22,
parts: [{
part_id: 3,
description: 'something'
},
{
part_id: 4,
description: 'something'
}
]
}
];
if this is your input..
var list = []
input.map(item => {
item.parts.map(part => {
list.push(part);
});
});