1

I have an two arrays of objects. My goal is to replace an object from the second array into the first one based upon 'id'. I have a working solution, but would like to extend it by adding the object to the first array if a value isnt found. Please advice.

function mergeById(arr) {
  return {
    with: function(arr2) {
      return _.map(arr, item => {
        return _.find(arr2, obj => obj.id === item.id) || item
      })
    }
  }
}

var result = mergeById([{
      id: '124',
      name: 'qqq'
    },
    {
      id: '589',
      name: 'www'
    },
    {
      id: '567',
      name: 'rrr'
    }
  ])
  .with([{
    id: '124',
    name: 'ttt'
  }, {
    id: '45',
    name: 'yyy'
  }])

console.log(result);

/**
[
  {
    "id": "124",
    "name": "ttt"
  },
  {
    "id": "589",
    "name": "www"
  },
  {
    "id": "567",
    "name": "rrr"
  },
  {
    id: '45',
    name: 'yyy'
  }
]
**/
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

Please advice.

2 Answers 2

1

You need to filter the second array and add the values who have no common id.

function mergeById(arr) {
    return {
        with: function(arr2) {
            return [
                ..._.map(arr, item => _.find(arr2, obj => obj.id === item.id) || item),
                ..._.filter(arr2, item => !_.some(arr, obj => obj.id === item.id))
            ];
        }
    }
}

var result = mergeById([{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '567', name: 'rrr' } ])
      .with([{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }]);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

A shorter approach with a Map and single loops for every array.

function mergeById(array) {
    const
        add2map = (m, o) => m.set(o.id, o),
        map = array.reduce(add2map, new Map);
    return {
        with: function(array2) {
            return Array.from(array2
                .reduce(add2map, map)
                .values()
            );
        }
    }
}

var result = mergeById([{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '567', name: 'rrr' } ])
      .with([{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }]);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you as always :)
1

Use _.differenceBy(arr2, arr, 'id') to find all items that appear in arr2 that doesn't have a counterpart in arr by id, and concat them to the results of the _.map() action.

Note: instead using _.find() (O(n)) on each iteration, iterate arr2 once with _.keyBy() (O(n)) to create a dictionary { [id]: item }, and then get the items in O(1).

const mergeById = arr => ({
  with(arr2) {
    const arr2Dict = _.keyBy(arr2, 'id')

    return _.map(arr, item => arr2Dict[item.id] || item)
      .concat(_.differenceBy(arr2, arr, 'id'))
  }
})

const result = mergeById([{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '567', name: 'rrr' } ])
      .with([{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }])

console.log(result)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

You can replace/add in a single loop by concating both arrays, reducing to a Map, and just adding the items by id to the Map:

const mergeById = arr => ({
  with(arr2) {
    return Array.from(
      [...arr, ...arr2]
        .reduce((r, o) => r.set(o.id, o), new Map)
        .values()
    )
  }
})

const result = mergeById([{ id: '124', name: 'qqq' }, { id: '589', name: 'www' }, { id: '567', name: 'rrr' } ])
      .with([{ id: '124', name: 'ttt' }, { id: '45', name: 'yyy' }])

console.log(result)
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>

4 Comments

Why the two ? ?
be more specific
Sorry, there were two question marks in your code. Looks like you edited it now. Thanks
The 2 question marks ?? are called Nullish coalescing operator, but there are not necessary for this answer, and I thought that they might be confusing that's why I edited them out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.