How to group array of objects? - javascript

I have an array of objects such as:
const array = [
{
date: '02-02-1994',
time: '18:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-02-1994',
time: '20:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-04-1994',
time: '19:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-04-1994',
time: '19:00',
services: {
first: 1,
second: 2
}
}
]
I want to group it by time and date and get the result array:
const result = [{
date: '02-02-1994',
time: '18:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-02-1994',
time: '20:00',
services: {
first: 1,
second: 1
}
},
{
date: '02-04-1994',
time: '19:00',
services: {
first: 2,
second: 3
}
}]
I want to group array by date then by time and to get a sum in service object.
I try to group by date using reduce, but then I don't know

You can use reduce() method to do that.
const array = [{ date: '02-02-1994', time: '18:00', services: { first: 1, second: 1 } }, { date: '02-02-1994', time: '20:00', services: { first: 1, second: 1 } }, { date: '02-04-1994', time: '19:00', services: { first: 1, second: 1 } }, { date: '02-04-1994', time: '19:00', services: { first: 1, second: 2 } } ];
let result = array.reduce((arr, currentValue) => {
let item = arr.find(item =>
item.date === currentValue.date &&
item.time === currentValue.time);
if (item) {
item.services.first += currentValue.services.first;
item.services.second += currentValue.services.second;
} else {
arr.push(currentValue);
}
return arr;
}, []);
console.log(result);

You could take a joined key for collecting items of the same group in an object and then take the values of it as result set.
var array = [{ date: '02-02-1994', time: '18:00', services: { first: 1, second: 1 } }, { date: '02-02-1994', time: '20:00', services: { first: 1, second: 1 } }, { date: '02-04-1994', time: '19:00', services: { first: 1, second: 1 } }, { date: '02-04-1994', time: '19:00', services: { first: 1, second: 2 } }],
result = Object.values(array.reduce((r, { date, time, services }) => {
var key = [date, time].join('|');
r[key] = r[key] || { date, time, services: {} };
Object
.entries(services)
.forEach(([k, v]) => r[key].services[k] = (r[key].services[k] || 0) + v);
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Javascript get sum of indices of n number of arrays of integers

I have an array of n number of objects, each which has an array ("needs") in this case. What I want to do is iterate through the array, and grab the sum of the index of each array.
const testData =
[{
location: 'FL',
needs:[{date: '2021-01-01', count: 5},{date: '2021-01-02', count: 1},{date: '2021-01-03', count: 2},{date: '2021-01-04', count: 23},{date: '2021-01-05', count: 65}]
},{
location: 'AL',
needs:[{date: '2021-01-01', count: 1},{date: '2021-01-02', count: 2},{date: '2021-01-03', count: 3},{date: '2021-01-04', count: 4},{date: '2021-01-05', count: 5}]
}]
So in this case, I would be left with an array that looks like [6, 3, 5, 27, 70] since testData[0].needs[0] + testData[1].needs[0] === 6 & testData[0].needs[1] + testData[1].needs[1] === 3, etc.
The function I came up with
testData.map((val, index) => {
val.needs.map((needs, index) => {
dayArray.push(needs.count)
})
})
is unfortunately doing testData[0].needs[0] + testData[0].needs[1], more or less the opposite of what I need. How do I tweak this function to get the expected results?
you can use a mapper to track the sum of date like this
const testData = [{
location: 'FL',
needs: [{
date: '2021-01-01',
count: 5
}, {
date: '2021-01-02',
count: 1
}, {
date: '2021-01-03',
count: 2
}, {
date: '2021-01-04',
count: 23
}, {
date: '2021-01-05',
count: 65
}]
}, {
location: 'AL',
needs: [{
date: '2021-01-01',
count: 1
}, {
date: '2021-01-02',
count: 2
}, {
date: '2021-01-03',
count: 3
}, {
date: '2021-01-04',
count: 4
}, {
date: '2021-01-05',
count: 5
}]
}]
const mapper = testData.reduce((acc, cur) => {
cur.needs.forEach(item => {
acc[item.date] = (acc[item.date] || 0) + item.count;
});
return acc;
}, {});
const dates = Object.keys(mapper);
dates.sort();
console.log(dates.map(k => mapper[k]));

How to Group JavaScript Array of Object based on key

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));

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; }

Group an array of time object

I have a requirement to group an array of objects based on time interval. The input looks like:
[
{
_id: {
hour: 0,
interval: '0'
},
time: '0:0',
count: 10
},
{
_id: {
hour: 0,
interval: '15'
},
time: '0:15',
count: 5
},
{
_id: {
hour: 0,
interval: '30'
},
time: '0:30',
count: 1
},
{
_id: {
hour: 0,
interval: '45'
},
time: '0:45',
count: 2
},
{
_id: {
hour: 1,
interval: '0'
},
time: '1:0',
count: 4
},
{
_id: {
hour: 1,
interval: '15'
},
time: '1:15',
count: 3
},
{
_id: {
hour: 1,
interval: '30'
},
time: '1:30',
count: 5
},
{
_id: {
hour: 1,
interval: '45'
},
time: '1:45',
count: 1
}
]
My desired output:
[
{
"time": "0",
"0": 10,
"15": 5
"30": 1,
"45": 2
},
{
"time": "1",
"0": 4,
"15": 3
"30": 5,
"45": 1
}
]
I tried to use the following code to group the objects, which works to an extent, but I'm stuck on what to do next:
const a = [ { _id: { hour: 0, interval: '0' }, time: '0:0', count: 10 }, { _id: { hour: 0, interval: '15' }, time: '0:15', count: 5 }, { _id: { hour: 0, interval: '30' }, time: '0:30', count: 1 }, { _id: { hour: 0, interval: '45' }, time: '0:45', count: 2 }, { _id: { hour: 1, interval: '0' }, time: '1:0', count: 4 }, { _id: { hour: 1, interval: '15' }, time: '1:15', count: 3 }, { _id: { hour: 1, interval: '30' }, time: '1:30', count: 5 }, { _id: { hour: 1, interval: '45' }, time: '1:45', count: 1 }]
var group = a.reduce((r, a) => {
console.log("a", a);
console.log('r', r);
r[a._id.hour] = [...r[a._id.hour] || [], a];
return r;
}, {});
console.log("group", group);
Check if the object with that hour exists in the accumulator object first - if it doesn't, create one, then assign count to that object's [interval] property, and get the Object.values at the end to turn it back into an array:
const input=[{_id:{hour:0,interval:"0"},time:"0:0",count:10},{_id:{hour:0,interval:"15"},time:"0:15",count:5},{_id:{hour:0,interval:"30"},time:"0:30",count:1},{_id:{hour:0,interval:"45"},time:"0:45",count:2},{_id:{hour:1,interval:"0"},time:"1:0",count:4},{_id:{hour:1,interval:"15"},time:"1:15",count:3},{_id:{hour:1,interval:"30"},time:"1:30",count:5},{_id:{hour:1,interval:"45"},time:"1:45",count:1}];
const groupedObj = {};
for (const { _id: { hour, interval }, count } of input) {
if (!groupedObj[hour]) {
groupedObj[hour] = { time: hour };
}
groupedObj[hour][interval] = count;
}
const output = Object.values(groupedObj);
console.log(output);
Reduce the array, and create an object for each _id.time. Assign the current [interval] = count to the object. Get the entries, and use Array.from() to convert the entries to an array of the required form:
const arr = [{"_id":{"hour":0,"interval":"0"},"time":"0:0","count":10},{"_id":{"hour":0,"interval":"15"},"time":"0:15","count":5},{"_id":{"hour":0,"interval":"30"},"time":"0:30","count":1},{"_id":{"hour":0,"interval":"45"},"time":"0:45","count":2},{"_id":{"hour":1,"interval":"0"},"time":"1:0","count":4},{"_id":{"hour":1,"interval":"15"},"time":"1:15","count":3},{"_id":{"hour":1,"interval":"30"},"time":"1:30","count":5},{"_id":{"hour":1,"interval":"45"},"time":"1:45","count":1}];
// convert the entries to an array
const result = Array.from(Object.entries(
arr.reduce((r, o) => {
const { hour, interval } = o._id; // get the hour and interval
if(!r[hour]) r[hour] = {}; // create a the hour object
r[hour][interval] = o.count; // add the interval and count
return r;
}, {})
), ([time, values]) => ({ time, ...values })); // generate the result objects
console.log(result)
You can group object by reduce method. So at first you need to group by hour and then just add interval properties from each iteration of reduce method to the hour property:
const result = arr.reduce((a, c) => {
a[c._id.hour] = a[c._id.hour] || {};
a[c._id.hour].time = c._id.hour;
a[c._id.hour][c._id.interval] = c.count;
return a;
}, {})
console.log(result);
An example:
let arr = [
{
_id: {
hour: 0,
interval: '0'
},
time: '0:0',
count: 10
},
{
_id: {
hour: 0,
interval: '15'
},
time: '0:15',
count: 5
},
{
_id: {
hour: 0,
interval: '30'
},
time: '0:30',
count: 1
},
{
_id: {
hour: 0,
interval: '45'
},
time: '0:45',
count: 2
},
{
_id: {
hour: 1,
interval: '0'
},
time: '1:0',
count: 4
},
{
_id: {
hour: 1,
interval: '15'
},
time: '1:15',
count: 3
},
{
_id: {
hour: 1,
interval: '30'
},
time: '1:30',
count: 5
},
{
_id: {
hour: 1,
interval: '45'
},
time: '1:45',
count: 1
}
]
const result = arr.reduce((a, c) => {
a[c._id.hour] = a[c._id.hour] || {};
a[c._id.hour].time = c._id.hour;
a[c._id.hour][c._id.interval] = c.count;
return a;
}, {})
console.log(result);

Combining Like Objects by Date in JavaScript Array

I have the following array:
var objArray = [
{ num: 1, date: '1/12/2017' },
{ num: 3, date: '1/12/2017' },
{ num: 7, date: '1/12/2017' },
{ num: 1, date: '1/13/2018' },
{ num: 3, date: '1/16/2018' },
{ num: 4, date: '1/16/2018' }
];
I want to combine those with same dates so that the output array looks like this:
var outputArr = [
{ num: 11, date: '1/12/2017' },
{ num: 1, date: '1/13/2018' },
{ num: 7, date: '1/16/2018' }
];
I'm adding all num with similar dates and creating a single new object.
I have a very large dataset of objects like this so I'm trying to reduce the amount of processing time for this.
I've got the arrays sorted by date so that it mirrors objArray.
For loops seems cumbersome since I'm taking the first date in the array and checking every other element in the array a la the following pseudo-code:
var newArr = [];
for(i = 0; i < objArray.length; i++) {
for(j = 0; j < objArray.length; j++) {
var tempArr = [];
// check every date manually
// add similar to new array
tempArr.push({ similar items });
}
newArr.push(tempArr):
}
// Do another couple loops to combine those like arrays into another array
There has to be a more elegant way to perform this than running multiple for loops.
Any suggestions would be appreciated.
Simply use Array.reduce() to create a map and group values by date, Object.values() on the map will give you the desired output value:
let arr = [ { num: 1, date: '1/12/2017' }, { num: 3, date: '1/12/2017' }, { num: 7, date: '1/12/2017' }, { num: 1, date: '1/13/2018' }, { num: 3, date: '1/16/2018' }, { num: 4, date: '1/16/2018' } ];
let result = Object.values(arr.reduce((a, {num, date})=>{
if(!a[date])
a[date] = Object.assign({},{num, date});
else
a[date].num += num;
return a;
},{}));
console.log(result);
Using lodash,
// Aggregate num from unique dates
var g = _.groupBy(objArray,'date')
Object.keys(g).map(k=>({num:g[k].reduce((a,c)=>c.num+a,0),date:k}))
var objArray = [
{ num: 1, date: '1/12/2017' },
{ num: 3, date: '1/12/2017' },
{ num: 7, date: '1/12/2017' },
{ num: 1, date: '1/13/2018' },
{ num: 3, date: '1/16/2018' },
{ num: 4, date: '1/16/2018' }
];
let outputArr = Array.from(objArray.reduce((acc, obj)=>{
acc.set(obj.date, (acc.get([obj.date]) || 0) + obj.num);
return acc;
}, new Map()))
.map(kv=>({num: kv[1], date: kv[0]}))
console.log(outputArr);
gives:
[ { num: 11, date: '1/12/2017' },
{ num: 1, date: '1/13/2018' },
{ num: 7, date: '1/16/2018' } ]
You could also remove the if statements and use a Set if you wanted to be even more declarative.
var objArray = [
{ num: 1, date: '1/12/2017' },
{ num: 3, date: '1/12/2017' },
{ num: 7, date: '1/12/2017' },
{ num: 1, date: '1/13/2018' },
{ num: 3, date: '1/16/2018' },
{ num: 4, date: '1/16/2018' }
];
var mSet = new Set(objArray.map(d => d.date));
return Array.from(mSet).map(d => {
return
{
date: d,
sum: (objArray
.filter(o => o.date === d)
.map(n => n.num)
.reduce((a, c) => a + c, 0))
}
);
This returns:
[{ date: 1/12/2017, sum: 11},
{ date: 1/13/2018, sum: 1 },
{ date: 1/16/2018, sum: 7 }]
Here's another way. It's more verbose, but if you're just starting out it might be easier to understand as opposed to using array methods like reduce().
objArray = [
{ num: 1, date: '1/12/2017' },
{ num: 3, date: '1/12/2017' },
{ num: 7, date: '1/12/2017' },
{ num: 1, date: '1/13/2018' },
{ num: 3, date: '1/16/2018' },
{ num: 4, date: '1/16/2018' }
]
function combineObj(data) {
let validator= new Set();
let combinedArr = [];
let numCount = 0;
// Create a list of unique properties to match against:
data.forEach((e) => validator.add(e.date));
// For each value in the validator, create a new object in a new array
// and add the unique values from the validator to the respective property:
validator.forEach((e) => {
combinedArr.push({
num: 0,
date: e
});
})
// Lastly, for each object in the combinedArr, use a counter to sum up the total values of each property
// as you loop through your data:
combinedArr.forEach((e) => {
numCount = 0;
data.forEach((ee) => {
if (e.date === ee.date) {
numCount += ee.num;
e.num = numCount;
}
})
})
return combinedArr;
}
Returns:
[
{ num: 11, date: '1/12/2017' },
{ num: 1, date: '1/13/2018' },
{ num: 7, date: '1/16/2018' }
]

Categories

Resources