jwald3
jwald3

Reputation: 19

Taking JSON data, grouping by a property, and aggregating using multiple aggregation functions

Here is a sample of JSON data of fruit weights and prices:

let fruitData = [{"fruit":"apple","weight":12,"price":1.80},
                 {"fruit":"apple","weight":15,"price":2.00},
                 {"fruit":"apple","weight":10,"price":1.60},
                 {"fruit":"banana","weight":22,"price":3.00},
                 {"fruit":"banana","weight":24,"price":3.20}]

If I want to group by the "fruit" property and return mean "weight" and "price" values for each fruit, what do I do to achieve this? The end result would be something like:

aggFruitData = [{"fruit":"apple","weight":12.3333,"price":1.8},
                {"fruit":"banana","weight":23,"price":3.1}]

The big hope is that the data can still be in an easy to manipulate form like JSON following the transformation. I know that SQL provides a groupby method, but I'm wondering if that is more efficient or if using native JS is more effective. Ideally, this could be something that could be scaled up, such as including another property to group by (maybe like month sold/month price was recorded). I'm open to using either vanilla JS methods or a library/framework meant to parse in this way— I just want efficiency with the project's execution

Upvotes: 0

Views: 476

Answers (1)

see sharper
see sharper

Reputation: 12045

Using vanilla js:

let fruitData = [{
    "fruit": "apple",
    "weight": 12,
    "price": 1.80
  },
  {
    "fruit": "apple",
    "weight": 15,
    "price": 2.00
  },
  {
    "fruit": "apple",
    "weight": 10,
    "price": 1.60
  },
  {
    "fruit": "banana",
    "weight": 22,
    "price": 3.00
  },
  {
    "fruit": "banana",
    "weight": 24,
    "price": 3.20
  }
];
const aggregated = Object.values(fruitData.reduce((current, item) => {
  if (!current[item.fruit]) {
    current[item.fruit] = {
      ...item,
      count: 1
    };
  } else {
    const i = current[item.fruit];
    i.weight = (i.weight * i.count + item.weight) / (i.count + 1);
    i.price = (i.price * i.count + item.price) / (i.count + 1);
    i.count++;
  }
  return current;
}, {})).map(({
  fruit,
  weight,
  price
}) => ({
  fruit,
  weight,
  price
}));
console.log(aggregated);
// [ { fruit: 'apple', weight: 12.333333333333334, price: 1.8 },
// { fruit: 'banana', weight: 23, price: 3.1 } ]

You could also use the lodash library (_.groupBy etc) functions for this. Note that the last .map is to strip out the count field only, but you may actually find it useful to have that!

Upvotes: 1

Related Questions