27

There is _.merge functionality in lodash. I want to achieve the same thing in ES6 or ES7.

Having this snippet:

Object.assign({}, {key: 2}, {key: undefined})

I want to receive {key: 2}. Currently I receive {key: undefined}

This is NOT a deep merge.

Is it possible? If yes then how to achieve that?

8
  • Note: this question does not cover my case with undefined: stackoverflow.com/questions/13852852/… Oct 11, 2016 at 12:29
  • You can take the second object, prune any values that are undefined and then use it in Object.assign
    – VLAZ
    Oct 11, 2016 at 12:47
  • @vlaz is there native ES6 way to prune undefiend values from object? Oct 11, 2016 at 12:52
  • Well, if you take ES5 with just a dash of ES6 then arr.filter(x => typeof x !== "undefined" )
    – VLAZ
    Oct 11, 2016 at 12:53
  • 2
    Why does your object have undefined values in it? Just wondering.
    – user663031
    Oct 11, 2016 at 13:04

6 Answers 6

11

You can't achieve that with a straight usage of Object.assign, because each next object will rewrite the same keys for prev merge. The only way, to filter your incoming objects with some hand-crafted function.

function filterObject(obj) {
    const ret = {};
    Object.keys(obj)
        .filter((key) => obj[key] !== undefined)
        .forEach((key) => ret[key] = obj[key]);
    return ret;
}
4
  • you could also pass the predicate as an argument, so that it works like Array.filter
    – georg
    Oct 11, 2016 at 12:51
  • I think you're missing a closing ) after obj[key]
    – TechnoTim
    Oct 18, 2018 at 16:36
  • @TechnoTim thnx, fixed
    – steppefox
    Oct 19, 2018 at 9:03
  • How to have the same logic within a deepmerge ? Aug 17, 2021 at 16:22
11

You can simply filter out the keys with undefined values before passing them to Object.assign():

const assign = (target, ...sources) =>
  Object.assign(target, ...sources.map(x =>
    Object.entries(x)
      .filter(([key, value]) => value !== undefined)
      .reduce((obj, [key, value]) => (obj[key] = value, obj), {})
  ))

console.log(assign({}, {key: 2}, {key: undefined}))

5

Write a little utility to remove undefined values:

function removeUndefined(obj) {
  for (let k in obj) if (obj[k] === undefined) delete obj[k];
  return obj;
}

Then

Object.assign({}, {key: 2}, removeUndefined({key: undefined}))

This seems preferable to writing your own assign with wired-in behavior to remove undefined values.

1
  • 1
    That approach mutates the incoming object and could make problems in some cases
    – steppefox
    Jan 22, 2020 at 15:18
2

use lodash to omit nil values and then combine the two objects into one via spread

{ ...(omitBy({key: 2}, isNil)), ...(omitBy({key: undefined}, isNil))}

See more info on lodash here https://lodash.com/docs/4.17.15

1

With ES2019/ES10's new object method, Object.fromEntries(), Michał's answer can be updated:

const assign = (target, ...sources) =>
  Object.assign(target, ...sources.map(x =>
    Object.fromEntries(
      Object.entries(x)
        .filter(([key, value]) => value !== undefined)
    )
  ))

console.log(assign({}, {key: 2}, {key: undefined}))
1

If you just need the values and don't need an object, you could also use object destructuring:

const input = { a: 0, b: "", c: false, d: null, e: undefined };
const { a = 1, b = 2, c = 3, d = 4, e = 5, f = 6 } = input;
console.log(a, b, c, d, e, f);
// => 0, "", false, null, 5, 6

This will only override absent or undefined values.

I often use this for function argument default values like this:

function f(options = {}) {
  const { foo = 42, bar } = options;
  console.log(foo, bar);
}

f();
// => 42, undefined

f({})
// => 42, undefined

f({ foo: 123 })
// => 123, undefined

f({ bar: 567 })
// => 42, 567

f({ foo: 123, bar: 567 })
// => 123, 567

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.