From 9f72e1dab72ad8b7e75c2fc9faac66e19efa2e35 Mon Sep 17 00:00:00 2001 From: Jose Berrocal Date: Wed, 7 Jun 2017 02:22:45 -0600 Subject: [PATCH] chore: fix and simplify deep merging --- src/translate.service.ts | 4 +- src/util.ts | 108 ++++++++++++--------------------------- 2 files changed, 34 insertions(+), 78 deletions(-) diff --git a/src/translate.service.ts b/src/translate.service.ts index 8b15d0a8..e3814271 100644 --- a/src/translate.service.ts +++ b/src/translate.service.ts @@ -14,7 +14,7 @@ import {TranslateStore} from "./translate.store"; import {TranslateLoader} from "./translate.loader"; import {MissingTranslationHandler, MissingTranslationHandlerParams} from "./missing-translation-handler"; import {TranslateParser} from "./translate.parser"; -import {deepMerge, isDefined} from "./util"; +import {mergeDeep, isDefined} from "./util"; export const USE_STORE = new OpaqueToken('USE_STORE'); @@ -269,7 +269,7 @@ export class TranslateService { */ public setTranslation(lang: string, translations: Object, shouldMerge: boolean = false): void { if(shouldMerge && this.translations[lang]) { - this.translations[lang] = deepMerge(this.translations[lang], translations); + this.translations[lang] = mergeDeep(this.translations[lang], translations); } else { this.translations[lang] = translations; } diff --git a/src/util.ts b/src/util.ts index 435118f4..6da6459b 100644 --- a/src/util.ts +++ b/src/util.ts @@ -16,32 +16,32 @@ * @returns {boolean} True if arguments are equal. */ export function equals(o1: any, o2: any): boolean { - if(o1 === o2) return true; - if(o1 === null || o2 === null) return false; - if(o1 !== o1 && o2 !== o2) return true; // NaN === NaN + if (o1 === o2) return true; + if (o1 === null || o2 === null) return false; + if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN let t1 = typeof o1, t2 = typeof o2, length: number, key: any, keySet: any; - if(t1 == t2 && t1 == 'object') { - if(Array.isArray(o1)) { - if(!Array.isArray(o2)) return false; - if((length = o1.length) == o2.length) { - for(key = 0; key < length; key++) { - if(!equals(o1[key], o2[key])) return false; + if (t1 == t2 && t1 == 'object') { + if (Array.isArray(o1)) { + if (!Array.isArray(o2)) return false; + if ((length = o1.length) == o2.length) { + for (key = 0; key < length; key++) { + if (!equals(o1[key], o2[key])) return false; } return true; } } else { - if(Array.isArray(o2)) { + if (Array.isArray(o2)) { return false; } keySet = Object.create(null); - for(key in o1) { - if(!equals(o1[key], o2[key])) { + for (key in o1) { + if (!equals(o1[key], o2[key])) { return false; } keySet[key] = true; } - for(key in o2) { - if(!(key in keySet) && typeof o2[key] !== 'undefined') { + for (key in o2) { + if (!(key in keySet) && typeof o2[key] !== 'undefined') { return false; } } @@ -56,70 +56,26 @@ export function isDefined(value: any): boolean { return typeof value !== 'undefined' && value !== null; } -/** Start deep merge, from https://github.com/KyleAMathews/deepmerge **/ -function isMergeableObject(val: T): boolean { - let nonNullObject = val && typeof val === 'object'; - - return nonNullObject - && Object.prototype.toString.call(val) !== '[object RegExp]' - && Object.prototype.toString.call(val) !== '[object Date]'; -} - -function emptyTarget(val: T): any { - return Array.isArray(val) ? [] : {}; -} - -function cloneIfNecessary(value: T, options: DeepMergeOptions): T { - let clone = options && options.clone === true; - return clone && isMergeableObject(value) ? deepMerge(emptyTarget(value), value, options) : value; +export function isObject(item: any): boolean { + return (item && typeof item === 'object' && !Array.isArray(item)); } -function defaultArrayMerge(target: T[], source: T[], options: DeepMergeOptions): T[] { - let destination = target.slice(); - source.forEach(function(e, i) { - if(typeof destination[i] === 'undefined') { - destination[i] = cloneIfNecessary(e, options); - } else if(isMergeableObject(e)) { - destination[i] = deepMerge(target[i], e, options); - } else if(target.indexOf(e) === -1) { - destination.push(cloneIfNecessary(e, options)); - } - }); - return destination; -} - -function mergeObject(target: any, source: any, options: DeepMergeOptions): T { - let destination: any = {}; - if(isMergeableObject(target)) { - Object.keys(target).forEach(function(key) { - destination[key] = cloneIfNecessary(target[key], options) +export function mergeDeep(target: any, source: any): any { + target = JSON.parse(JSON.stringify(target)); + source = JSON.parse(JSON.stringify(source)); + let output = Object.assign({}, target); + if (isObject(target) && isObject(source)) { + Object.keys(source).forEach((key: any) => { + if (isObject(source[key])) { + if (!(key in target)) { + Object.assign(output, { [key]: source[key] }); + } else { + output[key] = mergeDeep(target[key], source[key]); + } + } else { + Object.assign(output, { [key]: source[key] }); + } }); } - Object.keys(source).forEach(function(key) { - if(!isMergeableObject(source[key]) || !target[key]) { - destination[key] = cloneIfNecessary(source[key], options); - } else { - destination[key] = deepMerge(target[key], source[key], options); - } - }); - return destination; -} - -export interface DeepMergeOptions { - clone?: boolean; - - arrayMerge?(destination: T, source: T, options?: DeepMergeOptions): T; -} - -export function deepMerge(target: T, source: T, options?: DeepMergeOptions): T { - let array = Array.isArray(source); - options = options || {arrayMerge: defaultArrayMerge} as any; - let arrayMerge: any = options.arrayMerge || defaultArrayMerge; - - if(array) { - return Array.isArray(target) ? arrayMerge(target, source, options) : cloneIfNecessary(source, options); - } else { - return mergeObject(target, source, options); - } + return output; } -/** End deep merge **/