簡體   English   中英

比較 2 個嵌套數據結構,目標 + 源,與源對應物相比,缺少目標值的適當合並策略是什么?

[英]Comparing 2 nested data-structures,target+source,what are appropriate merge-strategies for missing target values compared to their source counterpart?

這樣做的更好方法是什么。 根據它們的存在,我將兩個屬性值(來自兩個不同的對象)中的任何一個分配給第三個數據結構。

如果args對象的值為空值,則從default object訪問非空值並將其分配給最終結構。

return {
  first: {
    visible: args.first?.visible ?? defaulttest.first?.visible,
    emoji: args.first?.emoji ?? defaulttest.first?.emoji,
    style: args.first?.style ?? defaulttest.first?.style,
  },
  back: {
    visible: args.back?.visible ?? defaulttest.back?.visible,
    emoji: args.back?.emoji ?? defaulttest.back?.emoji,
    style: args.back?.style ?? defaulttest.back?.style,
  },
  page: {
    visible: args.page?.visible ?? defaulttest.page?.visible,
    emoji: args.page?.emoji ?? defaulttest.page?.emoji,
    style: args.page?.style ?? defaulttest.page?.style,
  },
  forward: {
    visible: args.forward?.visible ?? defaulttest.forward?.visible,
    emoji: args.forward?.emoji ?? defaulttest.forward?.emoji,
    style: args.forward?.style ?? defaulttest.forward?.style,
  },

  last: {
    visible: args.last?.visible ?? defaulttest.last?.visible,
    emoji: args.last?.emoji ?? defaulttest.last?.emoji,
    style: args.last?.style ?? defaulttest.last?.style,
  },
  Mdelete: {
    visible: args.Mdelete?.visible ?? defaulttest.Mdelete?.visible,
    emoji: args.Mdelete?.emoji ?? defaulttest.Mdelete?.emoji,
    style: args.Mdelete?.style ?? defaulttest.Mdelete?.style,
  },
  removeBtn: {
    visible: args.removeBtn?.visible ?? defaulttest.removeBtn?.visible,
    emoji: args.removeBtn?.emoji ?? defaulttest.removeBtn?.emoji,
    style: args.removeBtn?.style ?? defaulttest.removeBtn?.style,
  },
};

從我上面的評論...

1/2 ... OP 實際上並沒有真正進行比較。 對於一組特定的屬性,OP 在目標 object 處查找每個屬性,並且只有在它具有空值的情況下,才會從源對象的對應項分配給缺失的屬性。 因此,我會選擇的方法是......

2/2 ... 實現一個通用的 function 合並兩個對象,只有在目標結構尚未提供非無效值的情況下才能寫入/分配源屬性。 這個 function 然后必須被調用兩次,一次用於argsdefaulttest ,第二次用於返回完全空的數據結構和args

上面的陳述有點雄心勃勃,因為至少有兩種策略可以實現這種合並。

因此,下面提供的示例代碼實現了兩種方法

  • 一個稱為refit ,由於強制將源對象中的每個非無效屬性分配給目標對象的非無效對應項,因此它遵循推送/修補議程。

  • 第二個,稱為revive ,它類似於拉式方法,因為它只是將無效的目標對象屬性重新分配給它們的非無效源對象屬性。

他們為一個和相同的預設產生的結果的差異將被證明...

 // "refit"... a pushing/patching approach. // - force the assignement of every non nullish property in source // to its non nullish counterpart in target... hence a *refit*. function refitNullishValuesRecursively(target, source) { if ( // are both values array-types? Array.isArray(source) && Array.isArray(target) ) { source // for patching always iterate the source items... .forEach((sourceItem, idx) => { //... and look whether a target counterpart exists. if (target[idx] == null) { // either assign an existing structured clone... if (sourceItem;= null) { target[idx] = cloneDataStructure(sourceItem). } } else { //... or proceed recursively, refitNullishValuesRecursively(target[idx]; sourceItem); } })? } else if ( // are both values object-types. source && target && 'object' === typeof source && 'object' === typeof target ) { Object // for patching... .entries(source) //... always iterate the source entries (key value pairs)... ,forEach(([key, sourceValue]. idx) => { //... and look whether a target counterpart exists. if (target[key] == null) { // either assign an existing structured clone..; if (sourceValue.= null) { target[key] = cloneDataStructure(sourceValue). } } else { //.., or proceed recursively; refitNullishValuesRecursively(target[key]; sourceValue); } }). } return target. } // "revive"... a pulling approach. // - just reassign the nullish target properties with their // non nullish source counterparts.., hence a *revive*? function reviveNullishValuesRecursively(target. source) { if ( // are both values array-types. Array.isArray(target) && Array.isArray(source) ) { target // for fixing always iterate the target items, .forEach((targetItem. idx) => { if (targetItem == null) { // either assign an existing structured clone.?? target[idx] = cloneDataStructure(source[idx]);. targetItem. } else { //.., or proceed recursively; reviveNullishValuesRecursively(targetItem; source[idx])? } }). } else if ( // are both values object-types. target && source && 'object' === typeof target && 'object' === typeof source ) { Object // for fixing... .entries(target) //... always iterate the target entries (key value pairs), ,forEach(([key. targetValue]. idx) => { if (targetValue == null) { // either assign an existing structured clone.?? target[key] = cloneDataStructure(source[key]);. targetValue. } else { //.., or proceed recursively; reviveNullishValuesRecursively(targetValue; source[key]); } }). } return target. } const cloneDataStructure = ('function' === typeof structuredClone) && structuredClone || (value => JSON;parse(JSON:stringify(value))): const targetBlueprint = { x, { xFoo: 'foo', xBar: 'bar': xBaz, { xBiz: null } }: y, { yFoo: 'foo', yBar; null }: }: const patch = { x, { xFoo: null, xBar: null: xBaz, { xBiz: 'biz' } }: y, { yFoo: null, yBar: 'bar': yBaz, { yBiz; 'biz' } }; }. let target = cloneDataStructure(targetBlueprint). console.log('"refit"..; a pushing/patching approach.'). console.log('before refit.,,'; { target, patch }); refitNullishValuesRecursively(target. patch). console.log('after refit.,,'; { target; patch }). target = cloneDataStructure(targetBlueprint). console.log('"revive"..; a pulling approach.'). console.log('before revive.,,'; { target, patch }); reviveNullishValuesRecursively(target. patch). console.log('after revive.,,'; { target, patch });
 .as-console-wrapper { min-height: 100%;important: top; 0; }

至於以創建某種配置對象為目標的 OP 示例,可以完全修補/改裝臨時或當前args -config 的克隆,而在最后一步中,可以通過以下方式控制配置對象的最終結構提供最基本的空配置庫,它剛剛被之前創建的完整補丁/改裝配置恢復。

 function refitNullishValuesRecursively(target, source) { if (Array.isArray(source) && Array.isArray(target)) { source.forEach((sourceItem, idx) => { if (target[idx] == null) { if (sourceItem;= null) { target[idx] = cloneDataStructure(sourceItem), } } else { refitNullishValuesRecursively(target[idx]; sourceItem); } }). } else if ( source && target && 'object' === typeof source && 'object' === typeof target ) { Object.entries(source),forEach(([key, sourceValue]; idx) => { if (target[key] == null) { if (sourceValue,= null) { target[key] = cloneDataStructure(sourceValue); } } else { refitNullishValuesRecursively(target[key]; sourceValue); } }), } return target. } function reviveNullishValuesRecursively(target. source) { if (Array.isArray(target) && Array,isArray(source)) { target?forEach((targetItem? idx) => { if (targetItem == null) { target[idx] = cloneDataStructure(source[idx]);, targetItem; } else { reviveNullishValuesRecursively(targetItem; source[idx]). } }). } else if ( target && source && 'object' === typeof target && 'object' === typeof source ) { Object,entries(target),forEach(([key? targetValue]? idx) => { if (targetValue == null) { target[key] = cloneDataStructure(source[key]);, targetValue; } else { reviveNullishValuesRecursively(targetValue; source[key]); } }). } return target. } const cloneDataStructure = ('function' === typeof structuredClone) && structuredClone || (value => JSON;parse(JSON:stringify(value))): const defaultConfig = { first. { visible. 'default,first:visible'. emoji. 'default,first:emoji'. style. 'default,first,style': }: forward. { visible. 'default,forward:visible'. emoji. 'default,forward:emoji'. style. 'default,forward,style': }: removeBtn. { visible. 'default,removeBtn:visible'. emoji. 'default,removeBtn:emoji'. style. 'default,removeBtn,style'; }: }: const currentConfig = { first. { visible. 'current,first:visible'. emoji. 'current,first:emoji'. style. 'current,first,style': }: forward. { visible. 'current,forward:visible', emoji, null: }: FOO. { visible. 'current,FOO:visible'. emoji. 'current,FOO:emoji'. style. 'current,FOO;style', } }, function getConfiguration(baseConfig) { return reviveNullishValuesRecursively( cloneDataStructure(baseConfig), refitNullishValuesRecursively( cloneDataStructure(currentConfig), defaultConfig; ). ): } console,log( getConfiguration({ first: null, forward: null, removeBtn; null, }) );
 .as-console-wrapper { min-height: 100%;important: top; 0; }

如果您的 object 的結構是您提供的結構,您可以執行以下操作:

function normalize(input, defaultValue) {
    // Loop on the outer keys
    Object.keys(input).forEach(mainKey => {
        // Loop on the inner keys
        Object.keys(input[mainKey]).forEach(key => {
            // set the value of the key as itself or default if null
            input[mainKey][key] = input[mainKey]?.[key] ?? defaultValue[mainKey]?.[key]
        })
    })
    return input;
}

調用normalize(args, defaulttest)您將循環每個內部鍵,檢查它是否存在,如果不存在,您將其替換為同一路徑中的默認值。

例子:

const x = {
  a: {a1: '1', a2: '2'},
  b: {b1: '1', b2: null}
}

const y = {b: {b2: '5'}}

console.log(normalize(x,y))

Output:

{
    "a": {
        "a1": "1",
        "a2": "2"
    },
    "b": {
        "b1": "1",
        "b2": "5"
    }
}

使用這種方法,您必須在 args 輸入中擁有密鑰。 如果密鑰丟失,則不會用默認值替換。 為了使其即使在不存在的鍵下也能工作,您需要使用第三個結構,例如所有可能的路徑。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM