Zoidy
Zoidy

Reputation: 384

Lodash merging arrays and handling duplicates

I have two arrays:

var a = [
    {aId: 1, name: "a1"}, 
    {aId: 2, name: "a2"}
];
var b = [
    {parentId: 1, description: "b1"}, 
    {parentId: 1, description: "b2"}, 
    {parentId: 2, description: "b3"}
];

I want to merge these arrays based on the parentId === aId;

I did:

var c = _.map(a, function(obj) {
    return _.assign(obj, _.find(b, {parentId: obj.aId}));
})

JSfiddle

This works as expected, but I need it to handle duplicates - when this finds a match, it correctly merges it, but I instead need it to push the mergee into a new property, instead of merging.

What I get:

[ 
    {aId: 1, name: "a1", description: "b1"}, 
    {aId: 2, name: "a2", description: "b3"}
]

What I want:

[ 
    {
        aId: 1, 
        name: "a1", 
        b: [
            {parentId: 1, description: "b1"}, 
            {parentId: 1, description: "b2"}
        ]
     }, 
     {
        aId: 2, 
        name: "a2", 
        b: [
            {parentId: 2, description: "b1"}
        ]
     }
]

The common property can of course be omitted;

Upvotes: 5

Views: 3877

Answers (1)

Yosvel Quintero
Yosvel Quintero

Reputation: 19070

Edit

While this answer is very helpful and satisfies the original question to an extent, there's some discussion regarding mutation in the comments, which can cause unexpected behaviour, for example when using redux, or other libraries that rely on immutable state.

The original answer uses _.assign which causes the mutation. The below uses the es6 spreads operator to return new objects into a new array.

const a = [{aId: 1, name: "a1"}, {aId: 2, name: "a2"}];
const b = [{parentId: 1, description: "b1"}, {parentId: 1, description: "b2"}, {parentId: 2, description: "b3"}];

const c = _.map(a, (obj) => 
  ({ ...obj, ...{ b: _.filter(b, {parentId: obj.aId}) }});

console.log('mimick original', c);

Original Answer:

Your code is almost done; in the _.assign() you can pass an object with a property b equal to the result of _.filter() instead of _.find():

{
  b: _.filter(b, {parentId: obj.aId})
}

Note that _.find() returns the matched element, else undefined and _.filter() returns the new filtered array.

Code:

var a = [{aId: 1, name: "a1"}, {aId: 2, name: "a2"}],
    b = [{parentId: 1, description: "b1"}, {parentId: 1, description: "b2"}, {parentId: 2, description: "b3"}];

_.map(a, function(obj) {
  return _.assign(obj, {
    b: _.filter(b, {parentId: obj.aId})
  });
});

console.log(a);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Upvotes: 3

Related Questions