Group array of objects by common key (date string) - javascript

I have an array as below.
const list = [
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-02",
number: 4,
count: 1
},
{
date: "2020-01-02",
number: 4,
count: 1
}
]
And I want to sum number and count depends on same date. After that it should be merged in same date. Therefore I want to get the result as below.
const list = [{
totalNumber: 9,
totalCount: 6,
detail:[{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
},
{
date: "2020-01-01",
number: 3,
count: 2
}]
},
{
totalNumber: 8,
totalCount: 2,
detail:[
{
date: "2020-01-02",
number: 4,
count: 1
},
{
date: "2020-01-02",
number: 4,
count: 1
}]
}
]
I know stackoverflow is not code writing site. But I made this from some data. But this is my last stage for completing my object. And I don't know how I can group like that. I'' appreciate if you help me to resolve this problem. Thank you so much for reading it.

To me, optimal approach is building Map (having necessary property as a key) with Array.prototype.reduce(), then extracting array of values (aggregated records) with Map.prototype.values():
const src = [{date:"2020-01-01",number:3,count:2},{date:"2020-01-01",number:3,count:2},{date:"2020-01-01",number:3,count:2},{date:"2020-01-02",number:4,count:1},{date:"2020-01-02",number:4,count:1}],
groupped = [...src
.reduce((acc, {date, number, count}) => {
const group = acc.get(date)
if(group){
group.totalNumber += number
group.totalCount += count
group.detail.push({date, number, count})
} else {
acc.set(
date,
{
date,
totalNumber: number,
totalCount: count,
detail: [{
date,
number,
count
}]
}
)
}
return acc
}, new Map)
.values()
]
console.log(groupped)
.as-console-wrapper{min-height:100%;}

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

Max element in an array in dailogflow

I am trying to calculate max element in an array . I tried this code but it is returning [object Object]
Is there something i am missing while doing in dailogflow.
function studentgroup(agent){
let games = [
{ id: 1, name: 'Star Wars: Imperial Assault', votes: 3},
{ id: 2, name: 'Game of Thrones: Second Edition', votes: 4 },
{ id: 3, name: 'Merchans and Marauders', votes: 5 },
{ id: 4, name: 'Eclipse', votes: 6 },
{ id: 5, name: 'Fure of Dracula', votes: 2 }
];
let maxGame = games.reduce((max, game) => max.votes > game.votes ? max : game);
agent.add(`${maxGame}`);
}
You can simply find the maximum element by iterating over the array.
let games = [
{ id: 1, name: 'Star Wars: Imperial Assault', votes: 3},
{ id: 2, name: 'Game of Thrones: Second Edition', votes: 4 },
{ id: 3, name: 'Merchans and Marauders', votes: 5 },
{ id: 4, name: 'Eclipse', votes: 6 },
{ id: 5, name: 'Fure of Dracula', votes: 2 }
];
maxElement = -Infinity;
element = null
for (const game of games) {
if (game.votes > maxElement) {
maxElement = game.votes;
element = game;
}
}
console.log(element)
The issue is that maxGame is an object. Using your example, that object will be
{ id: 4, name: 'Eclipse', votes: 6 }
But agent.add() is expecting to send back a string. The default "string" form of an object is "[object Object]", as you've seen.
You probably want to return something that makes more sense when displayed or read aloud, so it might make more sense for that line to be something like
agent.add(`The winner, with ${maxElement.votes} votes, is ${maxElement.name}.`)
which, given the example, would say something like
The winner, with 6 votes, is Eclipse.

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' }
]

Group array of objects by multiple properties with Lodash

is possible with lodash library group elements by 2 properties?
I have array of objects like this:
[{
id: 1,
amount: 2000,
date: "2018-01-31T00:00:00.000Z"
},{
id: 2,
amount: 3000,
date: "2017-07-31T00:00:00.000Z"
},{
id: 3,
amount: 6000,
date: "2018-01-31T00:00:00.000Z"
},{
id: 4,
amount: 7000,
date: "2017-01-31T00:00:00.000Z"
},{
id: 5,
amount: 5000,
date: "2017-03-31T00:00:00.000Z"
},{
id: 6,
amount: 3000,
date: "2018-02-22T00:00:00.000Z"
},{
id: 7,
amount: 4500,
date: "2017-01-31T00:00:00.000Z"
}]
My goal is group objects in array by:
year
month
Purpose of that grouping is that I need in result order these objects' sum of amount by newest. So for that reason I need distinguish January 2017 from January 2018. It should be 2 different groups.
I am not sure if my approach is correct so I write here my required output:
[
3000, // sum of 2018-2
8000, // sum of 2018-1
3000 // sum of 2017-7
5000 // sum of 2017-3
11500 // sum of 2017-1
]
I tried following command but it doesn't work and give me error:
let xxx = _(data)
.groupBy(function(i) {
new Date(i.date).getFullYear()
})
.groupBy(function(i) {
new Date(i.date).getMonth()
})
.map(x => x.amount)
.sum()
.orderBy('date').value();
Can you help me to fix it ? Thanks.
You can just concat your year and month with groupBy and use it.
var grouped = _.groupBy(data, function(i) {
return new Date(i.date).getFullYear()+'-'+new Date(i.date).getMonth()
})
var resultUnsorted = _.map(t, (val, key) => ({key: key, val: val.reduce((p, c) => c.amount + p, 0) }));
then sort using _.orderBy
const output = _.orderBy(resultUnsorted, 'key');
you can write your custom sort function using the behaviour you want.

Creating arrays in JavaScript by comparing values in another array

In JavaScript, I'm trying to create arrays based on the values of another array and I'm having difficultly.
I've got an array of dates in string format (dates) e.g.
["30/09/2015", "31/10/2015", "30/11/2015", "31/12/2015"]
I've got an Object to represent multiple bank accounts (balancesSortByAccId) e.g.
Cash - (Array size: 3)
id: 1, date: "30/09/2015", balance: 30
id: 2, date: "31/10/2015", balance: 50
id: 3, date: "30/11/2015", balance: 100
Natwest - (Array size: 2)
id: 4, date: "30/11/2015", balance: 400
id: 5, date: "31/12/2015", balance: 200
Whilst looping through all the accounts in balancesSortByAccId, I want to be able to create an array for the balance at each date in the dates array i.e.
[30, 50, 100, null]
[null, null, 400, 200]
How could I achieve this?
UPDATE: jsfiddle code - https://jsfiddle.net/gx8bLehb/
The easiest way would be to transform your cash and natwest arrays into a hash sorted by date, something like balancesByDate:
var balancesByDate = _.groupBy(cash, function(entry) {return entry.date});
Then use an array map() function, e.g. from lodash to iterate the dates array and for each date look up the account line in the balancesByDate hash. From that line, return the balance property in the map function.
dates.forEach(function(date){
if (balancesByDate[date]) {
results.push(_.map(balancesByDate[date], function(line){
return line.balance;
}));
} else {
results.push(null);
}
});
However, you need to be aware that your dataset most likely could contain duplicate balances for a day, you should plan for that (my code returns an array for each day).
https://jsfiddle.net/hdpuuc5d/1/
A solution in plain javascript with a helper object for the dates:
var dates = ["30/09/2015", "31/10/2015", "30/11/2015", "31/12/2015"],
datesObj = dates.reduce(function (r, a, i) { r[a] = i; return r; }, {}),
balances = {
Cash: [
{ id: 1, date: "30/09/2015", balance: 30 },
{ id: 2, date: "31/10/2015", balance: 50 },
{ id: 3, date: "30/11/2015", balance: 100 }
],
Natwest: [
{ id: 4, date: "30/11/2015", balance: 400 },
{ id: 5, date: "31/12/2015", balance: 200 }
]
},
result = {};
Object.keys(balances).forEach(function (k) {
result[k] = Array.apply(null, { length: dates.length }).map(function () { return null; });
balances[k].forEach(function (a) {
result[k][datesObj[a.date]] = a.balance;
});
});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');

Categories

Resources