Merge objects/arrays declaratively.
- Objects: deeply, shallowly, or both
- Arrays: items can be updated, merged, added, inserted, appended, prepended, deleted or set
Please reach out if you're looking for a Node.js API or CLI engineer (11 years of experience). Most recently I have been Netlify Build's and Netlify Plugins' technical lead for 2.5 years. I am available for full-time remote positions.
This is intended for cases where objects/arrays manipulation in JavaScript is not available.
For example, a library where shared configuration files can be extended.
extend: my-shared-config
# Deep merge
log:
verbosity: silent
# Shallow merge
provider:
_merge: shallow
name: redis
type: local
# Delete properties
output:
_merge: delete
rules:
# Update arrays deeply
1:
level: silent
# Append arrays
'-0':
name: appendedRule
Or a server receiving network patch requests.
PATCH /pets/0
{
// Deep merge
"capabilities": { "play": true },
// Shallow merge
"title": { "name": "felix", "_merge": "shallow" },
// Append arrays
"toys": { "-0": "newToy" }
}
import declarativeMerge from 'declarative-merge'
declarativeMerge({ a: 1, b: { c: 2 }, d: 3 }, { a: 10, b: { e: 20 } })
// { a: 10, b: { c: 2, e: 20 }, d: 3 }
declarativeMerge(
{ a: 1, b: { c: 2 }, d: 3 },
{ a: 10, b: { e: 20 }, _merge: 'shallow' },
)
// { a: 10, b: { e: 20 }, d: 3 }
declarativeMerge(
{ a: 1, b: { c: 2 }, d: 3 },
{ a: 10, b: { e: 20 }, _merge: 'set' },
)
// { a: 10, b: { e: 20 } }
// `_merge` can be specified in nested objects
declarativeMerge(
{ a: 1, b: { c: 2 }, d: 3 },
{ a: 10, b: { e: 20, _merge: 'set' } },
)
// { a: 10, b: { e: 20 }, d: 3 }
declarativeMerge(
{ a: 1, b: { c: 2 }, d: 3 },
{ a: 10, b: { e: 20, _merge: 'deep' }, _merge: 'set' },
)
// { a: 10, b: { c: 2, e: 20 } }
declarativeMerge(
{ a: 1, b: { c: 2 }, d: 3 },
{ a: 10, b: { e: 20, _merge: 'delete' } },
)
// { a: 10, d: 3 }
// By default, arrays override each other
declarativeMerge({ one: ['a', 'b', 'c'] }, { one: ['X', 'Y'] }) // { one: ['X', 'Y'] }
// They can be updated instead using an object where the keys are the array
// indices (before any updates).
declarativeMerge(
{ one: ['a', 'b', 'c'], two: 2 },
{ one: { 1: 'X' }, three: 3 },
)
// { one: ['a', 'X', 'c'], two: 2, three: 3 }
// This works on top-level arrays too
declarativeMerge(['a', 'b', 'c'], { 1: 'X', 2: 'Y' }) // ['a', 'X', 'Y']
// If the new array items are objects, they are merged
declarativeMerge(
[{ id: 'a' }, { id: 'b', value: { name: 'Ann' } }, { id: 'c' }],
{ 1: { value: { color: 'red' } } },
)
// [{ id: 'a' }, { id: 'b', value: { name: 'Ann', color: 'red' } }, { id: 'c' }]
declarativeMerge(
[{ id: 'a' }, { id: 'b', value: { name: 'Ann' } }, { id: 'c' }],
{ 1: { value: { color: 'red' }, _merge: 'shallow' } },
)
// [{ id: 'a' }, { id: 'b', value: { color: 'red' } }, { id: 'c' }]
declarativeMerge(['a', 'b', 'c'], { '*': 'X' }) // ['X', 'X', 'X']
declarativeMerge(['a', 'b', 'c'], { '-1': 'X' }) // ['a', 'b', 'X']
declarativeMerge(['a', 'b', 'c'], { 4: 'X' }) // ['a', 'b', 'c', undefined, 'X']
// Array of items can be used
declarativeMerge(['a', 'b', 'c'], { 1: ['X', 'Y'] }) // ['a', 'X', 'Y', 'c']
declarativeMerge(['a', 'b', 'c'], { 1: ['X'] }) // ['a', 'X', 'c']
declarativeMerge(['a', 'b', 'c'], { 1: [['X']] }) // ['a', ['X'], 'c']
// If the key ends with +, items are prepended, not replaced
declarativeMerge(['a', 'b', 'c'], { '1+': 'X' }) // ['a', 'X', 'b', 'c']
declarativeMerge(['a', 'b', 'c'], { '-0': 'X' }) // ['a', 'b', 'c', 'X']
declarativeMerge(['a', 'b', 'c'], { '-0': ['X', 'Y'] }) // ['a', 'b', 'c', 'X', 'Y']
declarativeMerge(['a', 'b', 'c'], { '0+': ['X', 'Y'] }) // ['X', 'Y', 'a', 'b', 'c']
declarativeMerge(['a', 'b', 'c'], { 1: [] }) // ['a', 'c']
declarativeMerge({}, { one: { 0: 'X', 2: 'Z' } }) // { one: ['X', undefined, 'Z'] }
declarativeMerge({ one: true }, { one: { 0: 'X', 2: 'Z' } }) // { one: ['X', undefined, 'Z'] }
npm install declarative-merge
This package works in both Node.js >=18.18.0 and browsers.
This is an ES module. It must be loaded using
an import
or import()
statement,
not require()
. If TypeScript is used, it must be configured to
output ES modules,
not CommonJS.
firstValue
any
secondValue
any
options
Options
Return value: any
Merge firstValue
and secondValue
.
Any object can change the merge mode using a _merge
property with
value "deep"
(default), "shallow"
,
"set"
or "delete"
.
Arrays can be merged using objects where the keys are the array indices. Items can be updated, merged, added, inserted, appended, prepended, deleted or set.
The _merge
property and array updates objects can only be used in
secondValue
. They are left as is in firstValue
.
firstValue
and secondValue
are not modified. Plain objects and arrays are
deeply cloned.
Inherited and non-enumerable properties are ignored.
Options are an optional object.
Type: string | symbol
Default: "_merge"
Name of the property used to specify the merge mode.
declarativeMerge({ a: 1 }, { b: 2, _mergeMode: 'set' }, { key: '_mergeMode' }) // { b: 2 }
Symbols can be useful to prevent injections when the input is user-provided.
const mergeMode = Symbol('mergeMode')
declarativeMerge({ a: 1 }, { b: 2, [mergeMode]: 'set' }, { key: mergeMode }) // { b: 2 }
set-array
: underlying module to merge arrayswild-wild-utils
: applydeclarative-merge
on multiple properties at once using this module'smerge()
method
For any question, don't hesitate to submit an issue on GitHub.
Everyone is welcome regardless of personal background. We enforce a Code of conduct in order to promote a positive and inclusive environment.
This project was made with ❤️. The simplest way to give back is by starring and sharing it online.
If the documentation is unclear or has a typo, please click on the page's Edit
button (pencil icon) and suggest a correction.
If you would like to help us fix a bug or add a new feature, please check our guidelines. Pull requests are welcome!