-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdeep-merge.js
98 lines (85 loc) · 2.33 KB
/
deep-merge.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/**
* Checks if the given value can be merged
* @param {any} value
* @returns {boolean}
*/
function canMerge(value) {
const nonNullObject = value && typeof value === 'object'
return nonNullObject
&& Object.prototype.toString.call(value) !== '[object RegExp]'
&& Object.prototype.toString.call(value) !== '[object Date]'
}
/**
* Returns a corresponding empty entity according to the given value type
* @param {any} value
* @returns {(object|Array)}
*/
function emptyEntity(value) {
return Array.isArray(value) ? [] : {}
}
/**
* Returns a clone of the given value (if required)
* @param {any} value
* @param {object} config
* @returns {(object|Array)}
*/
function maybeClone(value, config = {}) {
const { clone } = config
return (clone && canMerge(value))
? deepMerge(emptyEntity(value), value, config)
: value
}
/**
* Handles arrays merging
* @param {Array} target
* @param {Array} source
* @param {object} config
* @returns {Array}
*/
function mergeArrays(target, source, config) {
const destination = [...target]
source.forEach((entry, i) => {
if (typeof destination[i] === 'undefined') {
destination[i] = maybeClone(entry, config)
} else if (canMerge(entry)) {
destination[i] = deepMerge(target[i], entry, config)
} else if (target.indexOf(entry) === -1) {
destination.push(maybeClone(entry, config))
}
})
return destination
}
/**
* Handles objects merging
* @param {object} target
* @param {object} source
* @param {object} config
*/
function mergeObjects(target, source, config) {
const destination = {}
if (canMerge(target)) {
Object.keys(target).forEach(key => {
destination[key] = maybeClone(target[key], config)
})
}
Object.keys(source).forEach(key => {
destination[key] = (!canMerge(source[key]) || !target[key])
? maybeClone(source[key], config)
: deepMerge(target[key], source[key], config)
})
return destination
}
/**
* Provides a deep merge between two objects or arrays
* @param {(object|Array)} target
* @param {(object|Array)} source
* @param {object} config
*/
export default function deepMerge(target, source, config) {
if (Array.isArray(source)) {
return Array.isArray(target)
? mergeArrays(target, source, config)
: maybeClone(source, config)
}
return mergeObjects(target, source, config)
}