Skip to content

Commit 4272062

Browse files
refactor(utils): add merge function (#45)
* perf(utils): extract `lodash.merge` * test: add test case * refactor: optimize * chore: revert `gitignore` * refactor: update Co-authored-by: John <[email protected]>
1 parent 6a5b074 commit 4272062

File tree

4 files changed

+204
-4
lines changed

4 files changed

+204
-4
lines changed

src/__tests__/utils-merge.test.ts

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import { merge } from '../core/utils/lodash';
2+
3+
describe('utils', () => {
4+
beforeAll(() => {});
5+
6+
beforeEach(() => {});
7+
8+
test('no other object are passed', () => {
9+
const obj1 = {
10+
a: [{ b: { c: 1 } }],
11+
};
12+
13+
expect(merge(obj1)).toStrictEqual(obj1);
14+
});
15+
16+
test('merge should work: case 1', () => {
17+
const obj1 = {
18+
a: [{ b: { c: 1 } }],
19+
};
20+
const obj2 = {
21+
a: [{ b: { d: 2 } }],
22+
};
23+
expect(merge(obj1, obj2)).toStrictEqual({ a: [{ b: { c: 1, d: 2 } }] });
24+
});
25+
26+
test('merge should work: case 2', () => {
27+
const obj1 = {
28+
a: [{ b: { c: 1 } }],
29+
};
30+
const obj2 = {
31+
a: [{ b: { d: undefined } }],
32+
};
33+
expect(merge(obj1, obj2)).toStrictEqual({ a: [{ b: { c: 1 } }] });
34+
});
35+
36+
test('merge should work: case 3', () => {
37+
const obj1 = {
38+
a: [{ b: { c: 1 } }],
39+
};
40+
const obj2 = {
41+
a: [{ b: { c: undefined } }],
42+
};
43+
expect(merge(obj1, obj2)).toStrictEqual({ a: [{ b: { c: 1 } }] });
44+
});
45+
46+
test('merge should work: case 4', () => {
47+
const obj1 = {
48+
a: [{ b: { c: 1 } }],
49+
};
50+
const obj2 = {
51+
a: { b: { c: undefined } },
52+
};
53+
expect(JSON.stringify(merge(obj1, obj2))).toBe(`{"a":[{"b":{"c":1}}]}`);
54+
});
55+
56+
test('merge should work: case 5', () => {
57+
const obj1 = {
58+
a: { b: { c: 1 } },
59+
};
60+
const obj2 = {
61+
a: { b: { c: undefined, d: 2 } },
62+
};
63+
expect(merge(obj1, obj2)).toStrictEqual({ a: { b: { c: 1, d: 2 } } });
64+
});
65+
66+
test('merge should work: case 6', () => {
67+
const obj1 = {
68+
a: { b: { c: 1 } },
69+
};
70+
const obj2 = {
71+
a: 1,
72+
};
73+
expect(merge(obj1, obj2)).toStrictEqual({ a: 1 });
74+
});
75+
76+
test('merge should work: case 7', () => {
77+
const obj1 = {
78+
a: { b: { c: 1 } },
79+
};
80+
const obj2 = {
81+
a: {
82+
b: {
83+
c: () => {
84+
console.log(123);
85+
},
86+
},
87+
},
88+
};
89+
expect(merge(obj1, obj2)).toStrictEqual(obj2);
90+
});
91+
92+
test('merge should work: case 8', () => {
93+
const obj1 = {
94+
a: [{ b: { b1: 123 } }],
95+
};
96+
const obj2 = {
97+
a: [{ b: [1, 2] }],
98+
};
99+
100+
expect(merge(obj1, obj2)).toStrictEqual({
101+
a: [{ b: { 0: 1, 1: 2, b1: 123 } }],
102+
});
103+
});
104+
105+
test('merge should work: case 9', () => {
106+
const object = {
107+
a: [{ g: 3 }, { f: 5 }],
108+
b: 123,
109+
};
110+
111+
const other = {
112+
b: null,
113+
};
114+
115+
expect(merge(object, other)).toStrictEqual({
116+
a: [{ g: 3 }, { f: 5 }],
117+
b: null,
118+
});
119+
});
120+
121+
test('merge should work: case 10', () => {
122+
const object = {
123+
a: [{ g: 3 }, { f: 5 }],
124+
b: 123,
125+
};
126+
127+
const other = {
128+
b: undefined,
129+
};
130+
131+
expect(merge(object, other)).toStrictEqual({
132+
a: [{ g: 3 }, { f: 5 }],
133+
b: 123,
134+
});
135+
});
136+
137+
test('multi object merge should work', () => {
138+
const object = {
139+
a: [{ b: 2 }, { d: 4 }],
140+
};
141+
142+
const other1 = {
143+
a: [{ c: 3 }, { e: 5 }],
144+
};
145+
146+
const other2 = {
147+
a: [{ g: 3 }, { f: 5 }],
148+
b: 123,
149+
};
150+
151+
const other3 = {
152+
b: {
153+
my: 'name',
154+
},
155+
};
156+
157+
expect(merge(object, other1, other2, other3)).toStrictEqual({
158+
a: [
159+
{ b: 2, c: 3, g: 3 },
160+
{ d: 4, e: 5, f: 5 },
161+
],
162+
b: { my: 'name' },
163+
});
164+
});
165+
});

src/core/utils/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const isString = (val: unknown): val is string =>
88
toTypeString(val) === '[object String]';
99
export const isPlainObject = (val: unknown): val is Record<string, any> =>
1010
toTypeString(val) === '[object Object]';
11+
export const isArray = (val: unknown): val is any[] => Array.isArray(val);
1112

1213
export const isObject = (val: unknown): val is Record<any, any> =>
1314
val !== null && typeof val === 'object';

src/core/utils/lodash/index.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
/**
2-
* source by `lodash`
3-
* https://github.com/lodash/lodash.git
4-
*/
51
/* istanbul ignore next */
62
export { default as debounce } from './debounce';
73
export { default as throttle } from './throttle';
4+
export { default as merge } from './merge';

src/core/utils/lodash/merge.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { isPlainObject, isArray, isObject } from '../index';
2+
3+
type MergeObject = Record<string, any>;
4+
5+
function baseMerge(origin: MergeObject, target: MergeObject) {
6+
for (const key in target) {
7+
if (target[key] === undefined) {
8+
continue;
9+
}
10+
11+
if (
12+
!isObject(target[key]) || // `target[key]` is not an object
13+
!isObject(origin[key]) || // `target[key]` is not an object
14+
!(key in origin) // `key` is not in the origin object
15+
) {
16+
origin[key] = target[key];
17+
continue;
18+
}
19+
20+
if (isPlainObject(target[key]) || isArray(target[key])) {
21+
baseMerge(origin[key], target[key]);
22+
}
23+
}
24+
}
25+
26+
function merge(origin: MergeObject, ...others: MergeObject[]): any {
27+
const result = Object.assign({}, origin);
28+
if (!others.length) return result;
29+
30+
for (const item of others) {
31+
baseMerge(result, item);
32+
}
33+
34+
return result;
35+
}
36+
37+
export default merge;

0 commit comments

Comments
 (0)