2

I am trying to combine an array of objects while removing duplicates based of a particular value, in this case it's id. I want to merge the other properties in each of the objects.

This is what I have:

var myArray = [
    {
        id : 1,
        rendering : 0,
        completed : 1
    },
    {
        id : 2,
        rendering : 0,
        completed : 1
    },
    {
        id : 3,
        rendering : 0,
        completed : 1
    },
    {
        id : 1,
        rendering : 1,
        completed : 0
    },
    {
        id : 2,
        rendering : 1,
        completed : 0
    },
    {
        id : 3,
        rendering : 1,
        completed : 0
    },
]

This is what I want :

var myDesiredArray = [
    {
        id : 1,
        rendering: 1,
        completed: 1
    },
    {
        id : 2,
        rendering: 1,
        completed: 1
    },
    {
        id : 3,
        rendering: 1,
        completed: 1
    },
]

I'd be happy with straight javascript or underscore/lodash. Any suggestions would be greatly appreciated.

abyrne85
  • 1,370
  • 16
  • 33

6 Answers6

3

Using underscore, here's one way to do it

Define a sum function

function sum(numbers) {
    return _.reduce(numbers, function(result, current) {
        return result + parseFloat(current);
    }, 0);
}

Then groupby over id and pluck needed values and apply sum over them.

_.chain(myArray )
    .groupBy("id")
    .map(function(value, key) {
        return {
            id: key,
            rendering : sum(_.pluck(value, "rendering")),
            completed: sum(_.pluck(value, "completed"))
        }
    })
    .value();
Zero
  • 74,117
  • 18
  • 147
  • 154
2
function merge(arr) {
  var uniqItems = arr.reduce(function(memo, item) {
    if (!memo[item.id]) {
      memo[item.id] = item;
    } else {
      Object.keys(item).forEach(function(k) {
        if (item[k]) { memo[item.id][k] = item[k]; }
      });
    }
    return memo;
  }, {});
  return Object.keys(uniqItems).map(function(k) {
    return uniqItems[k];
  });
}

merge(myArray); // => myDesiredArray
maerics
  • 151,642
  • 46
  • 269
  • 291
1

There is multiple way to do, but as you want to check uniqueness, the easiest way would be to use a temporary map, indexed by your ids, on which you can accumulate the values of the other properties.

When the accumulation is done, convert the Map to a straight array, et voila.

You would do it like so:

var array;    // defined elsewhere
var tmp = {}; // temporary map

// accumulate the properties in the map
array.forEach(function(elt) {
  if (tmp[elt.id] == null)
    tmp[elt.id] = elt
  else {
    tmp[elt.id].prop += elt.prop;
    // (...) do the accumulation here
  }
});

// then convert this map to an array by iterating on the ids
Object.keys(tmp).map(function(id) { return tmp[id]; })
0

Pure JS solution

The first thing you should do is use a map for fast lookup:

var map = {};
myArray.forEach(function(item){
  var mapItem = map[item.id] || {
    id : item.id,
    rendering : 0,
    completed : 0
  }
  mapItem.rendering += item.rendering;
  mapItem.completed += item.completed;
  map[item.id] = mapItem;
});

Then you can convert the map back to an array:

var myDesiredArray = [];
for (var id in map) {
  myDesiredArray.push(map[id]);
}
Tibos
  • 27,507
  • 4
  • 50
  • 64
0

Here is another vanilla js version using a functional approach:

myArray.reduce(function(prev, cur) {
  if (prev[cur.id]) {
    prev[cur.id].rendering += cur.rendering;
    prev[cur.id].completed += cur.completed;
  } else {
    prev[cur.id] = cur;
  }
  return prev;
}, []).filter(function(val) {
  return val;
});
Rob M.
  • 35,491
  • 6
  • 51
  • 50
0

Solution with less loop possible

function myMerge(myArray) {
    var temp = {},
        myDesiredArray = [];
    for (var i = 0; i < myArray.length; ++i) {
        var elem = myArray[i];
        if (!temp[elem.id]) {
            temp[elem.id] = {
                'id': elem.id,
                'rendering': elem.rendering,
                'completed': elem.completed,
            };
            myDesiredArray.push(temp[elem.id])
        }
        temp[elem.id].rendering += elem.rendering;
        temp[elem.id].completed += elem.completed;

    }
    return myDesiredArray;
}
Raúl Martín
  • 4,471
  • 3
  • 23
  • 42