0

I have an array of objects:

[{person:101, year: 2012}, {person:102, year: 2012}, {person:103, year: 2013}]

And I want to be able to return an aggregate count for each year (much like a group by in SQL would achieve) in the format of:

[{year: 2012, count:2}, {year: 2013, count:1}]

What is the best way to achieve this in Vanilla Javascript?

  • 1
    Would a more useful output format not be `{"2012":2, "2013":1}`? Because this would be *very* easy to achieve with a simple loop ;) – Niet the Dark Absol May 07 '17 at 11:11
  • 2
    Check [that](http://stackoverflow.com/questions/5667888/counting-the-occurrences-of-javascript-array-elements) and have a try next time. You have google and not even try? – Sylogista May 07 '17 at 11:11

4 Answers4

1

As others have mentioned, an object would be a better fit for aggregating the data. You could use a normal loop, or reduce to do it:

var data = [{person:101, year: 2012}, {person:102, year: 2012}, {person:103,
year: 2013}];

var yearCounts = data.reduce(function (result, person) {
  var currentCount = result[person.year] || 0;
  result[person.year] = currentCount + 1;
  return result;
}, {});

console.log(yearCounts);

If you really need it as an array, you could the loop over the object and convert it to an array:

var data = [{person:101, year: 2012}, {person:102, year: 2012}, {person:103,
year: 2013}];

var yearCounts = data.reduce(function (result, person) {
  var currentCount = result[person.year] || 0;
  result[person.year] = currentCount + 1;
  return result;
}, {});

var year,
  yearCountArray = [];
for (year in yearCounts) {
  if (yearCounts.hasOwnProperty(year)) {
    yearCountArray.push({
      year: year,
      count: yearCounts[year]
    });
  }
}

console.log(yearCountArray);
Useless Code
  • 12,123
  • 5
  • 35
  • 40
1

You can use a combination of .reduce() with a Map to build a key-value pair object, where the key is the thing you want to group by (ie: year) and the value is the frequency of that property. You can then use Array.from() to build an array of objects from the Map:

const data = [{person:101, year: 2012}, {person:102, year: 2012}, {person:103, year: 2013}];

const res = Array.from(
  data.reduce((map, {year}) => map.set(year, (map.get(year) ?? 0)+1), new Map), 
  ([year, count]) => ({year, count})
);
console.log(res);
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
0

You need to use simple for like below :

var data = [{person:101, year: 2011}, 
            {person:102, year: 2012}, 
            {person:103, year: 2011}];


var result = {},
  i;

for(i=0; i < data.length; i++){
  let year = data[i].year;
  if(typeof result[year] === 'undefined'){
    result[year] = 1;
  }
  else{
    result[year] = result[year] + 1;
  }

}

console.log(result);
Useless Code
  • 12,123
  • 5
  • 35
  • 40
Mihir Bhende
  • 8,677
  • 1
  • 30
  • 37
0

Use a generic group by key reducer that will count the number of items in each group. I will take inspiration from a previous answer of mine. This function will return another function that act as a reducer, but you can always give it the key that you want as a parameter.

const groupByCounter = key => (result,current) => {
  const item = Object.assign({},current);
  if (typeof result[current[key]] == 'undefined'){
    result[current[key]] = 1;
  }else{
    result[current[key]]++;
  }
  return result;
};

const data = [{person:101, year: 2012}, {person:102, year: 2012}, {person:103, year: 2013}];

const group = data.reduce(groupByCounter('year'),{});
console.log(group);
David Lemon
  • 1,560
  • 10
  • 21