Ayoub k
Ayoub k

Reputation: 8918

Merge two objects inside array of objects

I have the following array:

[
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 5,
        "date": "2018-11-27T00:00:00.000Z"
    },
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 2,
        "date": "2018-11-27T00:00:00.000Z"
    },
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 1,
        "date": "2018-12-27T00:00:00.000Z"
    },
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 2,
        "date": "2018-12-27T00:00:00.000Z",
    }
]

And i want to merge the object based on idItem and date and calculate the average mark so i can have the following:

[
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 3.5,
        "date": "2018-11-27T00:00:00.000Z"
    },
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 1.5,
        "date": "2018-12-27T00:00:00.000Z"
    }
]

Upvotes: 0

Views: 79

Answers (4)

Max Voisard
Max Voisard

Reputation: 1942

First, I'll declare the array we will be using in this example:

var array = [
   {
      "idItem": "5d656f10394d6524c821f1b1",
      "mark": 5,
      "date": "2018-11-27T00:00:00.000Z"
   },
   {
      "idItem": "5d656f10394d6524c821f1b1",
      "mark": 2,
      "date": "2018-11-27T00:00:00.000Z"
   },
   {
      "idItem": "5d656f10394d6524c821f1b1",
      "mark": 1,
      "date": "2018-12-27T00:00:00.000Z"
   },
   {
      "idItem": "5d656f10394d6524c821f1b1",
      "mark": 2,
      "date": "2018-12-27T00:00:00.000Z",
   }
];

So, here's the situation: I have one program that does work, but another implementation that almost works. I'll start with the one that does work, and it uses the alasql library:

To load the dependency, use the following <script> tag:

<script src="https://cdn.jsdelivr.net/npm/[email protected]">

Implementation:

var newArray = alasql('SELECT idItem, AVG([mark]) AS [mark], date AS [date] 
FROM ? GROUP BY date',[array]);
console.log(JSON.stringify(newArray));

This next program only calculates the average mark for all IDs not taking into account the date (so the resulting average is 2.5). Perhaps someone can figure out how to properly run this program and edit my answer:

var sum = {};
for(var i = 0; i < array.length; i++) {
   var ele = array[i];
   if (!sum[ele.idItem]) {
      sum[ele.idItem] = {};
      sum[ele.idItem]["sum"] = 0;
      sum[ele.idItem]["count"] = 0;
   }
   sum[ele.idItem]["sum"] += ele.mark;
   sum[ele.idItem]["count"]++;
}
var result = [];
for (var idItem in sum) {
    result.push({idItem: idItem, mark: sum[idItem]["sum"] / sum[idItem]["count"]});
}
console.log(JSON.stringify(result));

Upvotes: 0

Djaouad
Djaouad

Reputation: 22794

If id and date strings don't have a comma (,) in them, then you can use this solution, I first generate an object where the keys are a concatenation of the id and the date, and the values are objects that have the number of elements that have the id and date of the key, and the sum of their marks, this object will then be used to construct the desired array:

var arr = [
  { "idItem": "5d656f10394d6524c821f1b1", "mark": 5, "date": "2018-11-27T00:00:00.000Z" },
  { "idItem": "5d656f10394d6524c821f1b1", "mark": 2, "date": "2018-11-27T00:00:00.000Z" },
  { "idItem": "5d656f10394d6524c821f1b1", "mark": 1, "date": "2018-12-27T00:00:00.000Z" },
  { "idItem": "5d656f10394d6524c821f1b1", "mark": 2, "date": "2018-12-27T00:00:00.000Z" }
];

var obj = {};

arr.forEach((o) => {
  var k = o.idItem + ',' + o.date;
  if (obj.hasOwnProperty(k)) {
    obj[k].s += o.mark;
    obj[k].n += 1;
  } else {
    obj[k] = {s: o.mark, n: 1};
  }
});

var result = [];

Object.keys(obj).forEach((k) => {
  var p = k.split(',');
  result.push({idItem: p[0], date: p[1], mark: obj[k].s / obj[k].n})
});

console.log(result);

Upvotes: 1

Danny Buonocore
Danny Buonocore

Reputation: 3777

Try this:

const input = [
  { idItem: "5d656f10394d6524c821f1b1", mark: 5, date: "2018-11-27T00:00:00.000Z" },
  { idItem: "5d656f10394d6524c821f1b1", mark: 2, date: "2018-11-27T00:00:00.000Z" },
  { idItem: "5d656f10394d6524c821f1b1", mark: 1, date: "2018-12-27T00:00:00.000Z" },
  { idItem: "5d656f10394d6524c821f1b1", mark: 2, date: "2018-12-27T00:00:00.000Z" }
];

const ids = new Set(input.map(e => `${e.idItem} ${e.date}`));
const grouped = [...ids].map(id => input.filter(
  e => `${e.idItem} ${e.date}` === id
)).map(
  group => ({
    ...group[0],
    mark: group.reduce((acc, cur) => cur.mark + acc, 0) / group.length,
  })
);

You can see the result logged out here

Probably not the most performant, but it works. First we get a set of all the combinations of id and date, then filter the input for each combination. For each group we then copy the date and id over to the result, and reduce the entries to get the average.

Upvotes: 1

Jasper Bernales
Jasper Bernales

Reputation: 1681

Im using lodash groupBy.

Object.entries(_.groupBy([
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 5,
        "date": "2018-11-27T00:00:00.000Z"
    },
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 2,
        "date": "2018-11-27T00:00:00.000Z"
    },
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 1,
        "date": "2018-12-27T00:00:00.000Z"
    },
    {
        "idItem": "5d656f10394d6524c821f1b1",
        "mark": 2,
        "date": "2018-12-27T00:00:00.000Z",
    }
], e => `${e.idItem}_${e.date}`)).reduce((acc, [key, value]) => {
  const mark = value.reduce((sum, el) => sum + el.mark, 0) / value.length
 const [idItem, date] = key.split('_')
  return [...acc, { idItem, date, mark }]
}, [])

Upvotes: 1

Related Questions