0

based on Summarize array of objects and calculate average value for each unique object name I could partially solve my problem. I did group by ed_id but I need to group by ed_id and then el_id and to get average value based on those group by, so here is my array of hashes:

a = [
{
 ed_id: 1,
 el_id: 127,
 value: 2
},
{
 ed_id: 1,
 el_id: 127,
 value: 6
},
{
 ed_id: 1,
 el_id: 129,
 value: 3
},
{
 ed_id: 1,
 el_id: 129,
 value: 13
},
{
 ed_id: 2,
 el_id: 127,
 value: 5
},
{
 ed_id: 2,
 el_id: 127,
 value: 9
},
{
 ed_id: 2,
 el_id: 129,
 value: 10
 }
]

So final array of hashes needs to look like:

b = [
{
 ed_id: 1,
 el_id: 127,
 value: 4
},
{
 ed_id: 1,
 el_id: 129,
 value: 8
},
{
 ed_id: 2,
 el_id: 127,
 value: 7
},
{
 ed_id: 2,
 el_id: 129,
 value: 10
 }
]

Thank you!

1
  • If you can group by ed_id it should not be that much trouble to then group the result by el_id Commented Jan 23, 2016 at 19:13

2 Answers 2

1

Methods/Functions used:

Array.prototype.reduce(callback[, initialValue]): The reduce() method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.

Object.prototype.keys(obj): The Object.keys() method returns an array of a given object's own enumerable properties, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).

var data = [
  { ed_id: 1, el_id: 127, value: 2  },
  { ed_id: 1, el_id: 127, value: 6  },
  { ed_id: 1, el_id: 129, value: 3  },
  { ed_id: 1, el_id: 129, value: 13 },
  { ed_id: 2, el_id: 127, value: 5  },
  { ed_id: 2, el_id: 127, value: 9  },
  { ed_id: 2, el_id: 129, value: 10 }
]


// group the data
var groupedData = data.reduce(function(l, r) {
  // construct a unique key out of the properties we want to group by
  var key = r.ed_id + "|" + r.el_id;

  // check if the key is already known
  if (typeof l[key] === "undefined") {
    // init with an "empty" object
    l[key] = {
      sum: 0,
      count: 0
    };
  }
  
  // sum up the values and count the occurences
  l[key].sum += r.value;
  l[key].count += 1;

  return l;
}, {});

console.log(JSON.stringify(groupedData));
//{"1|127":{"sum":8,"count":2},"1|129":{"sum":16,"count":2},"2|127":{"sum":14,"count":2},"2|129":{"sum":10,"count":1}}


// calculate the averages
var avgGroupedData = Object.keys(groupedData)
  // iterate over the elements in <groupedData> and transform them into the "old" format
  .map(function(key) {
    // split the constructed key to get the parts
    var keyParts = key.split(/\|/);

    // construct the "old" format including the average value
    return {
      ed_id: parseInt(keyParts[0], 10),
      el_id: parseInt(keyParts[1], 10),
      value: (groupedData[key].sum / groupedData[key].count)
    };
  });

console.log(JSON.stringify(avgGroupedData));
// [{"ed_id":1,"el_id":127,"value":4},{"ed_id":1,"el_id":129,"value":8},{"ed_id":2,"el_id":127,"value":7},{"ed_id":2,"el_id":129,"value":10}]

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

Comments

1

This should do what you want. Broken down into small functions for readability:

var groupingKey = function(item){
    return item.ed_id + "/" + item.el_id
}

var sum = function(total, item){
    return total + item.value;
}

var average = function(items){
    return _.reduce(items, sum, 0) / items.length;
}

var groupsToResult = function(groups){

    return {
        ed_id: groups[0].ed_id,
        el_id: groups[0].el_id,
        value: average(groups)
    }
}

var result = _.chain(a)
    .groupBy(groupingKey)
    .map(groupsToResult)
    .value();

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.