Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom array merging #37

Merged
merged 2 commits into from
Sep 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ methods
var merge = require('deepmerge')
```

merge(x, y)
merge(x, y, [options])
-----------

Merge two objects `x` and `y` deeply, returning a new merged object with the
Expand All @@ -42,7 +42,14 @@ If an element at the same key is present for both `x` and `y`, the value from

The merge is immutable, so neither `x` nor `y` will be modified.

The merge will also merge arrays and array values.
The merge will also merge arrays and array values by default. However, there are nigh-infinite valid ways to merge arrays, and you may want to supply your own. You can do this by passing an `arrayMerge` function as an option.

```js
function concatMerge(destinationArray, sourceArray, mergeOptions) {
return destinationArray.concat(sourceArray)
}
merge([1, 2, 3], [1, 2, 3], { arrayMerge: concatMerge }) // => [1, 2, 3, 1, 2, 3]
```

install
=======
Expand Down
70 changes: 39 additions & 31 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,50 @@ function isMergeableObject(val) {
&& Object.prototype.toString.call(val) !== '[object Date]'
}

return function deepmerge(target, src) {
var array = Array.isArray(src);
var dst = array ? [] : {};
function defaultArrayMerge(target, source, optionsArgument) {
var destination = target.slice()
source.forEach(function(e, i) {
if (typeof destination[i] === 'undefined') {
destination[i] = e
} else if (isMergeableObject(e)) {
destination[i] = deepmerge(target[i], e, optionsArgument)
} else if (target.indexOf(e) === -1) {
destination.push(e)
}
})
return destination
}

function mergeObject(target, source, optionsArgument) {
var destination = {}
if (isMergeableObject(target)) {
Object.keys(target).forEach(function (key) {
destination[key] = target[key]
})
}
Object.keys(source).forEach(function (key) {
if (!isMergeableObject(source[key]) || !target[key]) {
destination[key] = source[key]
} else {
destination[key] = deepmerge(target[key], source[key], optionsArgument)
}
})
return destination
}

function deepmerge(target, source, optionsArgument) {
var array = Array.isArray(source);
var options = optionsArgument || { arrayMerge: defaultArrayMerge }
var arrayMerge = options.arrayMerge || defaultArrayMerge

if (array) {
target = target || [];
dst = dst.concat(target);
src.forEach(function(e, i) {
if (typeof dst[i] === 'undefined') {
dst[i] = e;
} else if (isMergeableObject(e)) {
dst[i] = deepmerge(target[i], e);
} else if (target.indexOf(e) === -1) {
dst.push(e);
}
});
return arrayMerge(target, source, optionsArgument)
} else {
if (isMergeableObject(target)) {
Object.keys(target).forEach(function (key) {
dst[key] = target[key];
})
}
Object.keys(src).forEach(function (key) {
if (!isMergeableObject(src[key])) {
dst[key] = src[key];
} else {
if (!target[key]) {
dst[key] = src[key];
} else {
dst[key] = deepmerge(target[key], src[key]);
}
}
});
return mergeObject(target, source, optionsArgument)
}

return dst;
}

return deepmerge

}));
44 changes: 44 additions & 0 deletions test/custom-array-merge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
var merge = require('../')
var test = require('tap').test

test('custom merge array', function(t) {
var mergeFunctionCalled = false
function concatMerge(target, source, options) {
t.notOk(mergeFunctionCalled)
mergeFunctionCalled = true

t.deepEqual(target, [1, 2])
t.deepEqual(source, [1, 2, 3])
t.equal(options.arrayMerge, concatMerge)

return target.concat(source)
}
const destination = {
someArray: [1, 2],
someObject: { what: 'yes' }
}
const source = {
someArray: [1, 2, 3]
}

const actual = merge(destination, source, { arrayMerge: concatMerge })
const expected = {
someArray: [1, 2, 1, 2, 3],
someObject: { what: 'yes' }
}

t.ok(mergeFunctionCalled)
t.deepEqual(actual, expected)
t.end()
})

test('merge top-level arrays', function(t) {
function concatMerge(a, b) {
return a.concat(b)
}
var actual = merge([1, 2], [1, 2], { arrayMerge: concatMerge })
var expected = [1, 2, 1, 2]

t.deepEqual(actual, expected)
t.end()
})