diff --git a/packages/util/src/__tests__/object.test.ts b/packages/util/src/__tests__/object.test.ts index 024d2e72..3a47caff 100644 --- a/packages/util/src/__tests__/object.test.ts +++ b/packages/util/src/__tests__/object.test.ts @@ -1,5 +1,5 @@ import 'jest'; -import { objectEquals, flattenObject, getObjectProperty, getObjectValues, setObjectProperty, sortProperties } from '../object'; +import { objectEquals, flattenObject, getObjectProperty, getObjectValues, setObjectProperty, sortProperties, merge } from '../object'; describe('util', () => { describe('#getValues', () => { @@ -205,4 +205,46 @@ describe('util', () => { expect(objectEquals('', false)).toBe(false); }); }); + describe('#merge', () => { + it('should merge objects in arrays ny index', () => { + const object = { + 'a': [{ 'b': 2 }, { 'd': 4 }] + }; + const other = { + 'a': [{ 'c': 3 }, { 'e': 5 }] + }; + merge(object, other); + expect(object).toStrictEqual({ 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }); + }); + it('should merge objects and add missing elements', () => { + const object = { + 'a': [{ 'b': 2 }, { 'd': 4 }] + }; + const other = { + 'a': [{ 'c': 3 }, { 'e': 5 }, { 'f': 6 }] + }; + merge(object, other); + expect(object).toStrictEqual({ 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }, { 'f': 6 }] }); + }); + it('should replace arguments in unequal in arrays', () => { + const object = { + 'a': { 'b': { 'c': [1,2] } } + }; + const other = { + 'a': { 'b': { 'c': [2] } } + }; + merge(object, other); + expect(object).toStrictEqual({ 'a': { 'b': { 'c': [2,2] } } }); + }); + it('should use mergeFn when passed', () => { + const object = { + 'a': { 'b': { 'c': [1,2] } } + }; + const other = { + 'a': { 'b': { 'c': [2] } } + }; + merge(object, other, merge); + expect(object).toStrictEqual({ 'a': { 'b': { 'c': [2,2] } } }); + }); + }); }); \ No newline at end of file diff --git a/packages/util/src/object.ts b/packages/util/src/object.ts index ca1d8e07..e5f9f0be 100644 --- a/packages/util/src/object.ts +++ b/packages/util/src/object.ts @@ -185,12 +185,17 @@ export function deepClone(value: T): T { } /** - * Merge multiple objects recursively into the target object - * @param {object} object target into which sources are merged - * @param {...object} sources source from which to merge - * @returns + * Merge multiple objects recursively into the target object object. + * + * For arrays the elements of the source array are merged into the target array, elements on the same index are merged. + * If the source array is longer than the target array the extra elements are appended to the target array. + * If the source array is shorter than the target array the extra elements in the target array are not deleted. + * * + * @param object target into which sources are merged + * @param sources source from which to merge + * @returns The target object with the sources merged into it */ -export function merge(object: any, ...sources: any[]) { +export function merge(object: any, ...sources: any[]): any { for (const source of sources.filter(s => s)) { for (const key of Object.keys(source)) { if (isObject(object[key]) && isObject(source[key])) {