Collect array into sub array buckets based on value (which is array in itself ) - javascript

I have an array of Javascript objects like below.
[
{
email: 'alex#test.com',
fn: 'Alex',
sn: 'McPherson',
phone: '01233xxxxx',
hours: '40',
rate: '20',
amount: '200',
vat: '60',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '2345',
area: 'uhj'
}
]
}
},
{
email: 'mike#test.com',
fn: 'Mike',
sn: 'Mann',
phone: '01233xxxxx',
hours: '50',
rate: '70',
amount: '500',
vat: '90',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '3456',
area: 'uio'
}
]
}
},
{
email: 'fred#test.com',
fn: 'Fred',
sn: 'Frogg',
phone: '01233xxxxx',
hours: '80',
rate: '90',
amount: '800',
vat: '100',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '4567',
area: 'asdaf'
},
{
id: '3456',
area: 'uio'
}
]
}
},
{
email: 'alex#test.com',
fn: 'Alex',
sn: 'McPherson',
phone: '01233xxxxx',
hours: '90',
rate: '30',
amount: '900',
vat: '120',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '4567',
area: 'asdaf'
},
{
id: '5678',
area: 'asdf'
}
]
}
}
]
What I ideally want is to group those of the same value (shipping.addresses.id) into there own sub array of objects. Expected outcome.
[
{
id: '1234',
area: 'xzy',
data: [
{
email: 'alex#test.com',
fn: 'Alex',
sn: 'McPherson',
phone: '01233xxxxx',
hours: '40',
rate: '20',
amount: '200',
vat: '60',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '2345',
area: 'uhj'
}
]
}
},
{
email: 'mike#test.com',
fn: 'Mike',
sn: 'Mann',
phone: '01233xxxxx',
hours: '50',
rate: '70',
amount: '500',
vat: '90',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '3456',
area: 'uhj'
}
]
}
}
]
},
{
id: '2345',
area: 'uhj',
data: [
{
email: 'alex#test.com',
fn: 'Alex',
sn: 'McPherson',
phone: '01233xxxxx',
hours: '40',
rate: '20',
amount: '200',
vat: '60',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '2345',
area: 'uio'
}
]
}
}
]
},
{
id: '3456',
area: 'uio',
data: [
{
email: 'mike#test.com',
fn: 'Mike',
sn: 'Mann',
phone: '01233xxxxx',
hours: '50',
rate: '70',
amount: '500',
vat: '90',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '1234',
area: 'xzy'
},
{
id: '3456',
area: 'uio'
}
]
}
},
{
email: 'fred#test.com',
fn: 'Fred',
sn: 'Frogg',
phone: '01233xxxxx',
hours: '80',
rate: '90',
amount: '800',
vat: '100',
agency: 'test',
start: '08/06/2017',
end: '10/06/2017',
shipping: {
addresses: [
{
id: '4567',
area: 'asdaf'
},
{
id: '3456',
area: 'uio'
}
]
}
}
]
}
]
I can group the input array using a specific attribute using particular key (code below) but I can't seem to get my head around resorting the array based on a key which is array in itself.
Array.from(
data.reduce(
(acc, o) => (acc.get(o.email).push(o), acc),
new Map(data.map( o => [o.email, []] ))
), ([key, value]) => value
)

You can reduce the data array into an object using shipping.addresses.id as keys and return an array using Object.values(). You will need to iterate over the addresses array of each object and create an entry for each id as they are encountered and push to these entries for subsequent elements with the same id.
const byAddressId = Object.values(
data.reduce((a, o) => {
o.shipping.addresses.forEach(({id, area}) => {
a[id] = {...a[id] ?? {id: id, area: area, data: []}};
a[id]['data'].push({...o});
});
return a;
}, {}));
const data = [{"email": "alex#test.com","fn": "Alex","sn": "McPherson","phone": "01233xxxxx","hours": "40","rate": "20","amount": "200","vat": "60","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": { "addresses": [ { "id": "1234", "area": "xzy" }, { "id": "2345", "area": "uhj" } ]}},{"email": "mike#test.com","fn": "Mike","sn": "Mann","phone": "01233xxxxx","hours": "50","rate": "70","amount": "500","vat": "90","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": { "addresses": [ { "id": "1234", "area": "xzy" }, { "id": "3456", "area": "uio" } ]}},{"email": "fred#test.com","fn": "Fred","sn": "Frogg","phone": "01233xxxxx","hours": "80","rate": "90","amount": "800","vat": "100","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": { "addresses": [ { "id": "4567", "area": "asdaf" }, { "id": "3456", "area": "uio" } ]}},{"email": "alex#test.com","fn": "Alex","sn": "McPherson","phone": "01233xxxxx","hours": "90","rate": "30","amount": "900","vat": "120","agency": "test","start": "08/06/2017","end": "10/06/2017","shipping": { "addresses": [ { "id": "4567", "area": "asdaf" }, { "id": "5678", "area": "asdf" } ]}}];
// return array of Object.values from the accumulator
const byAddressId = Object.values(
// reduce the data array into an object with shipping.addresses.id as keys
data.reduce((a, o) => {
// iterate over all addresses for each element
o.shipping.addresses.forEach(({id, area}) => {
// check if an id entry exists, otherwise create one
a[id] = {...a[id] ?? {id: id, area: area, data: []}};
// push the object to the data array of the id object
a[id]['data'].push({...o});
});
return a;
}, {}));
console.log(byAddressId);
That being said, you can use this same method to save yourself two map() calls compared to the group by email example you included in your question.
const byEmail = Object.values(
data.reduce((a, o) => (a[o.email] = [...a[o.email] ?? [], {...o}], a), {}));

Related

lodash filter using forEach?

Having trouble filtering out some data from a deeply nested array of objects.
I need to loop through each day, then loop through each event and filter out all events that don't include an attendee with the email 'barry#gmail.com'.
'18 Sun': [
{
name: 'Event 01',
attendees: [
{
name: 'John',
email: 'john#gmail.com'
},
{
name: 'Barry',
email: 'barry#gmail.com'
}
]
}
],
'19 Mon':[
{
name: 'Event 02',
attendees: [
{
name: 'John',
email: 'john#gmail.com'
}
]
},
{
name: 'Event 03',
attendees: [
{
name: 'John',
email: 'john#gmail.com'
},
{
name: 'Barry',
email: 'barry#gmail.com'
}
]
}
]
expectation after filtering (removed Event 02 since barry#gmail.com does not exist)
'18 Sun': [
{
name: 'Event 01',
attendees: [
{
name: 'John',
email: 'john#gmail.com'
},
{
name: 'Barry',
email: 'barry#gmail.com'
}
]
}
],
'19 Mon':[
{
name: 'Event 03',
attendees: [
{
name: 'John',
email: 'john#gmail.com'
},
{
name: 'Barry',
email: 'barry#gmail.com'
}
]
}
]
I've tried this snippet
_.forEach(globalEvents, d => {
_.forEach(d, e => {
_.filter(e.attendees, p => {
return p.email === 'barry#gmail.com'
})
})
})
but this just returns the entire, unfiltered array. I would like to use lodash, but isn't necessary.
Try this code it's works..
_.forEach(data, d => {
_.remove(d, e => {
return _.some(e.attendees, {email: 'barry#gmail.com'}) != true;
})
});

How to create a nested array of object from an array of objects

How Can I loop through this array of objects and change it so that the individual menu items are nested in the object menu_name?
const menus = [
{ menu_name: 'Entre', id:0 },
{
name: 'Soup',
price: 14.99,
id:1
},
{
name: 'Chips & Salsa',
price: 7.99,
id:2
},
{
name: 'Chicken Nuggets',
price: 12.99,
id:3
},
{ menu_name: 'Sides', id:4 },
{
name: 'Fries',
price: 4.99,
id:5
},
{
name: 'Drinks',
price: 2.99,
id:6
},
{
name: 'Onion Rings',
price: 5.99,
id:7
},
];
the end result should look like this for each menu_name object, where an array of menus is nested in the menu_name object
{
menu_name: 'Sides',
menu: [
{
name: 'Fries',
price: 4.99,
},
{
name: 'Drinks',
price: 2.99,
},
{
name: 'Onion Rings',
price: 5.99,
},
],
},
You can easily achieve this using reduce and object destructuring
const menus = [
{ menu_name: "Entre", id: 0 },
{
name: "Soup",
price: 14.99,
id: 1,
},
{
name: "Chips & Salsa",
price: 7.99,
id: 2,
},
{
name: "Chicken Nuggets",
price: 12.99,
id: 3,
},
{ menu_name: "Sides", id: 4 },
{
name: "Fries",
price: 4.99,
id: 5,
},
{
name: "Drinks",
price: 2.99,
id: 6,
},
{
name: "Onion Rings",
price: 5.99,
id: 7,
},
];
const result = menus.reduce((acc, curr) => {
const { menu_name } = curr;
if (menu_name) {
acc.push({ menu_name, menu: [] });
} else {
const { name, price } = curr;
acc[acc.length - 1].menu.push({ name, price });
}
return acc;
}, []);
console.log(result);
var newMenu = [];
menus.forEach(menu=>{
if(menu.menu_name){
newMenu.push({...menu, menu: []})
}else{
newMenu[newMenu.length-1].menu.push(menu)
}
});

Grouping and summing array objects

I'm having a data sample like this
this.userData = [
{id:1, category: 'Food', amount: 30, pDate: '2021-01-13', description: 'test desc'},
{id:2, category: 'Fuel', amount: 10, pDate: '2021-01-12', description: 'test desc'},
{id:3, category: 'Food', amount: 70, pDate: '2021-01-14', description: 'test desc'},
]
What I want to achieve with this data is to group it and sum it up so it comes out like this
[
{name: Food, total: 100},
{name: Fuel, total: 30}
]
What the current code I have, I do not get the output as I want.
const data = this.userData;
const groups = data.reduce((groups, item) => ({
...groups,
[item.category]: [...(groups[item.category] || []), item]
}), {});
console.log(groups);
You could take an object for grouping and get the values.
const
userData = [{ id:1, category: 'Food', amount: 30, pDate: '2021-01-13', description: 'test desc' }, { id:2, category: 'Fuel', amount: 10, pDate: '2021-01-12', description: 'test desc' }, { id:3, category: 'Food', amount: 70, pDate: '2021-01-14', description: 'test desc' }],
groups = Object.values(userData.reduce((r, o) => {
(r[o.category] ??= { name: o.category, total: 0 }).total += o.amount;
return r;
}, {}))
console.log(groups);
Try this
const userData = [
{id:1, category: 'Food', amount: 30, pDate: '2021-01-13', description: 'test desc'},
{id:2, category: 'Fuel', amount: 10, pDate: '2021-01-12', description: 'test desc'},
{id:3, category: 'Food', amount: 70, pDate: '2021-01-14', description: 'test desc'},
]
const hashMap = {}
for (const { category, amount } of userData) {
if (hashMap[category]) {
hashMap[category].total += amount
} else {
hashMap[category] = { name: category, total: amount }
}
}
const output = Object.values(hashMap)
console.log(output)

Filter array of objects by nested array of object value

Suppose I have an array of objects, each of which contains a nested array of objects likes:
[
{
id: 1,
title: 'Lorem',
private: false,
likes: [
{ id: 01, username: 'mike' },
{ id: 02, username: 'john' }
]
},
{
id: 2,
title: 'Ipsum',
private: true,
likes: [
{ id: 03, username: 'steve' },
{ id: 04, username: 'mike' },
{ id: 05, username: 'sara' }
]
},
{
id: 3,
title: 'Dolor',
private: false,
likes: [
{ id: 06, username: 'brad' },
{ id: 07, username: 'cameron' },
{ id: 08, username: 'liam' }
]
}
]
How can I filter the outer array by the username value inside its items nested array? Let's say output the items that contains in its inner arrays objects with property username: 'mike'?
You could filter by looking into the nested array.
var data = [{ id: 1, title: 'Lorem', private: false, likes: [{ id: 01, username: 'mike' }, { id: 02, username: 'john' }] }, { id: 2, title: 'Ipsum', private: true, likes: [{ id: 03, username: 'steve' }, { id: 04, username: 'mike' }, { id: 05, username: 'sara' }] }, { id: 3, title: 'Dolor', private: false, likes: [{ id: 06, username: 'brad' }, { id: 07, username: 'cameron' }, { id: 08, username: 'liam' }] }],
result = data.filter(({ likes }) => likes.some(({ username }) => username === 'mike'));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Use filter and some functions:
const input = [{
id: 1,
title: "Lorem",
private: false,
likes: [{
id: 1,
username: "mike"
}, {
id: 2,
username: "john"
}]
},
{
id: 2,
title: "Ipsum",
private: true,
likes: [{
id: 3,
username: "steve"
},
{
id: 4,
username: "mike"
},
{
id: 5,
username: "sara"
}
]
},
{
id: 3,
title: "Dolor",
private: false,
likes: [{
id: 6,
username: "brad"
},
{
id: 7,
username: "cameron"
},
{
id: 8,
username: "liam"
}
]
}
];
const criteria = {
username: "mike"
};
const result = input.filter(x =>
x.likes.some(y => y.username === criteria.username)
);
console.log(result);
you can use 'map' function to filter the internal array
const data = [
{
id: 1,
title: 'Lorem',
private: false,
likes: [
{ id: 1, username: 'mike' },
{ id: 2, username: 'john' }
]
},
{
id: 2,
title: 'Ipsum',
private: true,
likes: [
{ id: 3, username: 'steve' },
{ id: 4, username: 'mike' },
{ id: 5, username: 'sara' }
]
},
{
id: 3,
title: 'Dolor',
private: false,
likes: [
{ id: 6, username: 'brad' },
{ id: 7, username: 'cameron' },
{ id: 8, username: 'liam' }
]
}
]
const res = data.map( item => {
let likes = item.likes.filter( user => user.username === 'mike')
if(likes.length > 0 ) {
item.likes = likes
return item
}
return null
}).filter(item => item !== null )
console.log(res)
use filter and find in likes.
const filter = (data, name) =>
data.filter(({ likes }) => likes.find(({ username }) => username === name));
const data = [
{
id: 1,
title: "Lorem",
private: false,
likes: [
{ id: 01, username: "mike" },
{ id: 02, username: "john" },
],
},
{
id: 2,
title: "Ipsum",
private: true,
likes: [
{ id: 03, username: "steve" },
{ id: 04, username: "mike" },
{ id: 05, username: "sara" },
],
},
{
id: 3,
title: "Dolor",
private: false,
likes: [
{ id: 06, username: "brad" },
{ id: 07, username: "cameron" },
{ id: 08, username: "liam" },
],
},
];
console.log(filter(data, "mike"));
console.log(filter(data, "blah"));

JavaScript - Flatten Array of Nested Objects

I am working with an array of objects and need to flatten and re-assign the data set into an array of single objects using the object values as the new object keys.
Incoming Data:
results = [
{
surveyValues: [
{ id: 135, name: 'First Name', value: 'Jim' },
{ id: 136, name: 'Last Name', value: 'Jones' },
{ id: 137, name: 'City', value: 'Fredsburg' },
],
},
{
surveyValues: [
{ id: 135, name: 'First Name', value: 'Greg' },
{ id: 136, name: 'Last Name', value: 'Jones' },
{ id: 137, name: 'City', value: 'Waverly' },
],
},
];
Desired Result:
output = [
{
id: 1, FirstName: 'Jim', LastName: 'Jones', City: 'Fredsburg',
},
{
id: 2, FirstName: 'Greg', LastName: 'Jones', City: 'Waverly',
},
];
My current attempt gets me to placing the updated key/value pairs in the correct order, but creates a huge array of single objects.
Current Code:
const results = [];
surveyResults.map(s => s.surveyValues)
.forEach(s => Object.entries(s)
.forEach(([key, value]) => {
Object.entries(value)
.forEach(([k, v]) => {
if (k === 'name') {
results.push({
id: value.id,
[v]: value.value.toString(),
});
}
});
}));
Current Code Output:
[
{
id: 135, FirstName: 'Jim',
},
{
id: 136, LastName: 'Jones',
},
{
id: 137, City: 'Fredsburg',
},
{
id: 135, FirstName: 'Greg',
},
{
id: 136, LastName: 'Jones',
},
{
id: 137, City: 'Waverly',
},
]
What am I missing and why is my code not created the new arrays of objects as desired?
You may use map and reduce to accomplish this. I'm also accessing an index i in the map function's callback to set the id property and string.replace() to strip the space from the FirstName and LastName keys.
const results = [
{
surveyValues: [
{ id: 135, name: 'First Name', value: 'Jim' },
{ id: 136, name: 'Last Name', value: 'Jones' },
{ id: 137, name: 'City', value: 'Fredsburg' },
],
},
{
surveyValues: [
{ id: 135, name: 'First Name', value: 'Greg' },
{ id: 136, name: 'Last Name', value: 'Jones' },
{ id: 137, name: 'City', value: 'Waverly' },
],
},
];
const result = results.map((e, i) =>
e.surveyValues.reduce((a, e) => {
a[e.name.replace(" ", "")] = e.value;
return a;
}, {id: i + 1})
);
console.log(result);
Create each object in the outer map() loop, and add properties to it in the inner forEach().
surveyResults = [
{
surveyValues: [
{ id: 135, name: 'First Name', value: 'Jim' },
{ id: 136, name: 'Last Name', value: 'Jones' },
{ id: 137, name: 'City', value: 'Fredsburg' },
],
},
{
surveyValues: [
{ id: 135, name: 'First Name', value: 'Greg' },
{ id: 136, name: 'Last Name', value: 'Jones' },
{ id: 137, name: 'City', value: 'Waverly' },
],
},
];
const results = surveyResults.map((s, i) => {
let v = s.surveyValues;
let obj = {id: i};
v.forEach(item => obj[item.name.replace(/\s+/g, "")] = item.value.toString());
return obj;
});
console.log(results);

Categories