I have a json file with multiple transactions
with a date
and a price
attribute. Now I want to pare the dates and if they are in the same month and year I want to sum up the prices.
JSON:
transactions: [
{
date: "2017-11-17",
price: "28",
},
{
...
}
JavaScript:
request.onload = function() {
for(const transaction of request.response.transactions) {
let year = new Date(transaction.date).getFullYear();
let month = new Date(transaction.date).getMonth();
console.log(year + ' ' + month); // output: 2017-11 ...
}
};
I tried to loop over the json object but I struggle to find a solution to pare the dates.
I have a json file with multiple transactions
with a date
and a price
attribute. Now I want to pare the dates and if they are in the same month and year I want to sum up the prices.
JSON:
transactions: [
{
date: "2017-11-17",
price: "28",
},
{
...
}
JavaScript:
request.onload = function() {
for(const transaction of request.response.transactions) {
let year = new Date(transaction.date).getFullYear();
let month = new Date(transaction.date).getMonth();
console.log(year + ' ' + month); // output: 2017-11 ...
}
};
I tried to loop over the json object but I struggle to find a solution to pare the dates.
Share Improve this question asked Dec 8, 2017 at 13:39 DarioDario 6401 gold badge12 silver badges29 bronze badges 5- What is the expected output ? – ChrisR Commented Dec 8, 2017 at 13:44
- What's your expected output? An object array containing the sum for each month? (ex - [{month: 2017-10, priceTotal: 123}, month: 2017-11, priceTotal: 234}] – nipuna-g Commented Dec 8, 2017 at 13:44
-
The expected output is an array of
prices
sorted for each month and year. – Dario Commented Dec 8, 2017 at 13:45 - @nipuna777 yeah, that would be perfect =). But you can also help by just explaining how I can effectively pare my dates for each month and year. Thanks for your help – Dario Commented Dec 8, 2017 at 13:47
-
Do not use the built-in parser:
new Date(transaction.date).getFullYear()
is problematic as your string format will be treated as UTC by conforming browsers. So depending on your timezone, some dates for the first or last day of the month will appear to be in the adjacent month. Also see Why does Date.parse give incorrect results? – RobG Commented Dec 8, 2017 at 21:27
5 Answers
Reset to default 4Edit: Edited example with Object.assign
instead of Object spread.
You'll need to use reduce
to sum the prices. See ments for details.
const transactions = [{
date: "2017-11-17",
price: "28",
},
{
date: "2017-12-17",
price: "23",
},
{
date: "2017-11-17",
price: "12",
},
{
date: "2017-10-17",
price: "55",
},
{
date: "2017-11-17",
price: "09",
},
];
const sumTransactions = (transactions) => {
const summed = transactions.reduce((acc, current) => {
// Get the current date object
const date = new Date(current.date);
// Create your key/identifier
const key = `${date.getFullYear()}-${date.getMonth() + 1}`;
// Retreive the previous price from the accumulator
const previousPrice = acc[key]; // Might also return undefined
// Create your temp current price value, and be sure to deal with numbers.
let currentPrice = Number(current.price);
// If you had a previous value (and not undefined)
if (previousPrice) {
// Add it to our value
currentPrice += Number(previousPrice);
}
// Return the future accumulator value
return Object.assign(acc, {
[key]: currentPrice, // new values will overwrite same old values
})
}, {})
// Once we have all values, get the dates, and sort them (default: earlier first)
// Return an array of each value from the summed object to our sortedArray
const sortedArray = Object.keys(summed).sort().map((val) => {
return summed[val];
});
console.log("sortedArray", sortedArray);
};
sumTransactions(transactions);
I experimented a bit and came up with this solution:
var transactions = [
{
date: "2017-11-17",
price: "28",
},
{
date: "2017-12-17",
price: "22",
},
{
date: "2017-12-17",
price: "20",
}
]
var sumedUpDates = [];
var prices = [];
function isDateSumedUp(date) {
return sumedUpDates.indexOf(date.substring(0, 7)) !== -1;
}
function sumUpDate(date) {
var sum = 0;
transactions.forEach(t => {
if(t.date.substring(0, 7) === date.substring(0, 7)) {
sum += parseInt(t.price);
}
});
sumedUpDates.push(date.substring(0, 7));
prices.push(sum);
}
transactions.forEach(t => {
if(!isDateSumedUp(t.date)) {
sumUpDate(t.date);
}
});
var obj = {};
sumedUpDates.forEach((d, i) => obj[d] = prices[i]);
console.log(obj);
This solutions uses map to format your dates into year/month format for each object entry and then reduce to sum them by those separated dates.
const transactions = [
{date:"2017-11-17", price: "28",},
{date:"2017-12-17", price: "28",},
{date:"2017-11-17", price: "20",},
{date:"2017-12-17", price: "2",},
{date:"2017-11-17", price: "58",},
{date:"2017-11-17", price: "8",},
{date:"2017-10-17", price: "30",},
{date:"2018-11-17", price: "1",},
];
const mapper = single => {
let d = single.date.split('-');
let p = Number(single.price);
return { year: d[0], month: d[1], price: p };
}
const reducer = (group, current) => {
let i = group.findIndex(single => (single.year == current.year && single.month == current.month));
if (i == -1) {
return [ ...group, current ];
}
group[i].price += current.price;
return group;
};
const sumPrices = transactions.map(mapper).reduce(reducer, []);
console.log(sumPrices);
var array = [];
for (var i = 0; i < transactions.length; i++) {
var date = new Date(transactions[i].date);
var ym = date.getFullYear() + "-" + date.getMonth();
if (array[ym] == null) {
array[ym] = 0;
}
array[ym] += parseInt(transactions[i].price);
}
With this data
var transactions = [{
date: "2017-11-17",
price: "28",
},
{
date: "2017-12-17",
price: "5",
},
{
date: "2016-02-17",
price: "28",
},
{
date: "2015-11-17",
price: "25",
},
{
date: "2016-02-17",
price: "12",
},
{
date: "2017-11-17",
price: "50",
}
];
This will give you the sum of all of the year-months duplicates like this :
[
2017-10: 78,
2017-11: 5,
2016-1: 40,
2015-10: 25
]
Another solution is reduce:
var transactions = [
{date: "2017-11-17",price: "28"},
{date: "2017-12-17",price: "22"},
{date: "2017-12-17",price: "20"}
];
var result = transactions.reduce(function(acc, obj) {
var key = obj.date.substr(0,7);
acc[key] = (acc[key] || 0) + +obj.price;
return acc;
}, Object.create(null));
console.log(result);