Jaad
Jaad

Reputation: 89

Merging items of array of object if they have equal keys

Array of objects that I got

  [
    {
      "id": 1,
      "price": 100
    },
    {
      "id": 1,
      "price": 80
    },
    {
      "id": 2,
      "price": 8
    },
    {
      "id": 1,
      "price": 85
    }
  ]

Array of objects that I am trying to do

  [
    {
      "id": 1,
      "price": 88.33 // AVERAGE VALUE BETWEEN DUPLICATED OBJECTS
    },
    {
      "id": 2,
      "price": 8
    }
  ]

I am merging and getting the average price for duplicated objects.

What I have done:

I have tried to use filter() function but I removed the duplicated without merging the prices.

Upvotes: 0

Views: 60

Answers (3)

Siva Kondapi Venkata
Siva Kondapi Venkata

Reputation: 11001

Use forEach loop and build an object with keys as id and aggregate price. Use Object.values of above object and calculate the averages.

const data = [
  {
    id: 1,
    price: 100,
  },
  {
    id: 1,
    price: 80,
  },
  {
    id: 2,
    price: 8,
  },
  {
    id: 1,
    price: 85,
  },
];

const process = (arr) => {
  const res = {};
  arr.forEach(({ id, price }) => {
    res[id] ??= { id, sum: 0, count: 0 };
    res[id].sum += price;
    res[id].count += 1;
  });
  return Object.values(res).map(({ id, sum, count }) => ({
    id,
    price: sum / count,
  }));
};

console.log(process(data));

Upvotes: 0

Ele
Ele

Reputation: 33726

If you want to avoid extra loops and extra properties is not a problem, you can use a getter for each object as follow:

You can use the function Array.prototype.reduce for grouping objects by id and the function Object.values for extracting the grouped values.

The getter price calculates the average when this property is accessed.

Extra properties:

{
    count: Integer // count of repeated ids.
    sum: Double // total sum of prices
}

const arr = [    {      "id": 1,      "price": 100    },    {      "id": 1,      "price": 80    },    {      "id": 2,      "price": 8    },    {      "id": 1,      "price": 85    }  ],
      result = Object.values(arr.reduce((r, {id, price}) => {
        let current = (r[id] || (r[id] = {id, sum: 0, count: 0, get price() {
          return this.sum / this.count;
        }}));

        current.sum += price;
        current.count++;

        return r;
      }, {}));

console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Nick Parsons
Nick Parsons

Reputation: 50639

You can use .reduce() with an ES6 Map. By using reduce() you can accumulate all objects into a Map, where the key is the id from the object and the value is an accumulated array of price values for the given id. You can then convert the Map back into an array using Array.from(), where you can provide a mapping function to convert the [key, value] pairs from the map into an object. The object's price key will be the sum of all numbers in the value array (arr) divided by the length of the array, which will give you the average.

See example below:

const arr = [ { "id": 1, "price": 100 }, { "id": 1, "price": 80 }, { "id": 2, "price": 8 }, { "id": 1, "price": 85 } ];

const res = Array.from(arr.reduce((m, {id, price}) => {
  return m.set(id, [...(m.get(id) || []), price]);
}, new Map), ([id, arr]) => ({id, price: arr.reduce((t, n) => t+n, 0) / arr.length}));

console.log(res);

Upvotes: 1

Related Questions