How to groupBy an object key inside nested array of objects? - javascript

I have a nested array of objects and I want to groupBy id and form a new array. Here's my array:
mainArray = [
{ name: 'a',age: 10, company: [ { desc: 'test1' , id: 6 }, { desc: 'testa' , id: 10 }] },
{ name: 'b',age: 20, company: [ { desc: 'test2' , id: 30 }] },
{ name: 'c',age: 40, company: [ { desc: 'test3' , id: 10 }, { desc: 'testc' , id: 30 }] }
]
I can flatten the entire array but it doesn't seem like the right way to do it.
My new array should look like something like this:
result = [
comapny_6: [
{
name: 'a',
age: 10,
desc: 'test1'
},
],
comapny_10: [
{
name: 'a',
age: 10,
desc: 'testa'
},
{
name: 'c',
age: 40,
desc: 'test3'
}
],
company_30 :[
{
name: 'b',
age: 20,
desc: 'test2'
},
{
name: 'c',
age: 40,
desc: 'testc'
}
]
]
I am open to suggestions on how the final data structure should look like. The bottom line is I want groupBy id so that I have information about each company separated out.

You can use reduce to loop thru the array and construct the desired object output. Use forEach to loop thru company
var mainArray = [{"name":"a","age":10,"company":[{"desc":"test1","id":6},{"desc":"testa","id":10}]},{"name":"b","age":20,"company":[{"desc":"test2","id":30}]},{"name":"c","age":40,"company":[{"desc":"test3","id":10},{"desc":"testc","id":30}]}];
var result = mainArray.reduce((c, {name,age,company}) => {
company.forEach(({id,desc}) => (c["company_" + id] = c["company_" + id] || []).push({name,age,desc}));
return c;
}, {});
console.log(result);

You can first create a 1D array using flatMap() and then use reduce() to group
const mainArray = [
{ name: 'a',age: 10, company: [ { desc: 'test1' , id: 6 }, { desc: 'testa' , id: 10 }] },
{ name: 'b',age: 20, company: [ { desc: 'test2' , id: 30 }] },
{ name: 'c',age: 40, company: [ { desc: 'test3' , id: 10 }, { desc: 'testc' , id: 30 }] }
]
const flat = mainArray.flatMap(({company,...rest}) => company.map(a => ({...rest,...a})));
const res = flat.reduce((ac,{id,...rest}) => ((ac[`company_${id}`] || (ac[`company_${id}`] = [])).push(rest),ac),{})
console.log(res)
Explanation
reduce() is method with returns a single value after iterating through all the array. The accumulator i.e ac in above case is set to empty object {}(which is the second argument passed to function)
In each iteration we return the updated accumulator which becomes ac for next iteration. So what we return from function is following expression
((ac[`company_${id}`] || (ac[`company_${id}`] = [])).push(rest),ac)
ac[company_${id}] is using Bracket Notation which takes an expression company_${id}. It is same as
ac["company_" + id]
The above line checks if ac[company_${id}] exists in the ac then push() rest to the it.
If ac[company_${id}] is not created yet they set it to empty array [] then push() the rest to it.
The last part uses comma operator
((ac[`company_${id}`] || (ac[`company_${id}`] = [])).push(rest),ac)
The above whole expression will evaluate to the last value separated by comma , which is ac. So in each iteration we are pushing rest to the respective array and returning ac it the end. The code is equivalent to
const res = flat.reduce((ac,{id,...rest}) => {
//check if company id doesnot exist as key in ac then set it empty array
if(!ac[`company_${id}`]) ac[`company_${id}`] = [];
//push rest(which will be an object with all the keys expect id)
ac[`company_${id}`].push(rest)
//at last return ac
return ac;
})

You can achieve this with Array.reduce and inside it with an Array.forEach over the array of companies like this:
let data = [ { name: 'a',age: 10, company: [ { desc: 'test1' , id: 6 }, { desc: 'testa' , id: 10 }] }, { name: 'b',age: 20, company: [ { desc: 'test2' , id: 30 }] }, { name: 'c',age: 40, company: [ { desc: 'test3' , id: 10 }, { desc: 'testc' , id: 30 }] } ]
let result = data.reduce((r,{ name, age, company }) => {
company.forEach(({ id, desc }) =>
r[`company_${id}`] = (r[`company_${id}`] || []).concat({ name, age, desc }))
return r
}, {})
console.log(result)

Related

Compare Javascript Object field with another Object field [duplicate]

This question already has answers here:
Merge two array of objects based on a key
(23 answers)
Closed last year.
I have two Objects one of them has the Store Name and the other object has the Price for an item along with the Store ID on both objects. Such as;
obj1 = [
{id: 1,name: "Store1"},
{id: 2,name: "Store2"},
{id: 3,name: "Store3"}
];
obj2= [
{ id: 1, price: 100 },
{ id: 2, price: 200 },
{ id: 3, price: 300 }
];
What I want to achieve is that compare obj1 id with obj2 id if they are the same get the price and the store name from the same id. What is the best way to achieve this? I have been trying to use Array.map or filter but can't really make it work. Thank you!
You can use map & find
const obj1 = [{
id: 1,
name: "Store1"
},
{
id: 2,
name: "Store2"
},
{
id: 3,
name: "Store3"
},
{
id: 4,
name: "Store3"
}
];
const obj2 = [{
id: 1,
price: 100
},
{
id: 2,
price: 200
},
{
id: 3,
price: 300
}
];
const newData = obj1.map((item, index) => {
return {
...item,
// if the item exist in obj2 then get the price else assign empty string
price: obj2.find(elem => elem.id === item.id) ? .price || ''
}
});
console.log(newData)

find an object in Array of Array

if i want for an example loop Through this Array to find specific item in Items Array how to approach that? i made this logic but it doesn't work
DATA.map((D)=>{
return D.items.find((item)=>{
return item.name ==='Blue Beanie'
})
})
this is the Array plus how to create new ONE array includes the both of items arrays to be like that: items: [{
id: 1,
name: 'Brown Brim',
price: 25
},
{
id: 2,
name: 'Blue Beanie',
price: 18
},
{
id: 3,
name: 'Adidas NMD',
price: 220
},
{
id: 4,
name: 'Adidas Yeezy',
price: 280
}
]
const DATA= [
{
id: 1,
title: 'Hats',
routeName: 'hats',
items: [
{
id: 1,
name: 'Brown Brim',
price: 25
},
{
id: 2,
name: 'Blue Beanie',
price: 18
}
]
},
{
id: 2,
title: 'Sneakers',
routeName: 'sneakers',
items: [
{
id: 3,
name: 'Adidas NMD',
price: 220
},
{
id: 4,
name: 'Adidas Yeezy',
price: 280
}
]
}
];
Transform DATA into list of items and find from that list your expected item
const res = DATA.flatMap((D) => D.items).find(
(item) => item.name === "Brown Brim"
)
const DATA = [
{
id: 1,
title: "Hats",
routeName: "hats",
items: [
{
id: 1,
name: "Brown Brim",
price: 25,
},
{
id: 2,
name: "Blue Beanie",
price: 18,
},
],
},
{
id: 2,
title: "Sneakers",
routeName: "sneakers",
items: [
{
id: 3,
name: "Adidas NMD",
price: 220,
},
{
id: 4,
name: "Adidas Yeezy",
price: 280,
},
],
},
]
const res = DATA.flatMap((D) => D.items).find(
(item) => item.name === "Brown Brim"
)
console.log(res)
Reference
Array.prototype.flatMap()
Maybe this is helpful?
const DATA= [
{id: 1,title:'Hats',routeName:'hats',
items:[{id: 1,name:"Brown Brim",price:25},
{id: 2,name: 'Blue Beanie',price: 18}]},
{id: 2,title: 'Sneakers',routeName: 'sneakers',
items: [{id: 3,name: 'Adidas NMD',price: 220},
{id: 4,name: 'Adidas Yeezy',price: 280}]}
];
console.log(DATA.map(D=>D.items.find(item=>item.name==='Brown Brim'))
.filter(e=>e))
The .map returns either an element matching your criterion or undefined, The chained .filter then removes all "falsy" elements, i. e. all the undefined ones.
As for the first question "loop Through this Array to find a specific item in Items Array"
given it is not sorted in any way, this can be done by iterating over the DATA array and search inside the items
If want to have access to the item from the outside of the 'forEach' scope you have to declare the variable outside
Regarding the second question, use the reduce function while iterating the array
NOTE: You can obviously combine both tasks as you already iterate through the array, so no need to do it twice. But to avoid confusion, I separated the logic.
Also, if you do choose to combine the tasks, using the reduce is not relevant, but very much like the answer to the first question, you can declare a buffer such as an array, and just copy items to it on the go (I'll leave out questions on performance for that matter)
const DATA = [
{
id: 1, title: 'Hats', routeName: 'hats',
items: [
{id: 1,name: 'Brown Brim',price: 25},
{id: 2,name: 'Blue Beanie',price: 18}
]
},
{
id: 2, title: 'Sneakers', routeName: 'sneakers',
items: [
{id: 3,name: 'Adidas NMD',price: 220},
{id: 4,name: 'Adidas Yeezy',price: 280}
]
}
];
//Question 1
//creating the object that will hold the item
//outside of the 'forEach' scope so we can use it later
let res = {};
const searchRes = DATA.forEach(entry => {
entry.items.forEach(item => {
if (item.name === 'Brown Brim')
//duplicating the item to a variable declared outside of this scope
res = { ...item
};
});
});
console.log(`Q1 - The item we found:`);
console.log(res);
// Question 2
// Merging all object inside the 'items' array using Reduce
const merged = DATA.reduce((acc, curr) =>
acc.concat(curr.items), []);
console.log(`Q2 - The merged array:`);
console.log(merged)

Find Length of Object and then map over it Javascript

I have an object with a array of data objects, I want to map over them and then use the result in an if statement to flatten:
outsideData = {
id: 1,
city: 'London'
data: [
{
name: 'Test 1',
age: 15,
value: 20
},
{
name: 'Test 2',
age: 20,
value: 26
},
{
name: 'Test 3',
age: 31,
value: 45
},
{
name: 'Test 4',
age: 45,
value: 79
}]
}
And a have another object I want to add if the length is something
const address = {
value: 2
}
const city = {
value: 10
}
Now I want to check if the length of the data array
const myData = outsideData.data
If the length of the data is less than 3 add this object if greater than three had this object
if (outsideData.data.length <= 3) {
.map((d) => {
return [
{
name: d,
age: true
},
address
]
}).flat(1)
} else {
.map((name) => {
return [
{
name: d,
age: true
},
city
]
}
}
Something like this, how can I achieve mapping if it has a length that a specific size.
Thank you,
to get the count of properties in an object. you can use Object.keys(); to get an array from an object... which has a length property. (i don't think keys guarantees order being preserved - but that's not relevant to your question.
const newData = data.map(d=>{
if(Object.keys(d).length <= 3){
return {...d, address}; //98% sure on this syntax;
}else{return d;}
});
even more concisely
const newData = data.map(d=>Object.keys(d).length <= 3 ?{...d, address}:d);

Map over two arrays to see if one property matches, then push specific info into the first array

New to javascript and trying to learn! I am trying to map through two array of objects, and if a certain property matches, pull in specific information into the first array.
let result;
let arrNames = [{
id: 10
name: "A"
}, {
id: 11,
name: "B"
}, {
id: 12,
name: "C"
}, }, {
id: 13,
name: "A"
}, {
id: 14,
name: "B"
}]
let arrInfo = [{
name: "A",
info: "AAA"
}, {
name: "B",
info: "BBB"
}, {
name: "C",
info: "CCC"
}]
If arrNames.name == arrInfo.name, I would like push info into the names array.
Desired result:
let arrNames = [{
id: 10
name: "A",
info: "AAA"
}, {
id: 11,
name: "B",
info: "BBB"
}, {
id: 12,
name: "C",
info: "CCC"
}, }, {
id: 13,
name: "A",
info: "AAA"
}, {
id: 14,
name: "B",
info: "BBB"
}]
What I've tried:
const res = arrInfo.map((el, index) => {
if(el.name == arrNames[index].name)
arrNames.push(el.info)
}
^ This obviously doesn't work -- but I'm wondering if extend or push would be appropriate here.
Thanks in advance for your help (apologies that this is probably a dupe).
Convert arrInfo to a Map, with the name as the key. Now map arrNames and add the info you get from arrInfoMap using the name. Use object spread to combine both objects:
const arrNames = [{"id":10,"name":"A"},{"id":11,"name":"B"},{"id":12,"name":"C"},{"id":13,"name":"A"},{"id":14,"name":"B"}]
const arrInfo = [{"name":"A","info":"AAA"},{"name":"B","info":"BBB"},{"name":"C","info":"CCC"}]
const arrInfoMap = new Map(arrInfo.map(o => [o.name, o]))
const result = arrNames.map(o => ({ ...o, ...arrInfoMap.get(o.name) }))
console.log(result)
You can do something like this:
let arrNames = [
{
id: 10,
name: 'A'
},
{
id: 11,
name: 'B'
},
{
id: 12,
name: 'C'
},
{
id: 13,
name: 'A'
},
{
id: 14,
name: 'B'
}
];
let arrInfo = [
{
name: 'A',
info: 'AAA'
},
{
name: 'B',
info: 'BBB'
},
{
name: 'C',
info: 'CCC'
}
];
// do this
const result = arrNames.map((item) => {
const newItem = item; // here we define a new object that is the same as your object that is currently looped up to in your arrNames array
// loop your second array over this currently looped to object, seeing if the name matches
arrInfo.forEach((item2) => {
if (item.name === item2.name) {
newItem.info = item2.info; // if they do set a new property for your new object called info as the info from item 2 of this arrInfo array
}
});
// return this new object whether or not there was a match for the name property
return newItem;
});
console.log(result);
So the thing with your map method is that you need to remember to return something at the end of your callback function. You are simply pushing to an array, which is like using .map as a forEach. Map makes one array into another array of the same length. Here you are trying to make a new array where the array element being looped over will have an extra info property should it match your second array arrInfo's name.
So you what you can do is a forEach inside your map to check if they match, if so add a new property to your arrayNames element and return that as the new element for your newly created array. Hope that helped, please ask for clarifications if you need in the comments.

Map array inside object inside array into a single 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 combine the quantity.
Open to solutions using es6 but needs to not modify original array
This solution uses reduce and forEach to combine all objects in sub-arrays into a Map, with part_d as key, and the qty the result of the combined 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);
});
});

Categories

Resources