[英]Compare data in source sheet with target sheet and copy missing rows to target sheet with Google Apps Script
[英]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 然后必须被调用两次,一次用于
args
和defaulttest
,第二次用于返回完全空的数据结构和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.