3

I have a json file with multiple transactions with a date and a price attribute. Now I want to compare 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 compare the dates.

Dario
  • 618
  • 1
  • 11
  • 28
  • What is the expected output ? – ChrisR Dec 08 '17 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 Dec 08 '17 at 13:44
  • The expected output is an array of `prices` sorted for each month and year. – Dario Dec 08 '17 at 13:45
  • @nipuna777 yeah, that would be perfect =). But you can also help by just explaining how I can effectively compare my dates for each month and year. Thanks for your help – Dario Dec 08 '17 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?*](https://stackoverflow.com/questions/2587345/why-does-date-parse-give-incorrect-results) – RobG Dec 08 '17 at 21:27

5 Answers5

4

Edit: Edited example with Object.assign instead of Object spread.

You'll need to use reduce to sum the prices. See comments 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);
ChrisR
  • 3,922
  • 1
  • 14
  • 24
  • be careful with the [Spread-Operator](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/Spread_operator) – Crappy Dec 08 '17 at 14:27
  • I get an error now, do you know why? Module build failed: SyntaxError: Unexpected token ...acc, // Old values – Dario Dec 08 '17 at 14:42
  • yes, thats what i mentioned: more info https://stackoverflow.com/questions/43594687/unexpected-token-on-spread-operator-in-chrome – Crappy Dec 08 '17 at 14:58
  • I edited my example and replaced Object spread with `Object.assign()`. – ChrisR Dec 08 '17 at 15:37
  • @Crappy—there is no spread operator, there is [*spread syntax*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator). ;-) The same punctuator is also used for [*rest parameters*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters). – RobG Dec 08 '17 at 21:28
2

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);
Crappy
  • 441
  • 3
  • 7
1

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);
  
  
D Lowther
  • 1,609
  • 1
  • 9
  • 16
0
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
]
Tokipudi
  • 103
  • 1
  • 8
0

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);
RobG
  • 142,382
  • 31
  • 172
  • 209