英文:
JavaScript function for multi-level spread syntax
问题
我正在使用一个JavaScript对象,该对象为我正在使用的库提供了默认值
```javascript
const defaults = {
alpha: alpha_val,
beta: {
a: {
i: beta_a_i_val,
ii: beta_a_ii_val,
},
c: beta_c_val,
},
gamma: gamma_val,
};
到目前为止,我一直在以下方式覆盖默认值
const override = {
...defaults,
beta: {
...defaults.beta,
a: {
...defaults.beta.a,
i: beta_a_i_newval,
},
},
gamma: gamma_newval,
};
虽然这样可以工作,但真正的 defaults
对象非常庞大,因此覆盖许多值变得繁琐且难看。
我想知道是否有一种自动执行此类“深度扩展”的方法?
因此,例如创建上面的 override
对象将如下所示:
const newvals = {
beta: {
a: {
i: beta_a_i_newval,
},
},
gamma: gamma_newval,
};
const overrides = someFunction(defaults, newVals)
这似乎是一个常见的要求,但我一直在努力寻找实际执行此操作的方法。我对JavaScript非常陌生,所以非常感谢任何建议!
<details>
<summary>英文:</summary>
I'm working with a JavaScript object that gives the default values for a library I am working with
```javascript
const defaults = {
alpha: alpha_val,
beta: {
a: {
i: beta_a_i_val,
ii: beta_a_ii_val,
},
c: beta_c_val,
},
gamma: gamma_val,
};
So far, I've been overriding the defaults in the following way
const override = {
...defaults,
beta: {
...defaults.beta,
a: {
...defaults.beta.a,
i: beta_a_i_newval,
},
},
gamma: gamma_newval,
};
While this works, the true defaults
object is quite large and so overrides of many values becomes tedious and ugly.
I'm wondering if there is a way to write a function to do this sort of "deep spread" automatically?
So, creating the override
object above, for example, would look something like this:
const newvals = {
beta: {
a: {
i: beta_a_i_newval,
},
},
gamma: gamma_newval,
};
const overrides = someFunction(defaults, newVals)
It seems like this would be a common requirement, but I've been having trouble finding a way to actually do this. I'm very new to JavaScript, so any advice is greatly appreciated!
答案1
得分: 0
你可以使用lodash _.merge函数来实现这个目标:
const myFunc(options_){
const options = {}
const defaultOptions = {}
_.merge(options, defaultOptions)
// ... 你的 myFunc 代码的其余部分
}
从文档中可以看到:
> 它递归地将源对象的自身和继承的可枚举字符串键属性合并到目标对象中。如果目标值存在,则跳过解析为 undefined 的源属性。数组和普通对象属性会递归合并。其他对象和值类型会被赋值覆盖。源对象从左到右应用。后续源对象会覆盖先前源对象的属性赋值。
一般来说,_.merge
是我在构建每个 JavaScript 模块时用来合并默认值和用户选项的方法。我几乎从不在没有使用这种方法的情况下编写项目。
除了这一点,我已经为它创建了一个包装器,它返回一个新的对象而不是突变原始对象,并且我使用了 _.mergeWith 来只合并普通对象而不是数组。因为这会合并数组而不仅仅是对象。
如果你想的话,你也可以这样做。
英文:
You can use the lodash _.merge function for that:
const myFunc(options_){
const options = {}
const defaultOptions = {}
_.merge(options, defaultOptions)
// ... the rest of your myFunc code
}
From the docs:
> it recursively merges own and inherited enumerable string keyed
> properties of source objects into the destination object. Source
> properties that resolve to undefined are skipped if a destination
> value exists. Array and plain object properties are merged
> recursively. Other objects and value types are overridden by
> assignment. Source objects are applied from left to right. Subsequent
> sources overwrite property assignments of previous sources.
generally, _.merge
is the way I merge defaults and user options in the arguments in every JavaScript module I build.
I almost never write a project without using this method.
except that I have made a wrapper for it, which returns a new object instead of mutating the original one, and I've used _.mergeWith to only merge POJOs and not arrays. because this merges the arrays and not only the objects.
You can do the same if you wanted to.
答案2
得分: 0
const defaults = {
alpha: "alpha_val",
beta: {
a: {
i: "beta_a_i_val",
ii: "beta_a_ii_val",
iii: "blah",
},
c: "beta_c_val",
},
d: null,
gamma: "gamma_val",
};
const newvals = {
beta: {
a: {
i: "beta_a_i_newval",
iii: null,
},
},
d: "blah",
gamma: "gamma_newval",
};
const result = cloneObj(defaults, newvals);
//make sure none of the original objects are affected
result.beta.a.test = "ok";
console.log("defaults", defaults);
console.log("newvals", newvals);
console.log("result", result);
function cloneObj(def, obj)
{
const ret = {};
//iterate through the default object
for(let key in def)
{
// get new value
let val = obj[key];
if (val === undefined)
val = def[key]; //fallback to default value
//if it's an object and not a NULL call itself
if (val && val instanceof Object)
val = cloneObj(def[key], val);
ret[key] = val;
}
return ret;
}
英文:
What you are looking for is a recursive function that iterates an object and calls itself for each child object:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const defaults = {
alpha: "alpha_val",
beta: {
a: {
i: "beta_a_i_val",
ii: "beta_a_ii_val",
iii: "blah",
},
c: "beta_c_val",
},
d: null,
gamma: "gamma_val",
};
const newvals = {
beta: {
a: {
i: "beta_a_i_newval",
iii: null,
},
},
d: "blah",
gamma: "gamma_newval",
};
const result = cloneObj(defaults, newvals);
//make sure none of the original objects are affected
result.beta.a.test = "ok";
console.log("defaults", defaults);
console.log("newvals", newvals);
console.log("result", result);
function cloneObj(def, obj)
{
const ret = {};
//iterate through the default object
for(let key in def)
{
// get new value
let val = obj[key];
if (val === undefined)
val = def[key]; //fallback to default value
//if it's an object and not a NULL call itself
if (val && val instanceof Object)
val = cloneObj(def[key], val);
ret[key] = val;
}
return ret;
}
<!-- end snippet -->
答案3
得分: 0
可以编写一个函数,该函数接受默认对象(defaults object)和新值对象(newvals object),然后返回一个合并了两者的新对象。一种方法是使用递归函数,在其中循环遍历新值对象中的键,并执行类似于之前所做的“深度展开”操作。
以下是如何编写someFunction
函数的示例:
function someFunction(defaults, newvals) {
const result = { ...defaults };
for (const key of Object.keys(newvals)) {
if (typeof newvals[key] === 'object' && newvals[key] !== null) {
result[key] = someFunction(defaults[key], newvals[key]);
} else {
result[key] = newvals[key];
}
}
return result;
}
这个函数的工作方式是创建一个名为result
的新对象,该对象是默认对象的浅拷贝。然后,它循环遍历新值对象中的键,并检查值是否为对象。如果是对象,它会递归调用someFunction
函数以合并这两个对象。如果值不是对象,它会简单地将新值分配给result
对象中的键。
然后,您可以像这样使用someFunction
函数:
const overrides = someFunction(defaults, newVals);
这将返回合并了默认值和新值的新对象。
英文:
Yes, you can write a function that takes in the defaults object and the newvals object, and returns a new object that combines the two. One way to do this would be to use a recursive function, where you loop through the keys in the newvals object and perform a "deep spread" similar to what you did before.
Here is an example of how you could write the someFunction function:
function someFunction(defaults, newvals) {
const result = {...defaults};
for (const key of Object.keys(newvals)) {
if (typeof newvals[key] === 'object' && newvals[key] !== null) {
result[key] = someFunction(defaults[key], newvals[key]);
} else {
result[key] = newvals[key];
}
}
return result;
}
This function works by creating a new object called result that is a shallow copy of the defaults object. It then loops through the keys in the newvals object and checks if the value is an object. If it is, it calls the someFunction function recursively to merge the two objects. If the value is not an object, it simply assigns the new value to the key in the result object.
You can then use the someFunction function like this:
const overrides = someFunction(defaults, newVals);
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
function someFunction(defaults, newvals) {
const result = {...defaults};
for (const key of Object.keys(newvals)) {
if (typeof newvals[key] === 'object' && newvals[key] !== null) {
result[key] = someFunction(defaults[key], newvals[key]);
} else {
result[key] = newvals[key];
}
}
return result;
}
const newvals = {
beta: {
a: {
i: "beta_a_i_newval",
},
},
gamma: "gamma_newval",
};
const defaults = {
alpha: "alpha_val",
beta: {
a: {
i: "beta_a_i_val",
ii: "beta_a_ii_val",
},
c: "beta_c_val",
},
gamma: "gamma_val",
};
override = someFunction(defaults,newvals);
console.log(override)
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论