bikash
bikash

Reputation: 463

Javascript reduce to group objects based on condition

I have the following data:

var foo = [
    {'MonthNo' : 03, 'CustomerTypeID' : 1 , 'TotalSales' : 45 },
    {'MonthNo' : 03, 'CustomerTypeID' : 2 , 'TotalSales' : 64 },
    {'MonthNo' : 03, 'CustomerTypeID' : 4 , 'TotalSales' : 89 },
    {'MonthNo' : 03, 'CustomerTypeID' : 5 , 'TotalSales' : 42 },
    {'MonthNo' : 04, 'CustomerTypeID' : 1 , 'TotalSales' : 66 },
    {'MonthNo' : 04, 'CustomerTypeID' : 2 , 'TotalSales' : 78 },
    {'MonthNo' : 04, 'CustomerTypeID' : 5 , 'TotalSales' : 20 },
    {'MonthNo' : 04, 'CustomerTypeID' : 6 , 'TotalSales' : 10 }
];

I want to convert this into the following

{'MonthNo' : 03, 'CustomerTypeID' : 1 , 'TotalSales' : 198 },
{'MonthNo' : 03, 'CustomerTypeID' : 5 , 'TotalSales' : 42 },
{'MonthNo' : 04, 'CustomerTypeID' : 1 , 'TotalSales' : 144 },
{'MonthNo' : 04, 'CustomerTypeID' : 5 , 'TotalSales' : 20 },
{'MonthNo' : 04, 'CustomerTypeID' : 6 , 'TotalSales' : 10 }

For each MonthNo, except for CustomerTypeID 5 & 6, I want to add up the TotalSales value. For Ex: MonthNo 03 for CustomerTypeID 1,2,4 their TotalSales value is summed up to 198.similarly for MonthNo 04, TotalSales value of CustomerTypeID 1 & 2 is added up to 144.

I tried using the reduce function

var result = foo.reduce(function(obj,item){
    // code here
},{});

However , I not able to segregate them to get the desired result.Any help would be appreciated.

Upvotes: 5

Views: 1509

Answers (5)

Jonas Wilms
Jonas Wilms

Reputation: 138477

answer={};
for(i=0;i<foo.length;i++){
elem=foo[i];
answer[elem["MonthNo"]]=answer[elem["MonthNo"]]||{};
answer[elem["MonthNo"]][elem["CustomerTypeId"]]=answer[elem["MonthNo"]][elem["CustomerTypeId"]]||0;
     answer[elem["MonthNo"]][elem["CustomerTypeId"]]+=elem["TotalSales"];
}

This will result in:

answer:{
   15 (monthNo):{
       12 (Customer type id):123(Total Sales)
     }}

Now can print (or push to an array):

for(month in answer){
 for(id in answer[month]){
   console.log(month+" "+id+":"+answer[month][id];
 }}

Upvotes: 0

Ovidiu Dolha
Ovidiu Dolha

Reputation: 5413

A two-step functional approach with reduce:

var groups = foo.reduce(function(groups,item){
  var newGroup;
  var groupId = item.MonthNo;
  if (item.CustomerTypeID === 5 || item.CustomerTypeID === 6) {
    groupId = groupId + '-' + item.CustomerTypeID;
  }
  if (!groups[groupId]) {
    groups[groupId] = [];
  }
  groups[groupId].push(item);
  return groups;
},{});

var result = Object.keys(groups).reduce(function(res, groupId) {
  var merged = groups[groupId].reduce(function(merged, item) { 
    if (!merged.MonthNo) {
        merged.MonthNo = item.MonthNo;
    }
    if (!merged.CustomerTypeID) {
        merged.CustomerTypeID = item.CustomerTypeID;
    }
    merged.TotalSales = merged.TotalSales + item.TotalSales;
    return merged;
  }, {TotalSales: 0});
  res.push(merged);
  return res;
}, []);

console.log(result);

NOTE: with ES6 this would look nicer.

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386756

You could check the special cases and use a hash table for referencing the right object for counting.

var foo = [{ MonthNo: '03', CustomerTypeID: 1, TotalSales: 45 }, { MonthNo: '03', CustomerTypeID: 2, TotalSales: 64 }, { MonthNo: '03', CustomerTypeID: 4, TotalSales: 89 }, { MonthNo: '03', CustomerTypeID: 5, TotalSales: 42 }, { MonthNo: '04', CustomerTypeID: 1, TotalSales: 66 }, { MonthNo: '04', CustomerTypeID: 2, TotalSales: 78 }, { MonthNo: '04', CustomerTypeID: 5, TotalSales: 20 }, { MonthNo: '04', CustomerTypeID: 6, TotalSales: 10 }],
    result = foo.reduce(function (hash) {
        return function (r, a) {
            var cType = [5, 6].indexOf(a.CustomerTypeID) === -1 ? 1 : a.CustomerTypeID,
                key = [a.MonthNo, cType].join('|');

            if (!hash[key]) {
                hash[key] = { MonthNo: a.MonthNo, CustomerTypeID: cType, TotalSales: 0 };
                r.push(hash[key]);
            }
            hash[key].TotalSales += a.TotalSales;
            return r;
        }
    }(Object.create(null)), []);
 
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

Azad
Azad

Reputation: 5272

this may help you sorted foo array by MonthNo

var foo = [

  { 'MonthNo': 03, 'CustomerTypeID': 1, 'TotalSales': 45 },
  { 'MonthNo': 03, 'CustomerTypeID': 2, 'TotalSales': 64 },
  { 'MonthNo': 03, 'CustomerTypeID': 4, 'TotalSales': 89 },
  { 'MonthNo': 03, 'CustomerTypeID': 5, 'TotalSales': 42 },
  { 'MonthNo': 04, 'CustomerTypeID': 1, 'TotalSales': 66 },
  { 'MonthNo': 04, 'CustomerTypeID': 2, 'TotalSales': 78 },
  { 'MonthNo': 04, 'CustomerTypeID': 5, 'TotalSales': 20 },
  { 'MonthNo': 04, 'CustomerTypeID': 6, 'TotalSales': 10 }

];

var curMonth = -1, result = [], curObj;

for (var i = 0; i < foo.length; i++) {

  if (foo[i].CustomerTypeID == 6 || foo[i].CustomerTypeID == 5) {
    result.push(foo[i]);
    continue;
  } else {
    if (curMonth != foo[i].MonthNo) {
      curMonth = foo[i].MonthNo;
      curObj = {
        MonthNo: foo[i].MonthNo,
        CustomerTypeID: 1,
        TotalSales: foo[i].TotalSales
      };
      result.push(curObj);
    } else {
      curObj.TotalSales += foo[i].TotalSales;
    }

  }

}

console.log(result);

Upvotes: 0

Geeky
Geeky

Reputation: 7496

You can check the existing arrays monthno and customerTypeID and add up the totalsales accordingly

check the following snippet

var foo = [{
  'MonthNo': 03,
  'CustomerTypeID': 1,
  'TotalSales': 45
}, {
  'MonthNo': 03,
  'CustomerTypeID': 2,
  'TotalSales': 64
}, {
  'MonthNo': 03,
  'CustomerTypeID': 4,
  'TotalSales': 89
}, {
  'MonthNo': 03,
  'CustomerTypeID': 5,
  'TotalSales': 42
}, {
  'MonthNo': 04,
  'CustomerTypeID': 1,
  'TotalSales': 66
}, {
  'MonthNo': 04,
  'CustomerTypeID': 2,
  'TotalSales': 78
}, {
  'MonthNo': 04,
  'CustomerTypeID': 5,
  'TotalSales': 20
}, {
  'MonthNo': 04,
  'CustomerTypeID': 6,
  'TotalSales': 10
}];
var reducedfoo = foo.reduce(function(previous, current, index) {
  var index = checkExistence(previous, current)
  if (index > -1)
    previous[index].TotalSales += current.TotalSales;
  else
    previous.push(current);
  return previous;
}, []);

function checkExistence(prev, curr) {
  var index = -1;
  Object.keys(prev).forEach(function(item) {
    if (prev[item].MonthNo === curr.MonthNo && curr.CustomerTypeID != 5 && curr.CustomerTypeID != 6)
      index = item;
  });
  return index;
}

console.log(reducedfoo);

Hope it helps

Upvotes: 0

Related Questions