2

I have an array of objects like so:

[
  {
    "id": "1",
    "location": "US"
  },
  {
    "id": "7",
    "location": "US"
  },
  {
    "id": "1",
    "location": "France"
  },
  {
    "id": "1",
    "location": "China"
  }
]

I would like to end up with a resulting array that looks like this:

[
  {
    "id": "1",
    "locations": ["US", "France", "China"]
  },
  {
    "id": "7",
    "locations": ["US"]
  }
]

Is there a solid way to accomplish this using underscore?

I'm contemplating looping through the array and for each id looping through the rest of the array and pushing location values to a locations array on that first object (by id), then at the end removing all duplicate objects (by id) which do not contain a locations property.

This is different from existing questions on SO that simply ask about removing duplicates. I am aiming to remove duplicates while also holding on to certain property values from these duplicates in an array on the 'surviving' object.

MattDionis
  • 3,534
  • 10
  • 51
  • 105
  • Native JS can handle this nicely, especially with some new ES6 features. Does it have to be underscore? – Sterling Archer Dec 09 '15 at 16:41
  • @SterlingArcher It dos not need to be underscore. I'm using underscore so just wanted to mention that in case there were ways to accomplish this easily using underscore methods. – MattDionis Dec 09 '15 at 16:42
  • Possible duplicate of [Remove Duplicates from JavaScript Array](http://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array) – Endless Dec 09 '15 at 16:43

3 Answers3

3

Solution in plain Javascript

var data = [{ "id": "9" }, { "id": "1", "location": "US" }, { "id": "7", "location": "US" }, { "id": "1", "location": "France" }, { "id": "1", "location": "China" }],
    result = [];

data.forEach(function (a) {
    a.location && !result.some(function (b) {
        if (a.id === b.id) {
            b.locations.push(a.location);
            return true;
        }
    }) && result.push({ id: a.id, locations: [a.location] });
});

document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • if you wanted to remove all objects with repeating locations, could that be done with just one loop? – Rick Jun 22 '17 at 04:54
  • @Arrow, it should be possible, either by look up or with a Map/Set or an object as hash table. the decision between map and set, is if the locations are themself unique without `id`. if you need `id` as well, you need a map for grouping. – Nina Scholz Jun 22 '17 at 07:21
  • nice will attempt. I don't know how you feel about answering late questions, where there is already a selected answer. It is probably not worth your time, but I like how you solve these problems. The question is here if you are interested. https://stackoverflow.com/questions/44686078/how-to-remove-any-objects-that-appear-more-than-once-in-an-array-of-objects# – Rick Jun 22 '17 at 07:48
1

You can use reduce function to transform your array.

var data = [
    { "id": "1", "location": "US" },
    { "id": "7", "location": "US" },
    { "id": "1", "location": "France" },
    { "id": "1", "location": "China" }
];

var result = data.reduce(function (prev, item) {
    var newItem = prev.find(function(i) {
        return i.id === item.id;
    });
    if (!newItem) {
        prev.push({id: item.id, locations: [item.location]});
    } else {
        newItem.locations.push(item.location);
    }
    return prev;
}, []);
madox2
  • 49,493
  • 17
  • 99
  • 99
1

And a version using underscore:

var result = _.chain(data)
    .groupBy('id')
    .map(function(group, id){
        return {
            id: id,
            locations: _.pluck(group, 'location')
        }
    })
    .value();
Gruff Bunny
  • 27,738
  • 10
  • 72
  • 59