简体   繁体   中英

How to merge/concatenate values of same object properties in an array of objects using lodash?

I have an array of arrays of objects which looks like this:

let fruitSamples = [
  [
    {'id': 1,'type': 'apples','samples': [1, 2, 3]},
    {'id': 2,'type': 'bananas','samples': [1, 2, 7]},
    {'id': 3,'type': 'pears','samples': [1, 2, 3]}
  ],
  [
    {'id': 1,'type': 'apples','samples': [5, 2, 9]},
    {'id': 2,'type': 'bananas','samples': [1, 7, 7]},
    {'id': 3,'type': 'pears','samples': [12, 21, 32]}
  ],
  [
    {'id': 1,'type': 'apples','samples': [11, 2, 33]},
    {'id': 2,'type': 'bananas','samples': [17, 2, 67]},
    {'id': 3,'type': 'pears','samples': [91, 22, 34]}
  ]
];

I want to reduce and merge the above array using lodash into one so that the samples get concatenated together like so:

fruitSamples = [
  {'id': 1, 'type': 'apples', 'samples': [1,2,3,5,2,9,11,2,33]},
  {'id': 2, 'type': 'bananas', 'samples': [1,2,7,1,7,7,17,2,67]},
  {'id': 3, 'type': 'pears', 'samples': [1,2,3,12,21,32,91,22,34]},
]

I have tried many different approaches but since I want the shortest possible way of solving this what would be your recommendations?

I have tried this:

let test = _(fruitSamples)
  .flatten()
  .groupBy('type')
  .map(_.spread(_.merge))
  .value();

console.log(test);

This gives me the following result, which does not concatenate the samples:

test = [
  {'id': 1,'type': 'apples','samples': [11, 2, 33]},
  {'id': 2,'type': 'bananas','samples': [17, 2, 67]},
  {'id': 3,'type': 'pears','samples': [91, 22, 34]}
]

I feel using _.mergeWith might be the right answer, if so, I am looking for help implementing mergeWith in the best possible way as I am not sure how to do it. Any suggestions?

Instead of _.merge , you could try using _.mergeWith that accepts a customizer function which you can use to customize the assigned values.

From the official docs :

This method is like _.merge except that it accepts customizer which is invoked to produce the merged values of the destination and source properties.

 let fruitSamples = [ [ {'id': 1,'type': 'apples','samples': [1, 2, 3]}, {'id': 2,'type': 'bananas','samples': [1, 2, 7]}, {'id': 3,'type': 'pears','samples': [1, 2, 3]} ], [ {'id': 1,'type': 'apples','samples': [5, 2, 9]}, {'id': 2,'type': 'bananas','samples': [1, 7, 7]}, {'id': 3,'type': 'pears','samples': [12, 21, 32]} ], [ {'id': 1,'type': 'apples','samples': [11, 2, 33]}, {'id': 2,'type': 'bananas','samples': [17, 2, 67]}, {'id': 3,'type': 'pears','samples': [91, 22, 34]} ] ]; function customizer(objValue, srcValue) { if (_.isArray(objValue)) { return objValue.concat(srcValue); } } let test = _(fruitSamples) .flatten() .groupBy('type') .map(_.spread((...values) => { return _.mergeWith(...values, customizer); })) .value(); console.log(test); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script> 

Not sure how to do using lodash, but pure Javascript it's not too difficult.

Update: Thanks to @Taki doing a flat() first makes this even easier..

 let fruitSamples = [[{"id":1,"type":"apples","samples":[1,2,3]},{"id":2,"type":"bananas","samples":[1,2,7]},{"id":3,"type":"pears","samples":[1,2,3]}],[{"id":1,"type":"apples","samples":[5,2,9]},{"id":2,"type":"bananas","samples":[1,7,7]},{"id":3,"type":"pears","samples":[12,21,32]}],[{"id":1,"type":"apples","samples":[11,2,33]},{"id":2,"type":"bananas","samples":[17,2,67]},{"id":3,"type":"pears","samples":[91,22,34]}]]; let concat = fruitSamples.flat().reduce((a, i) => { const f = a.find(f => f.id === i.id); if (!f) a.push(i); else f.samples = [...f.samples, ...i.samples]; return a; }, []); console.log(concat); 

You can first convert the array or arrays to simple array using .reduce and then group by using .reduce again.

May be more easy to grab

Something like

 let fruitSamples = [ [ {'id': 1,'type': 'apples','samples': [1, 2, 3]}, {'id': 2,'type': 'bananas','samples': [1, 2, 7]}, {'id': 3,'type': 'pears','samples': [1, 2, 3]} ], [ {'id': 1,'type': 'apples','samples': [5, 2, 9]}, {'id': 2,'type': 'bananas','samples': [1, 7, 7]}, {'id': 3,'type': 'pears','samples': [12, 21, 32]} ], [ {'id': 1,'type': 'apples','samples': [11, 2, 33]}, {'id': 2,'type': 'bananas','samples': [17, 2, 67]}, {'id': 3,'type': 'pears','samples': [91, 22, 34]} ] ]; var merged = fruitSamples.reduce(function(prev, next) { return prev.concat(next); }); var res = merged.reduce(function(prev, next) { var index = prev.findIndex(o=> o.id == next.id) if(index >= 0) prev[index].samples = [...prev[index].samples,...next.samples]; else prev.push(next); return prev; },[]); console.log(res) 

One way to do it with javascript and no lodash:

var fruitSamples = [
[
   {'id': 1,'type': 'apples','samples': [1, 2, 3]},
   {'id': 2,'type': 'bananas','samples': [1, 2, 7]},
   {'id': 3,'type': 'pears','samples': [1, 2, 3]}
],
[
   {'id': 1,'type': 'apples','samples': [5, 2, 9]},
   {'id': 2,'type': 'bananas','samples': [1, 7, 7]},
   {'id': 3,'type': 'pears','samples': [12, 21, 32]}
],
[
   {'id': 1,'type': 'apples','samples': [11, 2, 33]},
   {'id': 2,'type': 'bananas','samples': [17, 2, 67]},
   {'id': 3,'type': 'pears','samples': [91, 22, 34]}
 ]
 ];

var test = fruitSamples.flat().reduce((rv,x)=>{
  var f = rv.find(y => y.id == x.id);
  if(f == null){
        f ={"id":x.id, "type":x.type,"samples":x.samples};
        rv.push(f);
  }
  else{
     f.samples = f.samples.concat(x.samples);
  }
  return rv;
},[]);
console.log(JSON.stringify(test))

https://jsfiddle.net/pimboden40/y8x96sbg/1/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM