From a15cc25771bcd7e359bf8238d7ba85907b95d7b5 Mon Sep 17 00:00:00 2001 From: Nilesh Chavan Date: Tue, 26 May 2015 14:41:54 -0700 Subject: [PATCH 1/3] Added capability to specify whether arrays should be merged or replaced --- index.js | 70 +++++++++++++++++++------------------ test/merge.js | 97 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 128 insertions(+), 39 deletions(-) diff --git a/index.js b/index.js index b7c605e..3983359 100644 --- a/index.js +++ b/index.js @@ -8,45 +8,47 @@ } }(this, function () { -return function deepmerge(target, src) { - var array = Array.isArray(src); - var dst = array && [] || {}; + return function deepmerge(target, src, replaceArray) { + 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 && !replaceArray) { + 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, replaceArray); + } else { + if (target.indexOf(e) === -1) { + dst.push(e); + } } + }); + } else if (array && replaceArray) { + dst = src; + } 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], replaceArray); + } + } + }); + } - return dst; -} + return dst; + } })); diff --git a/test/merge.js b/test/merge.js index 47e1061..f9e4eff 100644 --- a/test/merge.js +++ b/test/merge.js @@ -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, true) t.deepEqual(target, {}, 'merge should be immutable') t.deepEqual(res, src) + t.deepEqual(resWReplaceArray, src) t.end() }) @@ -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, true), expectedWReplaceArray) t.end() }) @@ -49,6 +58,14 @@ test('merge nested objects into target', function (t) { } } + var expectedWReplaceArray = { + key1: { + subkey1: 'changed', + subkey2: 'value2', + subkey3: 'added' + } + } + t.deepEqual(target, { key1: { subkey1: 'value1', @@ -56,6 +73,7 @@ test('merge nested objects into target', function (t) { } }) t.deepEqual(merge(target, src), expected) + t.deepEqual(merge(target, src, true), expectedWReplaceArray) t.end() }) @@ -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, 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": {} @@ -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, true), expectedWReplaceArray) t.end() }) @@ -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: { @@ -126,6 +162,7 @@ test('should replace object with simple key in target', function (t) { key2: 'value2' }) t.deepEqual(merge(target, src), expected) + t.deepEqual(merge(target, src, true), expectedWReplaceArray) t.end() }) @@ -134,20 +171,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, 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, true), expectedWReplaceArray) t.ok(Array.isArray(merge(target, src))) t.end() }) @@ -166,11 +208,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, true), expectedWReplaceArray) t.ok(Array.isArray(merge(target, src).key1)) t.ok(Array.isArray(merge(target, src).key2)) t.end() @@ -191,18 +239,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, 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' }} ] @@ -217,6 +271,39 @@ 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, 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, true), expectedWReplaceArray) + t.end() +}) \ No newline at end of file From e1f5a84692a569d7d4fa243984e4f501c070793c Mon Sep 17 00:00:00 2001 From: Joe Stanton Date: Tue, 9 Jun 2015 11:59:31 +0100 Subject: [PATCH 2/3] Switched to an options object to self-document --- index.js | 13 +++++++------ test/merge.js | 27 ++++++++++++++------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index 3983359..6c34f38 100644 --- a/index.js +++ b/index.js @@ -8,26 +8,27 @@ } }(this, function () { - return function deepmerge(target, src, replaceArray) { + return function deepmerge(target, src, opts) { + var opts = opts || {}; var array = Array.isArray(src); var dst = array && [] || {}; - if (array && !replaceArray) { + if (array && opts.replaceArrays) { + dst = src; + } else 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, replaceArray); + dst[i] = deepmerge(target[i], e, opts); } else { if (target.indexOf(e) === -1) { dst.push(e); } } }); - } else if (array && replaceArray) { - dst = src; } else { if (target && typeof target === 'object') { Object.keys(target).forEach(function (key) { @@ -42,7 +43,7 @@ if (!target[key]) { dst[key] = src[key]; } else { - dst[key] = deepmerge(target[key], src[key], replaceArray); + dst[key] = deepmerge(target[key], src[key], opts); } } }); diff --git a/test/merge.js b/test/merge.js index f9e4eff..b873d80 100644 --- a/test/merge.js +++ b/test/merge.js @@ -6,7 +6,7 @@ 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, true) + var resWReplaceArray = merge(target, src, { replaceArrays: true }) t.deepEqual(target, {}, 'merge should be immutable') t.deepEqual(res, src) @@ -32,7 +32,7 @@ test('merge existing simple keys in target at the roots', function (t) { t.deepEqual(target, { key1: 'value1', key3: 'value3' }) t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray) t.end() }) @@ -73,7 +73,7 @@ test('merge nested objects into target', function (t) { } }) t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray) t.end() }) @@ -107,7 +107,7 @@ test('replace simple key with nested object in target', function (t) { t.deepEqual(target, { key1: 'value1', key2: 'value2' }) t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray) t.end() }) @@ -137,7 +137,7 @@ test('should add nested object in target', function (t) { } t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray) t.end() }) @@ -162,7 +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, true), expectedWReplaceArray) + t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray) + t.end() }) @@ -175,7 +176,7 @@ test('should work on simple array', function (t) { t.deepEqual(target, ['one', 'two']) t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray) t.ok(Array.isArray(merge(target, src))) t.end() }) @@ -189,7 +190,7 @@ test('should work on another simple array', function (t) { t.deepEqual(target, ["a1", "a2", "c1", "f1", "p1"]) t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray) t.ok(Array.isArray(merge(target, src))) t.end() }) @@ -218,7 +219,7 @@ test('should work on array properties', function (t) { }) t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + 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() @@ -249,7 +250,7 @@ test('should work on array of objects', function (t) { { key3: ['four'] } ]) t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + 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') @@ -277,7 +278,7 @@ test('should work on arrays of nested objects', function (t) { ]; t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray) t.end() }) @@ -304,6 +305,6 @@ test('should work on objects with array properties', function (t) { } t.deepEqual(merge(target, src), expected) - t.deepEqual(merge(target, src, true), expectedWReplaceArray) + t.deepEqual(merge(target, src, { replaceArrays: true }), expectedWReplaceArray) t.end() -}) \ No newline at end of file +}) From 025739386ab1495503d414db603ff762a8f1e538 Mon Sep 17 00:00:00 2001 From: Paul Roper Date: Tue, 17 Nov 2015 12:24:01 +0000 Subject: [PATCH 3/3] Added concat option for array merging --- index.js | 18 +++++++++++------- test/merge.js | 26 +++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 6c34f38..cf88ac8 100644 --- a/index.js +++ b/index.js @@ -19,14 +19,18 @@ 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, opts); + if (opts.concatArrays) { + dst.push(e); } else { - if (target.indexOf(e) === -1) { - dst.push(e); - } + 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 { diff --git a/test/merge.js b/test/merge.js index b873d80..5192d18 100644 --- a/test/merge.js +++ b/test/merge.js @@ -282,7 +282,6 @@ test('should work on arrays of nested objects', function (t) { t.end() }) - test('should work on objects with array properties', function (t) { var target = { key1: { subkey: 'one' }, @@ -308,3 +307,28 @@ test('should work on objects with array properties', function (t) { 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() +})