6

I have an array of objects like this:

array1 = [
  {field: 'username', display: 'username', hide: true},
  {field: 'age', display: 'Age', hide: true},
  {field: 'height', display: 'Height', hide: true}
]

Then I have array2:

array2 = [
  {field: 'username', display: 'username 123', hide: false},
  {field: 'age', hide: false}
]

I want to merge these two arrays by their field i.e. the final result should be:

array3 = [
  {field: 'username', display: 'username 123', hide: false},
  {field: 'age', display: 'Age', hide: false},
  {field: 'height', display: 'Height', hide: true}
]

I tried var newObj = _.extend(array1, array2); but it didn't give me what I want.

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
user3214546
  • 6,523
  • 13
  • 51
  • 98
  • You are looking for concat. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat – Brian Dec 18 '14 at 22:24
  • @Brian: nope, he's doing some sort of updating. – Karoly Horvath Dec 18 '14 at 22:25
  • @Karoly Horvath Ah yes, he wants to merge the objects as well as the array – Brian Dec 18 '14 at 22:26
  • it's not even clear what the order will be if array2 contains new elements... is the order important? if not, go with a plain object (as a dictionary). if it does, an order dictionary would be better for the task. it helps with the lookups. – Karoly Horvath Dec 18 '14 at 22:27

1 Answers1

11

There's no one function that will do it, as its a fairly specialized operation. However, its a fairly simple composition of underscore functions:

_.values(_.extend(_.indexBy(array1, 'field'), _.indexBy(array2, 'field')))

We use indexBy to turn the arrays into objects keyed on the field value, and then extend does what we want. Finally, values turns it back into an array.

Note that while this doesn't make any guarantees about the order, in the current _ and v8 implementations the final array will be the things from array1 followed by the things from array2 that aren't in array1, sorted in the original ordering of each array.

Also note that the _.extend function is destructive on the first argument, while this doesn't change either array.

If you want to be sure that the order is the same as the original array1:

order = _.object(_.map(array1, function(obj, i) { return [obj.field, i]; }))
_.sortBy(_.values(_.extend(_.indexBy(array1, 'field'), _.indexBy(array2, 'field'))), function(obj) { order[obj.field]; })

Here we make a lookup table of positions and then sort the original solution based on the lookup table.

Aaron Dufour
  • 17,288
  • 1
  • 47
  • 69
  • thanks , that was what i want. How can i preserve the order . i mean i want that order should stay as of in first array. second array won't have any new elements. its only for updating – user3214546 Dec 18 '14 at 22:32
  • I have 2 list one is : `[{"image_id": 631293, "score": 73}, {"image_id": 11848407, "score": 56}]` second one is `[{"image_id": 631293, "article_id": 173}, {"image_id": 11848407, "article_id": 121}` if I try to apply your method, it does not change anything – Toucouleur Nov 21 '16 at 16:59
  • @Toucouleur You probably want to replace `'field'` with `'image_id'`, since it looks like that is your key field. – Aaron Dufour Nov 23 '16 at 05:01
  • I am almost 3 years late but this answer is exactly what I am looking for. Thank you so much! :) – Terry Jun 08 '17 at 11:37