miuosh
miuosh

Reputation: 906

group by unique values of property

I got array of objects (tasks). Each task has property named 'category' and 'duration'.

var tasks = [
{
 _id : "123",
 category : "someCategory",
 duration: "3432"
},
{
 _id : "113",
 category : "someCategory",
 duration: "23"
},
{
 _id : "124",
 category : "someCategory 2",
 duration: "1343"
},

{
 _id : "2124",
 category : "someCategory 2",
 duration: "1343"
},

{
 _id : "7124",
 category : "someCategory 5",
 duration: "53"
},

{
 _id : "34",
 category : "someCategory",
 duration: "753"
}
]

I'd like to group tasks by category (unique) and sum duration of each category.

Result should be like:

 var categories = ["someCategory", "someCategory 2" ... ]
    var duration = [ <summary duration of "someCategory">, <summary duration of "someCategory 2">, ... ]

I have groupBy function which gives me all categories. I can find uniqueCategories using Array.prototype.filter but still I have to sum 'duration'.

  var categoryMap = groupBy(tasks, 'category');
      var uniqueCategories = categoryMap.get('category').filter((x, i, a) => a.indexOf(x) == i);

    function groupBy(list, property) {
            var map = new Map();
            list.forEach(function(item) {
                const key = property;
                if(!map.has(key)) {
                  map.set(key, [item[key]])
                } else {
                  map.get(key).push(item[key])
                }
            })
            return map;
          }

Then I create array of { key : value } and sum by key i.e.

[
    {
     someCategory : 3432
    },
   {
     someCategory : 23
    }
.
.
.
]

Finally I achieve my goal but code looks messy and isn't readable at all... Is there better approach to do it in Javascript?

Upvotes: 0

Views: 4197

Answers (2)

kind user
kind user

Reputation: 41893

var tasks = [{"_id":"123","category":"someCategory","duration":"3432"},{"_id":"113","category":"someCategory","duration":"23"},{"_id":"124","category":"someCategory 2","duration":"1343"},{"_id":"2124","category":"someCategory 2","duration":"1343"},{"_id":"7124","category":"someCategory 5","duration":"53"},{"_id":"34","category":"someCategory","duration":"753"}]  

var arr = [];
tasks.forEach(v => arr.push(v.category));
var newArr = [...new Set(arr)];
var arr2 = [];

newArr.forEach(function(v) {
  var obj = {};
  obj.category = v;
  obj.duration = 0;
  arr2.push(obj);
});

arr2.forEach(v => tasks.forEach(c => c.category == v.category ? v.duration += parseInt(c.duration) : v));
console.log(arr2);

Upvotes: 2

Nenad Vracar
Nenad Vracar

Reputation: 122047

You could just return one object with category: duration.

var tasks = [{"_id":"123","category":"someCategory","duration":"3432"},{"_id":"113","category":"someCategory","duration":"23"},{"_id":"124","category":"someCategory 2","duration":"1343"},{"_id":"2124","category":"someCategory 2","duration":"1343"},{"_id":"7124","category":"someCategory 5","duration":"53"},{"_id":"34","category":"someCategory","duration":"753"}]  

var result = tasks.reduce(function(r, e) {
  r[e.category] = (r[e.category] || 0) + +e.duration
  return r;
}, {})

console.log(result)

Upvotes: 3

Related Questions