TehShrike / deepmerge

A library for deep (recursive) merging of Javascript objects
MIT License
2.75k stars 216 forks source link

Merge objects and use the key order from the newest object #169

Open garyking opened 4 years ago

garyking commented 4 years ago

When merging two objects, in deepmerge just like in JS, key order is used from the first object, onward.

So:

import deepmerge from 'deepmerge'

const obj1 = { a: 'a', b: 'b' }
const obj2 = { b: 'c', a: 'd' }

const result = deepmerge(obj1, obj2)

console.log(result)

Would show { a: 'd', b: 'c' } rather than { b: 'c', a: 'd' }.

Please make it possible to inherit the key order from the newest object, instead of from the oldest one.

TehShrike commented 4 years ago

I can't tell from your comment which is the current behavior, and which is the behavior that you want to see instead.

The merging code first assigns the properties from the target object, and then assigns the properties from the source object: https://github.com/TehShrike/deepmerge/blob/8209fc671a07b73ace6141fa743cae28f3e2481e/index.js#L41-L52

garyking commented 4 years ago

Yes. Essentially, I was wondering if it's somehow possible to do the opposite.

At the moment, the target object dictates the order of the keys. I have a specific case, where I'm merging an unknown number of objects, and I want the object that gets merged last, to dictate the order of the keys.

I'm assuming that this is beyond the scope of this project? I figure I would ask anyway, before I write my own solution to this issue.

TehShrike commented 4 years ago

The target object doesn't dictate the order of the keys, since the keys on the final object are a superset of the all the properties on the target + all the properties on the source.

However, for the keys that exist on both, it is true that the order of those keys on the target is the order that will exist on the final object.

Just straight up swapping those two loops wouldn't work, because then the values from the target would overwrite the values from the source, which is the opposite of what is wanted.

To get the ordering that I think you want, you would copy the properties from the source first, and then you would copy all the keys from the target, but skip all of the properties that already existed on the destination.

That should be a small number of lines added, so I don't think I'd have a problem with making that change.

Could you start by writing some failing tests for this feature?

RebeccaStevens commented 3 years ago

The keys in an object should always be treated as if they don't have an order.

4.3.3 Object

An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function. A function stored in a property of an object is called a method.

In ES2015+ the order is deterministic but the order does not (always) follow the insertion order. If you need keys to be a certain order, use an entity array or a Map instead.

After merging the values, you could always use Object.entries() to generate the entities array, then sort that array. If you really want to use an object, you could then use Object.fromEntries() but remember what I said above: "the order of key in an object does not (always) follow the insertion order".