Consolidating array and adding amounts of similar objects - javascript

I have an array of objects that contain a date and an amount (among other things).
There is an object for each date with a specific amount, but there can also be multiple objects for the same date containing different amounts.
I'd like to consolidate the objects so I only have one object in the array for each date ... and have the amount corresponding to that date be the total sum of all previous amounts in those objects.
Examples will probably help here:
What my array looks like now:
[
{
date: "2019-1-1", // this is a dupe date
amount: 20,
...
},
{
date: "2019-1-1", // this is a dupe date
amount: 40,
...
},
{
date: "2019-1-2",
amount: 40,
...
},
{
date: "2019-1-3",
amount: 40,
...
}
]
What I would like my array to look like:
[
{
date: "2019-1-1", // this is now a unique date
amount: 60, // and the amount was totaled
...
},
{
date: "2019-1-2",
amount: 40,
...
},
{
date: "2019-1-3",
amount: 40,
...
}
]

Use .reduce to reduce an array into an object (or into anything else) by iterating over its properties. You just need to test to see if an object with a matching date already exists in the accumulator first:
const input = [
{
date: "2019-1-1", // this is a dupe date
amount: 20,
foo: 'bar',
},
{
date: "2019-1-1", // this is a dupe date
amount: 40,
foo: 'bar',
},
{
date: "2019-1-2",
amount: 40,
foo: 'bar',
},
{
date: "2019-1-3",
amount: 40,
foo: 'bar',
}
];
const output = input.reduce((accum, item) => {
const { date, amount } = item;
const foundObj = accum.find(({ date: findDate }) => findDate === date);
if (foundObj) {
foundObj.amount += amount;
return accum;
}
accum.push(item);
return accum;
}, []);
console.log(output);

You may do as follows;
var data = [ { date: "2019-1-1", // this is a dupe date
amount: 20},
{ date: "2019-1-1", // this is a dupe date
amount: 40},
{ date: "2019-1-2",
amount: 40},
{ date: "2019-1-3",
amount: 40}
],
result = Object.values(data.reduce((r,d) => r[d.date] ? (r[d.date].amount += d.amount, r)
: (r[d.date] = d, r), {}));
console.log(result);
Regarding a comment i guess i have to explain this a little for those who may not be familiar with some ES6 functionalities.
Object.values() is a Object method which returns all own property values in an array.
So we are reducing our objects into an hash object of which we collect the properties by Object.values() later. While reducing we check if the currently examined object's date value exists as key in our map. If not we create that key and insert the examined object at that key position, if yes then we increment the previously inserted objects amount property by the value of currently examined objects amount value.
If you don't want to mutate the original data then please change r[d.date] = d into r[d.date] = Object.assign({},d).

The way I would do it is to create an object with the dates as the key, then you can iterate over the array and create a new date property if it doesn't exist or increase the amount if it does, then convert it back into an array:
const items = data.reduce((acc, curr) => {
if (!acc[curr.date]) { // basically creating a property with the date as the key and the value is the current object
acc[curr.date] = { ...curr };
} else { // if it exists already, then just increment the amount
acc[curr.date].amount += curr.amount;
}
return acc;
}, {});
const newArray = Object.values(items); // grab all the values from the object above

Related

Calling function with a passed in array

I'm trying to write a function that will be called with an array that has information on a person such as their name and then age. I need this function to grab all of the numbers only and then return them then added up. I've done some research and it seems filter and reduce are what I need to do this in the easiest way for a total beginner like me to do?
Apologies for any typos/wrong jargon as my dyslexia gets the better of me sometimes.
An example of what kind of array is being passed into the function when called;
{ name: 'Clarie', age: 22 },
{ name: 'Bobby', age: 30 },
{ name: 'Antonio', age: 40 },
Would return the total added numbers.
// returns 92
Why isn't the array I'm calling this function with working? Can you provide me a working example without the array being hardcoded like the other answers? - I'm passing in an array to the function. The main objective is to grab any number from the passed in array and add them together with an empty array returning 0.
function totalNums(person) {
person.reduce((a,b) => a + b, 0)
return person.age;
}
console.log(totalNums([]))
You need to save the result into a new variable then console.log() it like this
const arr= [{ name: 'Clarie', age: 22 },
{ name: 'Bobby', age: 30 },
{ name: 'Antonio', age: 40 },...
];
function totalNums(person) {
let res = person.reduce((a,b) => a + b.age, 0)
return res;
}
console.log(totalNums(arr));
and this is why it has to be like that
.reduce()
js methods like .map(), .filter(), .reduce() and some others, they return a new array, they don't modify the original array.
You can console.log(arr); and you will get this output:
[{ name: 'Clarie', age: 22 },
{ name: 'Bobby', age: 30 },
{ name: 'Antonio', age: 40 },...
];
Your original array unchanged even after running your function so in order to get the result you expect, you need to store it inside a new variable
You need to save the result of your reduce.
For example with array of numbers you would do:
function totalNums(person) {
let res = person.reduce((a,b) => a + b, 0)
return res;
}
console.log(totalNums([5,6,4]))
And for your example you would like to do something like this:
function totalNums(person) {
let res = person.reduce((a,b) => a + b.age, 0)
return res;
}
console.log(totalNums([
{ name: 'Clarie', age: 22 },
{ name: 'Bobby', age: 30 },
{ name: 'Antonio', age: 40 }
]))
function totalNums(person) {
person.reduce((a,b) => a + b, 0)
return person.age;
}
console.log(totalNums([]))
Talking about the function you have created it is incorrect because:
return person.age; Here you are passing an array to function and then accessing it like it's an object.
person.reduce((a,b) => a + b, 0) you can't add a and b because b is an object.
You are not storing value which reduce function will return.
Solution Below :
The reduce function always returns something It never makes changes in the original array.
function totalNums(persons) {
const totalAge = persons.reduce((total, person) => total + person.age, 0);
return totalAge;
}
const persons = [
{ name: "Clarie", age: 22 },
{ name: "Bobby", age: 30 },
{ name: "Antonio", age: 40 },
];
console.log(totalNums(persons));
You can replace total and person with a and b respectively in the above code snippet for your reference.

How to filter data of object according to keys and make new array in react?

I am working with the object like this:-
{
0:
buyAmount: 16.664328043964396
buyAmountInUsd: 16.685266204095775
date: {date: '2021-12-07'}
sellAmount: {USD: 500, INR: 35000}
1:
buyAmount: 1004.7015442959262
buyAmountInUsd: 1005.9639175379324
date: {date: '2021-12-07'}
sellAmount: {USD: 1000, INR: 79000}
......
and I am trying to make a new array using useState hook with this data but the problem I am facing is how to filter data and make an almost similar array with the same data.
e.g.:-
0: [amount: 500, date: '2021-12-07'],
1: [amount: 1000, date: '2021-12-07']
The problem I am facing is I don't know the approach how to get the data like amount = sellAmount.USD and date = date.date
I thought of trying the for...of But I don't think it will be a good hit.
You can do this with Array.map
const arr = [{
buyAmount: 16.664328043964396,
buyAmountInUsd: 16.685266204095775,
date: {date: '2021-12-07'},
sellAmount: {USD: 500, INR: 35000}
},{
buyAmount: 1004.7015442959262,
buyAmountInUsd: 1005.9639175379324,
date: {date: '2021-12-07'},
sellAmount: {USD: 1000, INR: 79000}
}]
console.log(
arr.map(initialValue => {
return {
amount: initialValue.sellAmount.USD,
date: initialValue.date.date
}
})
)
The better idea would be the having an array of objects rather than array of array elements
let result = yourArray.map(each => ({ amount: each.sellAmount.USD, date: each.date.date }))
This is not related to ReactJS - just native Javascript object handling.
If your original data has the shape of an Object you can convert it into an Array like so:
const objData = { 0: {...}, 1: {...} };
const arrData = Object.entries(objData); // [[0, {...}], [1, {...}]];
From there, you can filter/map/sort your array with native array methods:
const reshapedArray = arrData.map(([key, value]) => {
return {
amount: value.sellAmount.USD,
date: value.date.date,
};
});
Then sort:
const sortedArray = reshapedArray.sort((prev, next) => {
return prev.amount - next.amount; // sort by amount ascending
});
You can of could chain these array functions and shorten the syntax a bit:
Object.entries(objData)
.map(([_, { sellAmount, date: { date } }]) => ({ amount: sellAmount.USD, date }))
.sort((a, b) => a.amount - b.amount);

Sort a nested array of object when index is a key in JavaScript

I am trying to sort nested array of objects, based off the key hours. The reason I am setting the index as userId, is that I want to be able to do highScoreList[userId] at a later time to grab the selected user information.
[
'587665723162626': { userId: '587665723162626', hours: 0, lastHours: 0 },
'120156769943556': { userId: '120156769943556', hours: 0, lastHours: 0 },
'773193626386432': { userId: '773193626386432', hours: 10, lastHours: 2 }
]
let highScoreList = [];
//data is inserted via another function from mongoDB and below is how I inserted into the array.
const updateHighScoreCache = (userId, hours, lastHours) =>{
highScoreList[userId] = { userId, hours, lastHours };
}
function compare(a, b) {
if(a.hours > b.hours) return 1;
if(b.hours > a.hours) return -1;
return 0;
}
highScoreList.sort(compare)
I have tried changing the if statement in the compare function to the following and the array did not change at all still:
if(highScoreList[a].hours > highScoreList[b].hours) return 1;
if(highScoreList[b].hours > highScoreList[a].hours) return -1;
The expected result after sort is:
[
'773193626386432': { userId: '773193626386432', hours: 10, lastHours: 2 },
'587665723162626': { userId: '587665723162626', hours: 0, lastHours: 0 },
'120156769943556': { userId: '120156769943556', hours: 0, lastHours: 0 }
]
But I keep getting the original array.
Your array literal at the top is not valid JS syntax. The code shows where you go wrong.
First you define highScoreList as an array, but then you don't populate the array, but create object properties for it, with:
highScoreList[userId] = { userId, hours, lastHours };
These (non-index) properties are ignored by the typical array methods such as sort. Those methods work on the values stored at the array indexes.
So change this:
highScoreList[userId] = { userId, hours, lastHours };
to this:
highScoreList.push({ userId, hours, lastHours });
If however, the structure is returned to you as a plain object (not an array), like so:
{
'587665723162626': { userId: '587665723162626', hours: 0, lastHours: 0 },
'120156769943556': { userId: '120156769943556', hours: 0, lastHours: 0 },
'773193626386432': { userId: '773193626386432', hours: 10, lastHours: 2 }
}
..then note that plain objects are not really the right tool for sorting. They better serve use cases where you don't care about order. So if you really get an object like this (not an array) and need order, then covert that object to an array first, with:
highScoreList = Object.values(highScoreList);
...and then call .sort
Another remark: your compare function will do the job, but the way it really is done, is as follows:
const compare = (a, b) => a.hours - b.hours;
If you array is:
const highScoreList = [
{ userId: '587665723162626', hours: 0, lastHours: 0 },
{ userId: '120156769943556', hours: 0, lastHours: 0 },
{ userId: '773193626386432', hours: 10, lastHours: 2 }
];
Then this code sorts it DESC:
highScoreList.sort((x,y) => y.hours - x.hours)
Then this code sorts it ASC:
highScoreList.sort((x,y) => x.hours - y.hours)

Create a table from added data sorted by month

I have a problem sorting data and getting an array with the right data. Here is the situation :
I get a table of data representing invoices (creation date and price) in this format :
invoices: [
0: {
amount: 200,
created_at: 1590572830425
},
1: {
amount: 799,
created_at: 1590572847553
}
...
]
I would like to loop on this table of invoices, and extract the total sum of the amount of invoices according to the month of the year (thanks to the fields created_at representing a timestamp.
At the end, I would like to obtain a table with 12 values maximum, representing the total amount of invoices classified by month
Expected result : [300, 450, 799, 650, 288, 400, 643, 809, 1073, 499, 640, 600]
Thank you in advance.
As you would not want amounts to get mingled up across different years, it would be better to have a result that lists the relevant years, and then for each year, have the 12 sums:
function monthTotals(invoices) {
let totals = {};
for (let {created_at, amount} of invoices) {
let date = new Date(created_at);
let year = date.getFullYear();
(totals[year] = (totals[year] || Array(12).fill(0)))[date.getMonth()] += amount;
}
return totals;
}
let invoices = [{
amount: 200,
created_at: 1590572830425
},{
amount: 799,
created_at: 1590572847553
}];
console.log(monthTotals(invoices));
If you can, try to always give a real input and output combination - i.e. ones that match (input maps to expected output), so that we can run it and test our output against your expected output. Also, often known as a minimal reproducible example (https://stackoverflow.com/help/minimal-reproducible-example):
const invoices = [
{
amount: 200,
created_at: 1590572830425
},
{
amount: 799,
created_at: 1590572847553
}
]
const outputArr = [0,0,0,0,0,0,0,0,0,0,0,0];
invoices.forEach(item => {
const myDate = new Date(item.created_at);
console.log(myDate.toDateString())
const month = parseInt(myDate.getMonth());
outputArr[month] += item.amount
});
console.log(outputArr);
Output:
[0,0,0,0,999,0,0,0,0,0,0,0]
Like this. I first did a reduce but that was overkill
This solution will not work across years
const invoices = [{
amount: 200,
created_at: 1590572830425
},
{
amount: 799,
created_at: 1590572847553
}
];
const monthVal = new Array(12).fill(0);
invoices.forEach(item => {
const month = new Date(item.created_at).getMonth();
monthVal[month] += item.amount;
});
console.log(monthVal)
Also you can sum up the entire process in reduce method.
var invoices = [ {amount: 200,created_at: 1590572830425},{amount: 799,created_at:1590572847553}];
var result = invoices.reduce((acc, elem)=>{
month = new Date(elem.created_at).getMonth();
acc[month] += elem.amount;
return acc;
},Array.from({length:12}, ()=>0));
console.log(result);

Remove duplicate elements based on date field in javascript

I want a function that takes an array and filters out old duplicates.
Specifically, if duplicate ids exist in myList, keep only the object with the newest date. Given the following array
let myList = [{
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-21 21:04:13"
},
{
id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5",
date: "2018-02-22 21:04:13"
},
{
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-23 21:04:13"
}]
the function should return:
[{
id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5",
date: "2018-02-22 21:04:13"
},
{
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-23 21:04:13"
}]
You can use the function reduce to build the desired output.
let myList = [{ id: "e9519e95-5a10-4274-ac24-de72ad60ffd7", date: "2018-02-21 21:04:13"},{ id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5", date: "2018-02-22 21:04:13"},{ id: "e9519e95-5a10-4274-ac24-de72ad60ffd7", date: "2018-02-23 21:04:13"}];
let result = Object.values(myList.reduce((a, {id, date}) => {
if (a[id]) {
if (a[id].date < date) a[id] = {id, date};
} else a[id] = {id, date};
return a;
}, {}));
console.log(result);
Put the entries into a hash table keyed by id. Each time you add an entry, look up the id and either keep the existing entry or replace it with the new one, based on whichever has a more recent date.
Map and Array.prototype.map() can be combined to functionally filter key based duplicates from arrays.
Array.prototype.sort() can be leveraged to guarantee order.
See below for a practical example.
// Input.
const input = [
{id: "e9519e95-5a10-4274-ac24-de72ad60ffd7", date: "2018-02-21 21:04:13"},
{id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5", date: "2018-02-22 21:04:13"},
{id: "e9519e95-5a10-4274-ac24-de72ad60ffd7", date: "2018-02-23 21:04:13"}
]
// Sort By Date.
const sortDate = array => array.sort((A, B) => new Date(A.date)*1 - new Date(B.date)*1)
// Filter Duplicates.
const filter = array => [...new Map(array.map(x => [x.id, x])).values()]
// Output.
const outputRaw = filter(input) // No guaranteed order.
const outputSorted = sortDate(filter(sortDate(input))) // Guaranteed latest.
// Proof.
console.log('Raw', outputRaw)
console.log('Sorted', outputSorted)
This isn't the best answer, just another take on #Ele's solution offered for completeness. Instead of plucking the values after the unique set is found, it works on the returned array for each iteration. The find during each iteration should be less efficient than a key lookup, which is one of the reasons it's not the best answer.
let myList = [{
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-21 21:04:13"
}, {
id: "026e7ecf-d236-4aff-b26d-7546ac85b7d5",
date: "2018-02-22 21:04:13"
}, {
id: "e9519e95-5a10-4274-ac24-de72ad60ffd7",
date: "2018-02-23 21:04:13"
}]
let result = myList.reduce((arr, { id, date }) => {
let found = arr.find(v=>v.id==id)
if (found) {
if (found.date < date)
found.date = date
}
else
arr.push({ id, date });
return arr;
}, []);
console.log(result);

Categories