Skip to content

Commit 5b2b75c

Browse files
committed
feat(deepMergeWithArray): new function
1 parent 7f8b16c commit 5b2b75c

File tree

2 files changed

+69
-2
lines changed

2 files changed

+69
-2
lines changed

src/object.test.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, it } from 'vitest'
2-
import { deepMerge, objectMap } from './object'
2+
import { deepMerge, deepMergeWithArray, objectMap } from './object'
33

44
it('objectMap', () => {
55
expect(objectMap({}, (...args) => args)).toEqual({})
@@ -64,3 +64,11 @@ describe('deepMerge', () => {
6464
expect(obj2.polluted).toBeUndefined()
6565
})
6666
})
67+
68+
describe('deepMergeWithArray', () => {
69+
it('should merge array values', () => {
70+
const obj1 = { a: ['A', 'B'] }
71+
const obj2 = { a: ['C'], b: ['D'] }
72+
expect(deepMergeWithArray({}, obj1, obj2)).toEqual({ a: ['A', 'B', 'C'], b: ['D'] })
73+
})
74+
})

src/object.ts

+60-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,10 @@ export function objectEntries<T extends object>(obj: T) {
6868
}
6969

7070
/**
71-
* Deep merge :P
71+
* Deep merge
72+
*
73+
* The first argument is the target object, the rest are the sources.
74+
* The target object will be mutated and returned.
7275
*
7376
* @category Object
7477
*/
@@ -105,6 +108,62 @@ export function deepMerge<T extends object = object, S extends object = T>(targe
105108
return deepMerge(target, ...sources)
106109
}
107110

111+
/**
112+
* Deep merge
113+
*
114+
* Differs from `deepMerge` in that it merges arrays instead of overriding them.
115+
*
116+
* The first argument is the target object, the rest are the sources.
117+
* The target object will be mutated and returned.
118+
*
119+
* @category Object
120+
*/
121+
export function deepMergeWithArray<T extends object = object, S extends object = T>(target: T, ...sources: S[]): DeepMerge<T, S> {
122+
if (!sources.length)
123+
return target as any
124+
125+
const source = sources.shift()
126+
if (source === undefined)
127+
return target as any
128+
129+
if (Array.isArray(target) && Array.isArray(source))
130+
target.push(...source)
131+
132+
if (isMergableObject(target) && isMergableObject(source)) {
133+
objectKeys(source).forEach((key) => {
134+
if (key === '__proto__' || key === 'constructor' || key === 'prototype')
135+
return
136+
137+
// @ts-expect-error
138+
if (Array.isArray(source[key])) {
139+
// @ts-expect-error
140+
if (!target[key])
141+
// @ts-expect-error
142+
target[key] = []
143+
144+
// @ts-expect-error
145+
deepMergeWithArray(target[key], source[key])
146+
}
147+
// @ts-expect-error
148+
else if (isMergableObject(source[key])) {
149+
// @ts-expect-error
150+
if (!target[key])
151+
// @ts-expect-error
152+
target[key] = {}
153+
154+
// @ts-expect-error
155+
deepMergeWithArray(target[key], source[key])
156+
}
157+
else {
158+
// @ts-expect-error
159+
target[key] = source[key]
160+
}
161+
})
162+
}
163+
164+
return deepMergeWithArray(target, ...sources)
165+
}
166+
108167
function isMergableObject(item: any): item is Object {
109168
return isObject(item) && !Array.isArray(item)
110169
}

0 commit comments

Comments
 (0)