Which is Better JavaScript Object Merging Libraries?
deepmerge vs lodash.merge vs immer
1 Year
deepmergelodash.mergeimmerSimilar Packages:
What's JavaScript Object Merging Libraries?

JavaScript object merging libraries provide developers with tools to combine objects in various ways, ensuring that properties from multiple sources can be integrated effectively. These libraries help manage state, configuration, and data structures in applications, particularly when dealing with nested objects or complex data. They differ in their approach to merging, mutability, and performance, making it essential to choose the right one based on specific use cases and requirements.

NPM Package Downloads Trend
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
deepmerge35,002,0222,75731.2 kB542 years agoMIT
lodash.merge33,379,50559,747-1085 years agoMIT
immer11,426,54227,637627 kB506 months agoMIT
Feature Comparison: deepmerge vs lodash.merge vs immer

Merging Strategy

  • deepmerge: Deepmerge provides a recursive merging strategy that can intelligently combine nested objects. It allows for custom merge functions, enabling developers to define how specific properties should be merged, making it highly flexible for complex data structures.
  • lodash.merge: Lodash.merge performs a shallow merge of properties, meaning it will overwrite properties at the first level and merge nested objects. It is less flexible than deepmerge but is simpler and faster for straightforward merging tasks.
  • immer: Immer uses a draft state approach where you can mutate a draft object, and it automatically produces the next immutable state. This makes it easy to manage state updates without worrying about the intricacies of immutability, simplifying the merging process in state management.

Mutability

  • deepmerge: Deepmerge operates on mutable objects, allowing you to modify the original objects during the merge process. However, it also respects immutability if you choose to create new objects instead of modifying the originals.
  • lodash.merge: Lodash.merge modifies the target object in place, which means it is mutable. This can lead to unintended side effects if the original object is used elsewhere, so care must be taken when using it.
  • immer: Immer is designed for immutability, allowing you to write code that looks like you're mutating the state while it actually creates a new immutable state. This is particularly useful in frameworks like React where immutability is crucial for performance and state management.

Performance

  • deepmerge: Deepmerge's performance can vary based on the complexity of the objects being merged. It is optimized for deep merging but may incur overhead with very large or deeply nested structures due to its recursive nature.
  • lodash.merge: Lodash.merge is generally faster for shallow merges and simpler objects. However, its performance may degrade with deeply nested structures due to its straightforward merging approach.
  • immer: Immer is optimized for performance in state management scenarios, as it only tracks changes to the draft state. This can lead to significant performance improvements in applications where state updates are frequent, especially in React.

Learning Curve

  • deepmerge: Deepmerge has a moderate learning curve, especially when dealing with custom merge functions. Developers need to understand how to structure their objects and the implications of deep merging.
  • lodash.merge: Lodash.merge is easy to learn and use, especially for developers already familiar with the lodash library. Its straightforward API makes it accessible for quick object merging tasks.
  • immer: Immer has a relatively low learning curve, particularly for those familiar with mutable state management. Its syntax is intuitive, allowing developers to adopt it quickly, especially in React applications.

Use Cases

  • deepmerge: Deepmerge is ideal for applications that require complex configuration merging, such as merging user preferences or settings where nested structures are common.
  • lodash.merge: Lodash.merge is perfect for simpler use cases where you need to combine objects without the need for deep merging, such as merging flat configuration objects or simple data structures.
  • immer: Immer is best suited for state management in frameworks like React, where immutability is a requirement. It simplifies the process of updating state without losing the benefits of immutability.
How to Choose: deepmerge vs lodash.merge vs immer
  • deepmerge: Choose deepmerge if you need a library that can handle deeply nested objects and offers a flexible merging strategy that allows for custom merging behavior. It is particularly useful for applications that require complex state management or configuration merging.
  • lodash.merge: Choose lodash.merge if you are looking for a well-established utility that provides a straightforward way to merge objects without deep cloning. It's suitable for simpler use cases where deep merging is not required, and you want to leverage the broader lodash ecosystem.
  • immer: Choose immer if you want to work with immutable state updates in a more intuitive way. It allows you to write code that appears to mutate the state directly while maintaining immutability under the hood, making it ideal for state management in React applications.
README for deepmerge

deepmerge

Merges the enumerable properties of two or more objects deeply.

UMD bundle is 723B minified+gzipped

Getting Started

Example Usage

const x = {
	foo: { bar: 3 },
	array: [{
		does: 'work',
		too: [ 1, 2, 3 ]
	}]
}

const y = {
	foo: { baz: 4 },
	quux: 5,
	array: [{
		does: 'work',
		too: [ 4, 5, 6 ]
	}, {
		really: 'yes'
	}]
}

const output = {
	foo: {
		bar: 3,
		baz: 4
	},
	array: [{
		does: 'work',
		too: [ 1, 2, 3 ]
	}, {
		does: 'work',
		too: [ 4, 5, 6 ]
	}, {
		really: 'yes'
	}],
	quux: 5
}

merge(x, y) // => output

Installation

With npm do:

npm install deepmerge

deepmerge can be used directly in the browser without the use of package managers/bundlers as well: UMD version from unpkg.com.

Include

deepmerge exposes a CommonJS entry point:

const merge = require('deepmerge')

The ESM entry point was dropped due to a Webpack bug.

API

merge(x, y, [options])

Merge two objects x and y deeply, returning a new merged object with the elements from both x and y.

If an element at the same key is present for both x and y, the value from y will appear in the result.

Merging creates a new object, so that neither x or y is modified.

Note: By default, arrays are merged by concatenating them.

merge.all(arrayOfObjects, [options])

Merges any number of objects into a single result object.

const foobar = { foo: { bar: 3 } }
const foobaz = { foo: { baz: 4 } }
const bar = { bar: 'yay!' }

merge.all([ foobar, foobaz, bar ]) // => { foo: { bar: 3, baz: 4 }, bar: 'yay!' }

Options

arrayMerge

There are multiple ways to merge two arrays, below are a few examples but you can also create your own custom function.

Your arrayMerge function will be called with three arguments: a target array, the source array, and an options object with these properties:

  • isMergeableObject(value)
  • cloneUnlessOtherwiseSpecified(value, options)

arrayMerge example: overwrite target array

Overwrites the existing array values completely rather than concatenating them:

const overwriteMerge = (destinationArray, sourceArray, options) => sourceArray

merge(
	[1, 2, 3],
	[3, 2, 1],
	{ arrayMerge: overwriteMerge }
) // => [3, 2, 1]

arrayMerge example: combine arrays

Combines objects at the same index in the two arrays.

This was the default array merging algorithm pre-version-2.0.0.

const combineMerge = (target, source, options) => {
	const destination = target.slice()

	source.forEach((item, index) => {
		if (typeof destination[index] === 'undefined') {
			destination[index] = options.cloneUnlessOtherwiseSpecified(item, options)
		} else if (options.isMergeableObject(item)) {
			destination[index] = merge(target[index], item, options)
		} else if (target.indexOf(item) === -1) {
			destination.push(item)
		}
	})
	return destination
}

merge(
	[{ a: true }],
	[{ b: true }, 'ah yup'],
	{ arrayMerge: combineMerge }
) // => [{ a: true, b: true }, 'ah yup']

isMergeableObject

By default, deepmerge clones every property from almost every kind of object.

You may not want this, if your objects are of special types, and you want to copy the whole object instead of just copying its properties.

You can accomplish this by passing in a function for the isMergeableObject option.

If you only want to clone properties of plain objects, and ignore all "special" kinds of instantiated objects, you probably want to drop in is-plain-object.

const { isPlainObject } = require('is-plain-object')

function SuperSpecial() {
	this.special = 'oh yeah man totally'
}

const instantiatedSpecialObject = new SuperSpecial()

const target = {
	someProperty: {
		cool: 'oh for sure'
	}
}

const source = {
	someProperty: instantiatedSpecialObject
}

const defaultOutput = merge(target, source)

defaultOutput.someProperty.cool // => 'oh for sure'
defaultOutput.someProperty.special // => 'oh yeah man totally'
defaultOutput.someProperty instanceof SuperSpecial // => false

const customMergeOutput = merge(target, source, {
	isMergeableObject: isPlainObject
})

customMergeOutput.someProperty.cool // => undefined
customMergeOutput.someProperty.special // => 'oh yeah man totally'
customMergeOutput.someProperty instanceof SuperSpecial // => true

customMerge

Specifies a function which can be used to override the default merge behavior for a property, based on the property name.

The customMerge function will be passed the key for each property, and should return the function which should be used to merge the values for that property.

It may also return undefined, in which case the default merge behaviour will be used.

const alex = {
	name: {
		first: 'Alex',
		last: 'Alexson'
	},
	pets: ['Cat', 'Parrot']
}

const tony = {
	name: {
		first: 'Tony',
		last: 'Tonison'
	},
	pets: ['Dog']
}

const mergeNames = (nameA, nameB) => `${nameA.first} and ${nameB.first}`

const options = {
	customMerge: (key) => {
		if (key === 'name') {
			return mergeNames
		}
	}
}

const result = merge(alex, tony, options)

result.name // => 'Alex and Tony'
result.pets // => ['Cat', 'Parrot', 'Dog']

clone

Deprecated.

Defaults to true.

If clone is false then child objects will be copied directly instead of being cloned. This was the default behavior before version 2.x.

Testing

With npm do:

npm test

License

MIT