How to Group JavaScript Array of Object based on key - javascript

So I have a data like this
const carts = [
{
name: 'Voucher A',
participants: [
{
date: 112
},
{
date: 112
}
],
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
},
{
name: 'Voucher B',
participants: [
{
date: 111
},
{
date: 112
}
],
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
}
]
And I want to group it based on the date (if it has same date). So for data above, the expected result will be
expected = [
{
name: 'Voucher A',
date: 1,
count: 1,
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
},
{
name: 'Voucher A',
date: 2,
count: 1,
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
}
]
Because it has different date. But if it has same date, the expected result will be
expected = [
{
name: 'Voucher A',
date: 1,
count: 2,
supplierName: 'ABC',
ticketDescription: 'Description of',
...data
}
]
I was trying to use reduce to group it but it did not give the structure I want
carts.forEach(cart => {
cart.participants.reduce((acc, obj) => {
acc[obj.date] = [...acc[obj.date] || [], obj]
return acc
}, {})
})

To organize the data, I think you need two associations to group by: the name and the dates and their counts for that name:
const carts = [
{
name: 'Voucher A',
participants: [
{
date: 1
},
{
date: 2
}
]
}
];
const groupedByNames = {};
for (const { name, participants } of carts) {
if (!groupedByNames[name]) groupedByNames[name] = {};
for (const { date } of participants) {
groupedByNames[name][date] = (groupedByNames[name][date] || 0) + 1;
}
}
const output = Object.entries(groupedByNames).flatMap(
([name, dateCounts]) => Object.entries(dateCounts).map(
([date, count]) => ({ name, date: Number(date), count })
)
);
console.log(output);

If you want use, just plain for loops, you can try this solution. It looks simple and elegant 😜😜
const carts = [
{
name: 'Voucher A',
participants: [
{
date: 1
},
{
date: 1
},
{
date: 2
}
]
},
{
name: 'Voucher B',
participants: [
{
date: 1
},
{
date: 2
},
{
date: 2
}
]
}
]
const finalOutput = []
for (const cart of carts) {
for (const participant of cart.participants) {
const res = finalOutput.find(e => e.name === cart.name && e.date === participant.date)
if (res) {
res.count += 1
} else {
finalOutput.push({ name: cart.name, date: participant.date, count: 1 })
}
}
}
console.log(finalOutput)

Use forEach and destructuring
const process = ({ participants, name }) => {
const res = {};
participants.forEach(({ date }) => {
res[date] ??= { name, count: 0, date };
res[date].count += 1;
});
return Object.values(res);
};
const carts = [
{
name: "Voucher A",
participants: [
{
date: 1,
},
{
date: 2,
},
],
},
];
console.log(carts.flatMap(process));
const carts2 = [
{
name: "Voucher A",
participants: [
{
date: 1,
},
{
date: 1,
},
],
},
];
console.log(carts2.flatMap(process));

Related

Filter array of objects by another object's values

I would like to filter an array of objects based on the values in another object. I am trying to map() the array and filter() inside the function so that I am able to get a new array with only the desired (active) fields.
I am trying to get the filter on the Object.entries(), I try to compare the keys and check if the values of the active filters are true, but I am not getting it right.
const records = [
{
id: 1,
name: "first",
date: "05/02"
},
{
id: 2,
name: "second",
date: "06/02"
},
{
id: 3,
name: "third",
date: "07/02"
},
{
id: 4,
name: "fourth",
date: "08/02"
}
];
const active = {
id: true,
name: false,
date: true
};
const result = records.map((record) => {
return Object.entries(record).filter((entry) => {
Object.entries(active).forEach((activeEntry) => {
return activeEntry[1] && activeEntry[0] === entry[0];
});
});
});
console.log(result);
This is the desired outcome
const desiredOutcome = [
{
id: 1,
date: "05/02"
},
{
id: 2,
date: "06/02"
},
{
id: 3,
date: "07/02"
},
{
id: 4,
date: "08/02"
}
];
You can filter over the entries of the object and convert it back to an object with Object.fromEntries.
const records=[{id:1,name:"first",date:"05/02"},{id:2,name:"second",date:"06/02"},{id:3,name:"third",date:"07/02"},{id:4,name:"fourth",date:"08/02"}];
const active = {
id: true,
name: false,
date: true
};
const res = records.map(x =>
Object.fromEntries(
Object.entries(x).filter(([k])=>active[k])));
console.log(res);
Simply filter the keys by the key existing and then use Object.fromEntries to go back to an object
const records = [
{
id: 1,
name: "first",
date: "05/02"
},
{
id: 2,
name: "second",
date: "06/02"
},
{
id: 3,
name: "third",
date: "07/02"
},
{
id: 4,
name: "fourth",
date: "08/02"
}
];
const active = {
id: true,
name: false,
date: true
};
const result = records.map((record) => {
return Object.fromEntries( Object.entries(record).filter(([key,value]) => active[key]));
});
console.log(result);

Reduce array of objects without using .push()

I have this array of objects:
[
{
user: 'User_1',
date: 1603926000000,
count: 3,
},
{
user: 'User_2',
date: 1603926000000,
count: 10,
},
{
user: 'User_2',
date: 1604876400000,
count: 1,
},
]
I reduce it with this function:
const reducedDataByDate = dataByDate.reduce((acc, d) => {
const foundUser = acc.find((a) => a.user === d.user)
const value = { date: formatDate(d.date), count: d.count }
if (!foundUser) {
acc.push({ user: d.user, data: [value] })
} else {
foundUser.data.push(value)
}
return acc
}, [])
with this outcome:
[
{
user: 'User_1',
data: [
{
date: '2020-10-29',
count: 10,
},
{
date: '2020-11-09',
count: 1,
},
],
},
{
user: 'User_2',
data: [
{
date: '2020-10-29',
count: 3,
},
],
},
]
Ideally, I would like get rid of pushing values to original acc and foundUser array but have little idea how to go about that. Any input is much appreciated!
You could collect the data with a Map and get the wanted format from it.
const
data = [{ user: 'User_1', date: 1603926000000, count: 3 }, { user: 'User_2', date: 1603926000000, count: 10 }, { user: 'User_2', date: 1604876400000, count: 1 }],
result = Array.from(
data.reduce(
(m, { user, ...o }) => m.set(user, [...(m.get(user) || []), o]),
new Map
),
([user, data]) => ({ user, data })
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

find() inside reduce() method returns undefined

I have two arrays of objects; districts and userCounts. I am trying to reduce districts and find userCounts inside reduce
const result = districts.reduce((acc, curr) => {
const findUser = userCounts.find(({ _id }) => _id === curr._id)
console.log(findUser)
})
all findUser is returning undefined
districts:
[
{
_id: '5efc41d74664920022b6c016',
name: 'name1'
},
{
_id: '5efc41a44664920022b6c015',
name: 'name2'
},
{
_id: '5efc2d84caa7964dcd843a7b',
name: 'name3'
},
{
_id: '5efc41794664920022b6c014',
name: 'name 4'
}
]
userCounts:
[
{ _id: '5efc2d84caa7964dcd843a7b', totalCount: 3 },
{ _id: '5efc41794664920022b6c014', totalCount: 1 }
]
well .filter() with .every() is better use case here
let districts = [
{
_id: "5efc41d74664920022b6c016",
name: "name1",
},
{
_id: "5efc41a44664920022b6c015",
name: "name2",
},
{
_id: "5efc2d84caa7964dcd843a7b",
name: "name3",
},
{
_id: "5efc41794664920022b6c014",
name: "name 4",
},
];
let userCounts = [
{ _id: "5efc2d84caa7964dcd843a7b", totalCount: 3 },
{ _id: "5efc41794664920022b6c014", totalCount: 1 },
];
const result = districts.filter((dist) => {
return userCounts.some(({ _id }) => _id === dist._id);
});
console.log(result);
const districs=[
{
_id: '5efc41d74664920022b6c016',
name: 'name1'
},
{
_id: '5efc41a44664920022b6c015',
name: 'name2'
},
{
_id: '5efc2d84caa7964dcd843a7b',
name: 'name3'
},
{
_id: '5efc41794664920022b6c014',
name: 'name 4'
}
]
const userCounts= [
{ _id: '5efc2d84caa7964dcd843a7b', totalCount: 3 },
{ _id: '5efc41794664920022b6c014', totalCount: 1 }
]
let filtered=userCounts.map(item=>{
return districs.find(elemnt=>elemnt._id===item._id)
})
console.log(filtered)
here you go, you can modify it however you want.
try this if you want to use reduce and modify your array:
const result = districts.reduce((acc, curr) => {
const findUser = userCounts.filter(({ _id }) => _id === curr._id)
return [...acc, {...curr , user:findUser.length > 0 ? findUser[0].totalCount :0 }]
},[])
const districts = [
{
_id: '5efc41d74664920022b6c016',
name: 'name1'
},
{
_id: '5efc41a44664920022b6c015',
name: 'name2'
},
{
_id: '5efc2d84caa7964dcd843a7b',
name: 'name3'
},
{
_id: '5efc41794664920022b6c014',
name: 'name 4'
}
]
const userCounts = [
{ _id: '5efc2d84caa7964dcd843a7b', totalCount: 3 },
{ _id: '5efc41794664920022b6c014', totalCount: 1 }
]
const result = districts.reduce((acc, curr) => {
const findUser = userCounts.filter(({ _id }) => _id === curr._id)
return [...acc, {...curr , user:findUser.length > 0 ? findUser[0].totalCount :0 }]
},[])
console.log(result)

Filter array inside array

I have the array as below
test_list = [
{
id: 1,
test_name: 'Test 1',
members: [
{
user_id: 3
},
{
user_id: 4
}
],
},
{
id: 2,
test_name: 'Test 2',
members: [
{
user_id: 4
},
{
user_id: 5
},
],
},
{
id: 3,
test_name: 'Test 2',
members: [
{
user_id: 8
},
{
user_id: 10
},
],
}
]
I want to filter the test for specific user_id, example if user_id = 4 I would like to have this result
{
id: 1,
...
},
{
id: 2,
...
},
I have tried with this but it only return the member
test_list.filter(function(item) {
item.members.filter(function(member) {
if(member.user_id === 4) {
return item;
}
});
})
Would anyone please help me in this case?
Check if .some of the objects in the members array have the user_id you're looking for:
test_list = [{
id: 1,
test_name: 'Test 1',
members: [{
user_id: 3
},
{
user_id: 4
}
],
},
{
id: 2,
test_name: 'Test 2',
members: [{
user_id: 4
},
{
user_id: 5
},
],
},
{
id: 3,
test_name: 'Test 2',
members: [{
user_id: 8
}]
}
];
const filtered = test_list.filter(
({ members }) => members.some(
({ user_id }) => user_id === 4
)
);
console.log(filtered);
You could use .reduce() and .filter() method of array to achieve required result.
Please check below working code snippet:
const arr = [{"id":1,"test_name":"Test 1","members":[{"user_id":3},{"user_id":4}]},{"id":2,"test_name":"Test 2","members":[{"user_id":4},{"user_id":5}]},{"id":3,"test_name":"Test 2","members":[{"user_id":8}]}];
const data = arr.reduce((r,{ members,...rest }) => {
let rec = members.filter(o => o.user_id === 4)
if(rec.length){
rest.members = rec;
r.push(rest);
}
return r;
},[]);
console.log(data);
Hope this works.
var members = item.members;
var filterById =members.filter((item1)=>{
return (item1.user_id===4)
});
return filterById.length > 0;
});
console.log(test_List_by_id)```

Transform the data

I have the following data structure:
const data = [
{
name: 'ABC',
salesData: [
{
timestamp: '2017-09-01',
value: 10
},
{
timestamp: '2017-09-02',
value: 2
}
]
},
{
name: 'DEF',
salesData: [
{
timestamp: '2017-09-01',
value: 8
},
{
timestamp: '2017-09-02',
value: 3
}
]
}
];
I would like to transform this to:
[
{
name: 'ABC',
'2017-09-01': 10,
'2017-09-02': 2
},
{
name: 'CDE',
'2017-09-01': 8,
'2017-09-02': 3
}
]
I'm trying to use Underscore's Chain and Map which I'm getting confused. So far I have the following, not sure how do I write the convertedSalesData to transform as per the need:
_.map(data, function(item) {
let name = item.name;
let salesData = item.salesData;
let convertedSalesData = ?
})
With ES6 you can use spread syntax ... to get this result.
const data = [{"name":"ABC","salesData":[{"timestamp":"2017-09-01","value":10},{"timestamp":"2017-09-02","value":2}]},{"name":"DEF","salesData":[{"timestamp":"2017-09-01","value":8},{"timestamp":"2017-09-02","value":3}]}]
var result = data.map(function({name, salesData}) {
return {name, ...Object.assign({}, ...salesData.map(({timestamp, value}) => ({[timestamp]: value})))}
})
console.log(result)
const data = [{
name: 'ABC',
salesData: [{
timestamp: '2017-09-01',
value: 10
},
{
timestamp: '2017-09-02',
value: 2
}
]
},
{
name: 'DEF',
salesData: [{
timestamp: '2017-09-01',
value: 8
},
{
timestamp: '2017-09-02',
value: 3
}
]
}
];
var res = data.map(function(a) {
var obj = {
name: a.name
};
a.salesData.forEach(function(x) {
obj[x.timestamp] = x.value;
})
return obj;
})
console.log(res);
Similar to #Nenad Vracar. I perfer to use 'reduce':
data.map(({ name, salesData }) => ({
name,
...salesData.reduce(
(record, { timestamp, value }) => {
record[timestamp] = value
return record
},
Object.create(null)
)
}))

Categories

Resources