From 5bffe4366a14310504894dd32bab482ef584f457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 24 Apr 2024 21:21:11 +0200 Subject: [PATCH] deep merge --- .../src/packages/core/utils/index.ts | 3 +- .../core/utils/object/deep-merge.function.ts | 22 +++++++ .../core/utils/object/deep-merge.test.ts | 61 +++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/utils/object/deep-merge.function.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/utils/object/deep-merge.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts index 8b243bcb6c..09d60ef911 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/index.ts @@ -16,4 +16,5 @@ export * from './string/generate-umbraco-alias.function.js'; export * from './string/increment-string.function.js'; export * from './string/split-string-to-array.js'; export * from './string/to-camel-case/to-camel-case.function.js'; -export * from './type/diff.type.js'; +export type * from './type/deep-partial.type.js'; +export type * from './type/diff.type.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/object/deep-merge.function.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/object/deep-merge.function.ts new file mode 100644 index 0000000000..b08629cb1d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/object/deep-merge.function.ts @@ -0,0 +1,22 @@ +import type { DeepPartial } from '../type/deep-partial.type.js'; + +/** + * Deep merge two objects. + * @param target + * @param ...sources + */ +export function umbDeepMerge(source: DeepPartial, fallback: T) { + const result = { ...fallback }; + + for (const key in source) { + if (Object.prototype.hasOwnProperty.call(source, key) && source[key] !== undefined) { + if (source[key].constructor === Object && fallback[key].constructor === Object) { + result[key] = umbDeepMerge(source[key], fallback[key]); + } else { + result[key] = source[key] as any; + } + } + } + + return result; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/utils/object/deep-merge.test.ts b/src/Umbraco.Web.UI.Client/src/packages/core/utils/object/deep-merge.test.ts new file mode 100644 index 0000000000..61509c9162 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/utils/object/deep-merge.test.ts @@ -0,0 +1,61 @@ +import { expect } from '@open-wc/testing'; +import { umbDeepMerge } from './deep-merge.function.js'; + +describe('UmbDeepMerge', () => { + beforeEach(() => {}); + + describe('merge just objects', () => { + it('transfers defined properties', () => { + const defaults = { + prop1: { + name: 'prop1', + value: 'value1', + }, + prop2: { + name: 'prop2', + value: 'value2', + }, + }; + const source = { + prop2: { + name: 'prop2_updatedName', + }, + }; + const result = umbDeepMerge(source, defaults); + + expect(result.prop1.name).to.equal('prop1'); + expect(result.prop2.name).to.equal('prop2_updatedName'); + }); + }); + + describe('merge objects with arrays', () => { + // The arrays should not be merged, but take the value from the main object. [NL] + it('transfers defined properties', () => { + const defaults = { + prop1: { + name: 'prop1', + value: ['entry1', 'entry2', 'entry3'], + }, + prop2: { + name: 'prop2', + value: ['entry4', 'entry4', 'entry5'], + }, + }; + const source = { + prop1: { + name: 'prop1_updatedName', + }, + prop2: { + name: 'prop2_updatedName', + value: ['entry666'], + }, + }; + const result = umbDeepMerge(source, defaults); + + expect(result.prop1.name).to.equal('prop1_updatedName'); + expect(result.prop2.value.join(',')).to.equal(defaults.prop1.value.join(',')); + expect(result.prop2.name).to.equal('prop2_updatedName'); + expect(result.prop2.value.join(',')).to.equal(source.prop2.value.join(',')); + }); + }); +});