修改对象中的第 n 个嵌套 属性
Modify nth nested property in object
这基本上是一个适用于任何编程语言的逻辑问题,我只是碰巧用 JavaScript 来做这个
基本上想象我有一些像
这样的对象
{Props:"stuff",
other: {
stuff: "etc",
other: {
Extra: "filler",
other: {}
}
}
}
本质上,这是一个具有未知数量额外属性的对象,但每个子对象都有一个嵌套的 属性,名称相同“在本例中为“其他”),深度可能是无限,这意味着根对象中可能有 n 个对象,每个子对象都包含在另一个对象中,该对象的键相同(同样在本例中为“其他”)
听起来可能比实际情况更复杂,但基本上 属性 other 是在根对象中嵌套和重复的
我想做一个函数来打破给定深度量 (n) 的嵌套链,从而将所有进一步嵌套的对象设置为一个值
这听起来可能很复杂,但基本上,在上面的示例中,对象的“其他”属性 的嵌套深度为 3,假设我想打破深度索引 2 处的深度,从而将对象更改为只有一个嵌套的“其他”属性,设置为某个值(例如数字或字符串,例如“5”),而不是设置为另一个也包含另一个的对象属性(以及潜在的附加属性等)
那么我如何创建一个接受基础对象的函数,其中包含未知数量的嵌套属性 Ruth key k
,并使其成为 return 一个新对象(或修改原始的,但我更喜欢 return 一个新的)它几乎与基础对象相同,除了在深度索引 n
处,嵌套的 属性 和键 k
将被设置为值 v
,而不是继续在它的未知附加嵌套深度
的链中
我什至无法理解如何做到这一点,我总是回到设置索引 k
等于根函数的 属性,但传递 属性本身作为参数代替原始根对象(基本上是递归)除非输入对象不包含 属性 k
,在这种情况下只有 属性 本身是 returned,但这只是对return最内层嵌套属性的递归方法,但不知该怎么做才能达到上述结果,这又是一个新对象几乎与基础对象 Ruth 相同,除了它嵌套 属性,深度 n
的键 k
被设置为值 v
,我完全不知道在哪里甚至开始
我们可以像这样使用递归函数
let obj = {Props:"stuff",
other: {
stuff: "etc",
other: {
Extra: "filler",
other: {
stuff: "abc",
other:{
stuff: "acbh",
other: {}
}
}
}
}
};
function normalizeObjectDepth(obj, level, curr_level, key){
if(curr_level==level-1){
obj.other = key;
return;
}
normalizeObjectDepth(obj.other, level, curr_level+1, key);
}
normalizeObjectDepth(obj, 3, 0, 5);
console.log(obj);
您可以使用 reduce
方法创建递归函数,该函数将检查当前递归级别是否小于目标级别,并根据该级别继续递归或将值设置为所需级别。
const data = {
Props: "stuff",
other: {
stuff: "etc",
other: {
Extra: "filler",
other: {}
}
}
}
function modify(obj, key, lvl, val, clvl = 0) {
return Object.entries(obj).reduce((r, [k, v]) => {
if (k === key) {
if (clvl < lvl) {
r[k] = modify(v, key, lvl, val, clvl + 1)
} else {
r[k] = val
}
} else {
r[k] = v;
}
return r;
}, {})
}
const result = modify(data, 'other', 2, 'my value');
console.log(result)
简单 for...in
循环的解决方案。
const data = {
Props: "stuff",
other: {
stuff: "etc",
other: {
Extra: "filler",
other: {
random: 'foo',
other: 'random'
}
}
}
}
function modify(obj, key, lvl, val, clvl = 0) {
const result = {}
for (let k in obj) {
if (k === key) {
if (clvl < lvl) {
result[k] = modify(obj[k], key, lvl, val, clvl + 1)
} else {
result[k] = val
}
} else {
result[k] = obj[k]
}
}
return result
}
const result = modify(data, 'other', 2, 'random');
console.log(result)
这确保了深度克隆,因此不会更改原始对象。唯一的“高级”部分是 JSON.parse(JSON.stringify(...)),但可以用任何深度复制方法代替,或者您可以在使用函数 [=11 之前简单地进行深度复制=]
const modifyNthKey = (obj, key, value, n) => {
let i = 0
let newObj = JSON.parse(JSON.stringify(obj))
let tmpObj = newObj
while (i < n) {
console.log('typeof: ' + typeof tmpObj[`${key}`])
if (typeof tmpObj[`${key}`] !== 'object') {
throw Error('unable to access key. Parent not object')
}
tmpObj = tmpObj[`${key}`]
i++
}
tmpObj[`${key}`] = value
return newObj
}
这是一个相当简单的递归版本。它不会改变您的输入,而是 returns 一个新结构:
const modify = (obj, key, value, depth) => ({
...obj,
[key]: depth <= 0 ? value : modify (obj [key], key, value, depth - 1)
})
const data = {Props: "stuff", other: {stuff: "etc", other: {Extra: "filler", other: {}}}}
console .log (modify (data, 'other', 'new value', 2))
//~> {Props: "stuff", other: {stuff: "etc", other: {Extra: "filler", other:"new value"}}}
console .log (modify (data, 'other', 'new value', 1))
//~> {Props: "stuff", other: {stuff: "etc", other: "new value"}}
console .log (modify (data, 'other', 'new value', 0))
//~> {Props: "stuff", other: "new value"}
.as-console-wrapper {max-height: 100% !important; top: 0}
本质上我们只是复制对象,将目标 属性 ('other'
) 设置为新值(如果我们在递归结束时,当 depth
为 0) 或使用 object[key]
和 depth -1
的递归调用结果(否则。)
请注意,我们不进行完整克隆,因此其他节点可能会通过引用共享。
这基本上是一个适用于任何编程语言的逻辑问题,我只是碰巧用 JavaScript 来做这个
基本上想象我有一些像
这样的对象{Props:"stuff",
other: {
stuff: "etc",
other: {
Extra: "filler",
other: {}
}
}
}
本质上,这是一个具有未知数量额外属性的对象,但每个子对象都有一个嵌套的 属性,名称相同“在本例中为“其他”),深度可能是无限,这意味着根对象中可能有 n 个对象,每个子对象都包含在另一个对象中,该对象的键相同(同样在本例中为“其他”)
听起来可能比实际情况更复杂,但基本上 属性 other 是在根对象中嵌套和重复的
我想做一个函数来打破给定深度量 (n) 的嵌套链,从而将所有进一步嵌套的对象设置为一个值
这听起来可能很复杂,但基本上,在上面的示例中,对象的“其他”属性 的嵌套深度为 3,假设我想打破深度索引 2 处的深度,从而将对象更改为只有一个嵌套的“其他”属性,设置为某个值(例如数字或字符串,例如“5”),而不是设置为另一个也包含另一个的对象属性(以及潜在的附加属性等)
那么我如何创建一个接受基础对象的函数,其中包含未知数量的嵌套属性 Ruth key k
,并使其成为 return 一个新对象(或修改原始的,但我更喜欢 return 一个新的)它几乎与基础对象相同,除了在深度索引 n
处,嵌套的 属性 和键 k
将被设置为值 v
,而不是继续在它的未知附加嵌套深度
我什至无法理解如何做到这一点,我总是回到设置索引 k
等于根函数的 属性,但传递 属性本身作为参数代替原始根对象(基本上是递归)除非输入对象不包含 属性 k
,在这种情况下只有 属性 本身是 returned,但这只是对return最内层嵌套属性的递归方法,但不知该怎么做才能达到上述结果,这又是一个新对象几乎与基础对象 Ruth 相同,除了它嵌套 属性,深度 n
的键 k
被设置为值 v
,我完全不知道在哪里甚至开始
我们可以像这样使用递归函数
let obj = {Props:"stuff",
other: {
stuff: "etc",
other: {
Extra: "filler",
other: {
stuff: "abc",
other:{
stuff: "acbh",
other: {}
}
}
}
}
};
function normalizeObjectDepth(obj, level, curr_level, key){
if(curr_level==level-1){
obj.other = key;
return;
}
normalizeObjectDepth(obj.other, level, curr_level+1, key);
}
normalizeObjectDepth(obj, 3, 0, 5);
console.log(obj);
您可以使用 reduce
方法创建递归函数,该函数将检查当前递归级别是否小于目标级别,并根据该级别继续递归或将值设置为所需级别。
const data = {
Props: "stuff",
other: {
stuff: "etc",
other: {
Extra: "filler",
other: {}
}
}
}
function modify(obj, key, lvl, val, clvl = 0) {
return Object.entries(obj).reduce((r, [k, v]) => {
if (k === key) {
if (clvl < lvl) {
r[k] = modify(v, key, lvl, val, clvl + 1)
} else {
r[k] = val
}
} else {
r[k] = v;
}
return r;
}, {})
}
const result = modify(data, 'other', 2, 'my value');
console.log(result)
简单 for...in
循环的解决方案。
const data = {
Props: "stuff",
other: {
stuff: "etc",
other: {
Extra: "filler",
other: {
random: 'foo',
other: 'random'
}
}
}
}
function modify(obj, key, lvl, val, clvl = 0) {
const result = {}
for (let k in obj) {
if (k === key) {
if (clvl < lvl) {
result[k] = modify(obj[k], key, lvl, val, clvl + 1)
} else {
result[k] = val
}
} else {
result[k] = obj[k]
}
}
return result
}
const result = modify(data, 'other', 2, 'random');
console.log(result)
这确保了深度克隆,因此不会更改原始对象。唯一的“高级”部分是 JSON.parse(JSON.stringify(...)),但可以用任何深度复制方法代替,或者您可以在使用函数 [=11 之前简单地进行深度复制=]
const modifyNthKey = (obj, key, value, n) => {
let i = 0
let newObj = JSON.parse(JSON.stringify(obj))
let tmpObj = newObj
while (i < n) {
console.log('typeof: ' + typeof tmpObj[`${key}`])
if (typeof tmpObj[`${key}`] !== 'object') {
throw Error('unable to access key. Parent not object')
}
tmpObj = tmpObj[`${key}`]
i++
}
tmpObj[`${key}`] = value
return newObj
}
这是一个相当简单的递归版本。它不会改变您的输入,而是 returns 一个新结构:
const modify = (obj, key, value, depth) => ({
...obj,
[key]: depth <= 0 ? value : modify (obj [key], key, value, depth - 1)
})
const data = {Props: "stuff", other: {stuff: "etc", other: {Extra: "filler", other: {}}}}
console .log (modify (data, 'other', 'new value', 2))
//~> {Props: "stuff", other: {stuff: "etc", other: {Extra: "filler", other:"new value"}}}
console .log (modify (data, 'other', 'new value', 1))
//~> {Props: "stuff", other: {stuff: "etc", other: "new value"}}
console .log (modify (data, 'other', 'new value', 0))
//~> {Props: "stuff", other: "new value"}
.as-console-wrapper {max-height: 100% !important; top: 0}
本质上我们只是复制对象,将目标 属性 ('other'
) 设置为新值(如果我们在递归结束时,当 depth
为 0) 或使用 object[key]
和 depth -1
的递归调用结果(否则。)
请注意,我们不进行完整克隆,因此其他节点可能会通过引用共享。