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

Array merge/replacement options #21

Closed
wants to merge 3 commits into from
Closed
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
75 changes: 41 additions & 34 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,52 @@
}
}(this, function () {

return function deepmerge(target, src) {
var array = Array.isArray(src);
var dst = array && [] || {};
return function deepmerge(target, src, opts) {
var opts = opts || {};
var array = Array.isArray(src);
var dst = array && [] || {};

if (array) {
target = target || [];
dst = dst.concat(target);
src.forEach(function(e, i) {
if (typeof dst[i] === 'undefined') {
dst[i] = e;
} else if (typeof e === 'object') {
dst[i] = deepmerge(target[i], e);
} else {
if (target.indexOf(e) === -1) {
dst.push(e);
if (array && opts.replaceArrays) {
dst = src;
} else if (array) {
target = target || [];
dst = dst.concat(target);
src.forEach(function (e, i) {
if (opts.concatArrays) {
dst.push(e);
} else {
if (typeof dst[i] === 'undefined') {
dst[i] = e;
} else if (typeof e === 'object') {
dst[i] = deepmerge(target[i], e, opts);
} else {
if (target.indexOf(e) === -1) {
dst.push(e);
}
}
}
});
} else {
if (target && typeof target === 'object') {
Object.keys(target).forEach(function (key) {
dst[key] = target[key];
})
}
});
} else {
if (target && typeof target === 'object') {
Object.keys(target).forEach(function (key) {
dst[key] = target[key];
})
}
Object.keys(src).forEach(function (key) {
if (typeof src[key] !== 'object' || !src[key]) {
dst[key] = src[key];
}
else {
if (!target[key]) {
Object.keys(src).forEach(function (key) {
if (typeof src[key] !== 'object' || !src[key]) {
dst[key] = src[key];
} else {
dst[key] = deepmerge(target[key], src[key]);
}
}
});
}
else {
if (!target[key]) {
dst[key] = src[key];
} else {
dst[key] = deepmerge(target[key], src[key], opts);
}
}
});
}

return dst;
}
return dst;
}

}));
122 changes: 117 additions & 5 deletions test/merge.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ test('add keys in target that do not exist at the root', function (t) {
target = {}

var res = merge(target, src)
var resWReplaceArray = merge(target, src, { replaceArrays: true })

t.deepEqual(target, {}, 'merge should be immutable')
t.deepEqual(res, src)
t.deepEqual(resWReplaceArray, src)
t.end()
})

Expand All @@ -22,8 +24,15 @@ test('merge existing simple keys in target at the roots', function (t) {
key3: 'value3'
}

var expectedWReplaceArray = {
key1: 'changed',
key2: 'value2',
key3: 'value3'
}

t.deepEqual(target, { key1: 'value1', key3: 'value3' })
t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.end()
})

Expand All @@ -49,13 +58,22 @@ test('merge nested objects into target', function (t) {
}
}

var expectedWReplaceArray = {
key1: {
subkey1: 'changed',
subkey2: 'value2',
subkey3: 'added'
}
}

t.deepEqual(target, {
key1: {
subkey1: 'value1',
subkey2: 'value2'
}
})
t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.end()
})

Expand All @@ -79,12 +97,21 @@ test('replace simple key with nested object in target', function (t) {
key2: 'value2'
}

var expectedWReplaceArray = {
key1: {
subkey1: 'subvalue1',
subkey2: 'subvalue2'
},
key2: 'value2'
}

t.deepEqual(target, { key1: 'value1', key2: 'value2' })
t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.end()
})

test('should add nested object in target', function(t) {
test('should add nested object in target', function (t) {
var src = {
"b": {
"c": {}
Expand All @@ -102,7 +129,15 @@ test('should add nested object in target', function(t) {
}
}

var expectedWReplaceArray = {
"a": {},
"b": {
"c": {}
}
}

t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.end()
})

Expand All @@ -117,6 +152,7 @@ test('should replace object with simple key in target', function (t) {
}

var expected = { key1: 'value1', key2: 'value2' }
var expectedWReplaceArray = { key1: 'value1', key2: 'value2' }

t.deepEqual(target, {
key1: {
Expand All @@ -126,6 +162,8 @@ test('should replace object with simple key in target', function (t) {
key2: 'value2'
})
t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)

t.end()
})

Expand All @@ -134,20 +172,25 @@ test('should work on simple array', function (t) {
var target = ['one', 'two']

var expected = ['one', 'two', 'three']
var expectedWReplaceArray = ['one', 'three']

t.deepEqual(target, ['one', 'two'])
t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.ok(Array.isArray(merge(target, src)))
t.end()
})

test('should work on another simple array', function(t) {
var target = ["a1","a2","c1","f1","p1"];
var src = ["t1","s1","c2","r1","p2","p3"];
test('should work on another simple array', function (t) {
var target = ["a1", "a2", "c1", "f1", "p1"];
var src = ["t1", "s1", "c2", "r1", "p2", "p3"];

var expected = ["a1", "a2", "c1", "f1", "p1", "t1", "s1", "c2", "r1", "p2", "p3"]
var expectedWReplaceArray = ["t1", "s1", "c2", "r1", "p2", "p3"]

t.deepEqual(target, ["a1", "a2", "c1", "f1", "p1"])
t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.ok(Array.isArray(merge(target, src)))
t.end()
})
Expand All @@ -166,11 +209,17 @@ test('should work on array properties', function (t) {
key2: ['four']
}

var expectedWReplaceArray = {
key1: ['one', 'three'],
key2: ['four']
}

t.deepEqual(target, {
key1: ['one', 'two']
})

t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.ok(Array.isArray(merge(target, src).key1))
t.ok(Array.isArray(merge(target, src).key2))
t.end()
Expand All @@ -191,18 +240,24 @@ test('should work on array of objects', function (t) {
{ key3: ['four', 'five'] }
]

var expectedWReplaceArray = [
{ key1: ['one', 'three'], key2: ['one'] },
{ key3: ['five'] }
]

t.deepEqual(target, [
{ key1: ['one', 'two'] },
{ key3: ['four'] }
])
t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.ok(Array.isArray(merge(target, src)), 'result should be an array')
t.ok(Array.isArray(merge(target, src)[0].key1), 'subkey should be an array too')

t.end()
})

test('should work on arrays of nested objects', function(t) {
test('should work on arrays of nested objects', function (t) {
var target = [
{ key1: { subkey: 'one' }}
]
Expand All @@ -217,6 +272,63 @@ test('should work on arrays of nested objects', function(t) {
{ key2: { subkey: 'three' }}
]

var expectedWReplaceArray = [
{ key1: { subkey: 'two' }},
{ key2: { subkey: 'three' }}
];

t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.end()
})

test('should work on objects with array properties', function (t) {
var target = {
key1: { subkey: 'one' },
key2: ['one', 'two']
}

var src = {
key1: { subkey: 'two' },
key2: ['three']
}

var expected = {
key1: { subkey: 'two' },
key2: ['one', 'two', 'three']
}

var expectedWReplaceArray = {
key1: { subkey: 'two' },
key2: ['three']
}

t.deepEqual(merge(target, src), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.end()
})

test('should work on array properties containing objects', function (t) {
var target = {
key1: "one",
key2: [ { key1: "one" } ]
}

var src = {
key2: [ { key1: "two" } ]
}

var expected = {
key1: "one",
key2: [ { key1: "one" }, { key1: "two" } ]
}

var expectedWReplaceArray = {
key1: "one",
key2: [ { key1: "two" } ]
}

t.deepEqual(merge(target, src, { concatArrays: true }), expected)
t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray)
t.end()
})