0

Am trying to sum the number of tickets per month. the array I have:

[['tickets', 'month','year'], [1, "june",2016],[3, "june",2015],[1, "december",2015]]

The result I want :

[['year', 'june','december'], [2016,1,0],[2015,3,1]]

but I can't figure out how to do it with javascript. any help? thanks,

6
  • 6
    How does ['year', 'june','december'] come to be? Have you tried anything at all? Commented Apr 26, 2017 at 16:04
  • It's certainly doable, but there are much better data structures (namely an object) to store this data. Are you locked into the data structure provided in your desired output? Commented Apr 26, 2017 at 16:05
  • @FelixKling The first element in the array are essentially headers. They line up with the other array elements' data Commented Apr 26, 2017 at 16:06
  • @mhodges: Ah now I see. Thanks! Commented Apr 26, 2017 at 16:06
  • @mhodges am working with Google Line chart. the data passed to the chart.draw function must be stored this way. Commented Apr 26, 2017 at 16:14

3 Answers 3

1

One possible approach to this: I'm grouping the dates into an object, creating a unique array of the months that have an entry, and then processing it into the array you want.

var inArray = [['tickets', 'month','year'], [1, "june",2016],[3, "june",2015],[1, "december",2015]]
var validMonths = {}

function dateReducer (acc, curr) {
  var year = curr[2]
  var month = curr[1]
  var num = curr[0]
  if (year === "year"){return acc}
  validMonths[month] = 0 // build unique array of valid months
  
  if (!acc[year]) {
    acc[year] = {}
    acc[year][month] = num
  } else {
    if (!acc[year][month]) {
      acc[year][month] = num
    } else {
      acc[year][month] += num
    }
  }
  return acc
}

var obj = inArray.reduce(dateReducer, {})
// This may be fine, depending on your needs
validMonths = Object.keys(validMonths)
var outArray = ["year"].concat(validMonths)

for (var year in obj) {
  if (obj.hasOwnProperty(year)){
    var subArray = [year]
    validMonths.forEach(v => {
      subArray.push(obj[year][v] || 0)
    })
    outArray.push(subArray)
  }
}

console.log(outArray)

Sign up to request clarification or add additional context in comments.

2 Comments

Don't know if the OP requires it, but this solution does not preserve the order of the years
True, that can easily be implemented by a sort if that's an important factor.
1

You could use a dynamic approach for an arbitrary count of month with a hash table as reference to the years.

var data = [['tickets', 'month', 'year'], [1, "june", 2016], [3, "june", 2015], [1, "december", 2015]],
    columns = ['year', 'june', 'december'],
    temp = data.slice(1).reduce(function (result, item) {
        result[item[2]] = result[item[2]] || {};
        result[item[2]][item[1]] = item[0];
        return result;
    }, {}),
    result = Object.keys(temp).sort(function (a, b) { return b - a; }).map(function (year) {
        return columns.map(function (month, i) {
            return i ? temp[year][month] || 0 : +year;
        });
    });

result.unshift(columns);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

4 Comments

Why single-letter variable names? That is probably one of the single worst things you can do for someone who is trying to learn and incorporate this into their own code.
its just like i as count variable, i choose a for an item of the array and r as result/return value.
It's lazy, ambiguous, hard to read, and not self-documenting. Different letters/abbreviations mean different things to different people. Even though it may make sense to you, a for item makes no sense to me.
@mhodges, now with talking var names.
0

You can actually get some pretty cool use out of ES6 features here (spread syntax and array destructuring)

The idea is to create an object containing your years/months/total tickets from your original data source, and then you can use that object to map the data into the structure you require, like so:

var data = [['tickets', 'month', 'year'], [1, "june", 2016], [3, "june", 2015], [1, "december", 2015]];

function mapTicketData(data) {
  var dataMap = data.slice(1).reduce(function(results, current, index) {
    var [tickets, month, year] = current;
    if (results.months.indexOf(month) === -1) {
      results.months.push(month);
    }
    if (results.years.indexOf(year) === -1) {
      results.years.push(year);
    }
    if (!results[year]) {
      results[year] = {};
    }
    if (!results[year][month]) {
      results[year][month] = 0;
    }
    results[year][month] += tickets;

    return results;
  }, { months: [], years: [] });

  return [['year', ...dataMap.months], ...dataMap.years.map(function(year) {
      return [year].concat(dataMap.months.map(function(month) {
        return dataMap[year][month] || 0;
      }));
    })
  ];
}

console.log(mapTicketData(data));

1 Comment

Thank you @mhodges! that's exactly what I needed

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.