比较2个嵌套的数据结构,即目标+来源,与源源对应物相比,缺少目标值的合并形式是什么?

发布于 2025-01-30 03:50:09 字数 1722 浏览 4 评论 0原文

这样做的更好方法。我是分配两个属性值之一(从两个不同的对象),具体取决于它们的存在,转到第三个数据结构。

如果args对象的值是 nullish 默认对象访问了一个非零值,并分配给了最终结构。

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,
  },
};

What is a better way of doing this. I'am assigning either of two property values (from two different objects), depending on their existence, to a third data-structure.

In case the args object's value is nullish a non nullish value gets accessed from the default object and assigned to the final structure.

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,
  },
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

私藏温柔 2025-02-06 03:50:09

从我上述评论...

1/2 ... OP实际上并没有真正比较。对于某个属性集,OP在目标对象上查找每个属性,并且只有在具有无效的值的情况下,才会从源对象的对应物到丢失的属性进行分配。因此,我选择的一种方法是...

2/2 ...实现一个通用函数,该功能以在目标结构尚未提供非零值的情况下仅写/分配的方式合并两个对象。然后,必须将此功能调用两次,以进行 defaultTest ,第二次要返回完全空的数据结构,args

以上声明有点雄心勃勃,因为至少有2种策略,即人们如何实现这种合并。

因此,下面提供的示例代码实现了两种方法

  • 一种,称为 revit ,该方法遵循推动/修补议程,这是由于强迫每个非无效属性的分配源对象到其目标对象的无效对应物。

  • 第二个称为 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; }

至于针对创建一种config-object的OP的示例,一个人可以完全修补/重新修补/重新安装临时或当前args -config的克隆,而在最后一步中,一个人可以控制配置对象的最终结构是提供最基本的空配置碱体,该结构刚刚由之前创建的完整补丁/Refit-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; }

From my above comments ...

1/2 ... The OP actually is not really comparing. For a certain set of properties the OP looks up each property at a target object, and only in case it features a nullish value there will be an assignment from a source object's counterpart to the missing property. Thus an approach I would choose was ...

2/2 ... implementing a generic function which merges two objects in a way that a source property can only be written/assigned in case the target structure does not already provide a non nullish value. This function then has to be invoked twice once for args and defaulttest and a second time for the to be returned entirely empty data structure and args.

The above statement was a bit ambitious for there are at least 2 strategies of how one could achieve such kind of mergers.

Thus the below provided example code implements two approaches

  • one, called refit, which follows a pushing/patching agenda due to forcing the assignement of every non nullish property in a source-object to its non nullish counterpart of a target-object.

  • a 2nd one, called revive, which resembles a pulling approach for it just reassigns the nullish target-object properties with their non nullish source-object counterparts.

The difference in the results they produce for one and the same preset is going to be demonstrated herby ...

// "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; }

As for the OP's example which targets the creation of kind of a config-object, one could fully patch/refit a clone of the temporary or current args-config, whereas within the last step one has control over the config-object's final structure by providing the most basic empty config-base which just gets revived by the before created full patch/refit-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; }

清风不识月 2025-02-06 03:50:09

如果您的对象的结构是您可以做的一个:

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;
}

调用归一化(args,defaulttest)您将在每个内部键上循环,请检查它是否存在,如果它不存在,则可以替换它在同一路径中默认。

示例:

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

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

console.log(normalize(x,y))

输出:

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

使用此方法,您必须在ARGS输入中具有键。如果键丢失,则不会用默认值代替。为了使其正常运行,即使使用不存在的密钥,您也需要使用第三个结构,例如所有可能的路径。

If the structure of your object is the one you presented you can do:

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;
}

Calling normalize(args, defaulttest) you will loop on each inner key, check if it exist and if it does not exist you substitute it with the default in the same path.

Example:

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"
    }
}

With this approach you must have the key in the args input. If the key is missing, it will not be substituted with the default. To make it work even with not-present keys you need to use a third structure with all the possible path for example.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文