1

I have lists of arrays like this:

var a = [
   {  month: 'January', count: 3 },
   {  month: 'February', count: 5 }, 
   {  month: 'March', count: 4 }
];
var b = [
   {  month: 'January', count: 4 },
   {  month: 'February', count: 5 }, 
   {  month: 'March', count: 1 }
];

And I want to create a "total" list, using Underscore, that looks like this:

var totals = [
   {  month: 'January', count: 7 },
   {  month: 'February', count: 10 }, 
   {  month: 'March', count: 5 }
];

I don't know in advance what the values of "month" will be, so I need to allow for this.

This is as far as I've got, but it's not very elegant. Is there a nicer way?

var totals = [];
_.each([a, b], function(d) { 
   _.each(items, function(e) { 
    var matchingItem = _.filter(totals, function(item, i) {
      return (item.month === e.month);
    });
    if (matchingItem) {
       var i = totals.indexOf(matchingItem);
       totals[i].count += e.count;
    } else { 
       var newItem = e;
       totals.push(e);
    }

   });
};

6 Answers 6

2

Using Underscore functions, you may do it like this, using a temporary object:

var total = [];
var tmp = {};

_.each(_.flatten([a,b]), function (obj) {
    tmp[obj.month] = tmp[obj.month] ? tmp[obj.month] + obj.count : obj.count;
});
_.each(tmp, function (c,m) {
    total.push({month: m, count: c});
});

See code there : http://jsfiddle.net/8cxQB/

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

Comments

2

This solution concatenates all the arrays, groups them by month and then spits out the totals:

var months = a.concat(b);

var totals = _.map( _.groupBy(months, 'month'), function(values, month){
    return {
        month: month,
        count: _.reduce(values, function(memo,value){
            return memo + value.count
        }, 0)
    }
});

If there are more data arrays then the months array could be built like so:

var months = a.concat(b, c, d); // etc

Comments

1
var totals = {};

a.concat(b).forEach(function(el) {
  if(totals[el.month])
    totals[el.month].count = totals[el.month].count + el.count;
  else
    totals[el.month] = el;
})

console.log(totals);

1 Comment

I get { undefined: NaN }
1

If you're willing to add another plugin, linq.js would be your best bet http://linqjs.codeplex.com/

EDIT:

var a = [
   {  month: 'January', count: 3 },
   {  month: 'February', count: 5 }, 
   {  month: 'March', count: 4 }
];
var b = [
   {  month: 'January', count: 4 },
   {  month: 'February', count: 5 }, 
   {  month: 'March', count: 1 }
];

var queryResult = Enumerable.From(a).Union(b)
.GroupBy("$.month", "", function (key, g) { var result = { month: key, count: g.Sum("$.count") }; return result;})
.ToArray();

console.log(queryResult);

2 Comments

Linq instead of Underscore might be a solution, but then at least provide the code to solve the OP's problem instead of a random sample.
Sorry about that, thought the first example was straightforward enough. I posted a real answer using linq.js now. Hope this helps
0

Why not have done an object like this

var counts =  {
   'January': 3,
   'February': 5 , 
   'March': 4
}

and then you can acces it with counts.January directly and modify it or within a loop with counts[month_name] = ...

hope that help

1 Comment

That would undoubtedly be better, but unfortunately that's not the nature of the data I'm dealing with - it's returned from an API and I don't control the structure...
0

Although not very elegant, this solution is one-lineish and doesn't use third party libs.

Array.from([...a, ...b].reduce((p, c) => p.set(c.month, {month: c.month, count: (p.get(c.month)?.count ?? 0) + c.count}), new Map())).map(x => x[1])

// readable version
Array.from([...a, ...b]
  .reduce((previous, current) => {
    const count = (previous.get(current.month) ?? 0) + current.count
    return previous.set(current.month, { month: current.month, count })
  }, new Map()))
  .map(x => x[1])

Comments

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.