0

I keep running into trouble when working with ObjectIds and lodash. Say I have two arrays of objects I want to use lodash _.unionBy() with:

var arr1 = [
    {
        _id: ObjectId('abc123'),
        old: 'Some property from arr1',
    },
    {
        _id: ObjectId('def456'),
        old: 'Some property from arr1',
    },
];

var arr 2 = [
    {
        _id: ObjectId('abc123'),
        new: 'I want to merge this with object in arr1',
    },
    {
        _id: ObjectId('def456'),
        new: 'I want to merge this with object in arr1',
    },
];

var res = _.unionBy(arr1, arr2, '_id');

Result

console.log(res);
/*
[
    {
        _id: ObjectId('abc123'),
        old: 'Some property from arr1',
    },
    {
        _id: ObjectId('def456'),
        old: 'Some property from arr1',
    },
    {
        _id: ObjectId('abc123'),
        new: 'I want to merge this with object in arr1',
    },
    {
        _id: ObjectId('def456'),
        new: 'I want to merge this with object in arr1',
    },
]
*/

Desired result

[
    {
        _id: ObjectId('abc123'),
        old: 'Some property from arr1',
        new: 'I want to merge this with object in arr1',
    },
    {
        _id: ObjectId('def456'),
        old: 'Some property from arr1',
        new: 'I want to merge this with object in arr1',
    },
]

Since ObjectIds are objects and they are not pointing to the same reference in many cases (e.g. when fetching documents from MongoDB and comparing with local seed for testing), I cannot use '_id' as iteratee.

How do I use lodash with ObjectIDs to achieve desired results?

nomadoda
  • 4,561
  • 3
  • 30
  • 45

3 Answers3

1

Try this, I removed ObjectId because it does not work in javascript. You can use .toString for string conversion.

var arr1 = [{
        _id: 'abc123',
        old: 'Some property from arr1',
    },
    {
        _id: 'def456',
        old: 'Some property from arr1',
    },
];

var arr2 = [{
        _id: 'abc123',
        new: 'I want to merge this with object in arr1',
    },
    {
        _id: 'def456',
        new: 'I want to merge this with object in arr1',
    },
];


const data = arr2.reduce((obj, ele) => {
    if (!obj[ele._id]) obj[ele._id] = ele.new;
    return obj;
}, {})

arr1 = arr1.map((d) => {
    if (data[d._id]) {
        d.new = data[d._id];
    }
    return d;
})

console.log(arr1);
Rahul Sharma
  • 9,534
  • 1
  • 15
  • 37
1

You'll have to use _.unionWith that allows you to use a custom comparator. Use the custom comparator to check equality between the two ObjectIds:

_.unionWith(arr1, arr2, (arrVal, othVal) => arrVal._id.equals(othVal._id));

Hope it helps.

acontell
  • 6,792
  • 1
  • 19
  • 32
  • Thank you, I did not quite understand how comperators worked. Unfortunately it didn't help though. It seems `union` is the wrong path here as it only creates an array of unique values. It doesn't merge the objects it seems. – nomadoda Mar 19 '18 at 14:40
0

This is what ended up solving my problem.

var res = arr1.map(a => {
  return _.assign(a, _.find(arr2, { _id: a._id }));
});

Thanks to Tushar's answer

nomadoda
  • 4,561
  • 3
  • 30
  • 45