通过匹配 Javascript 中深层嵌套对象中的 id 来更新对象值?
我有这个 Javascript 对象:
{
title: "Securities Finance Trade Entry",
children: [
{
containerType: "Tabs",
children: [
{
title: "Common",
children: [
{
containerType: "Row",
children: [
{
input: "ComboBox",
label: "Trade Type",
options: ["Repo", "Buy/Sell", "FeeBased"],
value: "FeeBased"
},
{
input: "ComboBox",
label: "Direction",
options: ["Loan", "Borrow"],
value: "Borrow"
}
]
},
{
containerType: "Row",
children: [
{
containerType: "Column",
children: [
{
containerType: "Row",
children: [
{
input: "Text",
label: "Book",
value: "test"
},
{
input: "Text",
label: "Counterparty",
value: "test"
}
]
},
{
containerType: "Row",
children: [
{
input: "Date",
label: "StartDate",
value: "10/02/2021"
},
{
input: "Date",
label: "EndDate",
value: "10/02/2021"
}
]
},
{
containerType: "Row",
children: [
{
input: "Text",
label: "Security",
value: "test"
},
{
input: "Numeric",
label: "Quantity",
value: "test"
}
]
}
]
}
]
}
]
}
]
}
]
}
给定一个特定的标签,我需要找到带有该标签的对象,然后替换该值,我遇到的问题是这种格式可能更加嵌套,我无法查看任何特定级别,我需要查看在所有级别的所有对象中,只需替换标签匹配的对象中的值即可。我尝试过递归,但无法让它工作,请问有人可以建议吗?
编辑:
这是我的尝试,但失败得很严重:)
const deepReplace = (obj, id, value) => {
if (obj.children) {
obj = obj.children.map((childObj) => {
if (typeof childObj === "object") {
deepReplace(childObj, id, value)
}
})
return obj;
};
if (obj.label === id) {
obj['value'] = value;
return obj
};
};
const newObj = deepReplace(myObj, 'TradeType', 'Repo')
I have this Javascript object:
{
title: "Securities Finance Trade Entry",
children: [
{
containerType: "Tabs",
children: [
{
title: "Common",
children: [
{
containerType: "Row",
children: [
{
input: "ComboBox",
label: "Trade Type",
options: ["Repo", "Buy/Sell", "FeeBased"],
value: "FeeBased"
},
{
input: "ComboBox",
label: "Direction",
options: ["Loan", "Borrow"],
value: "Borrow"
}
]
},
{
containerType: "Row",
children: [
{
containerType: "Column",
children: [
{
containerType: "Row",
children: [
{
input: "Text",
label: "Book",
value: "test"
},
{
input: "Text",
label: "Counterparty",
value: "test"
}
]
},
{
containerType: "Row",
children: [
{
input: "Date",
label: "StartDate",
value: "10/02/2021"
},
{
input: "Date",
label: "EndDate",
value: "10/02/2021"
}
]
},
{
containerType: "Row",
children: [
{
input: "Text",
label: "Security",
value: "test"
},
{
input: "Numeric",
label: "Quantity",
value: "test"
}
]
}
]
}
]
}
]
}
]
}
]
}
given a specific label I need to find the object with that label and then replace the value, the issue I have is that this format could be more nested and I cant look at any specific level, I need to look at all objects at all levels then just replace the value in the one where the label matches. I have tried recursion but can't get it to work, please can anyone advise?
EDIT:
This is my attempt but it fails badly :)
const deepReplace = (obj, id, value) => {
if (obj.children) {
obj = obj.children.map((childObj) => {
if (typeof childObj === "object") {
deepReplace(childObj, id, value)
}
})
return obj;
};
if (obj.label === id) {
obj['value'] = value;
return obj
};
};
const newObj = deepReplace(myObj, 'TradeType', 'Repo')
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这个问题似乎很适合递归。因为我们必须多次重复操作,而且我们不确定有多少次。
This problem seems to be a good candidate for recursion. Since we have to do a repeated operation multiple times and we are not sure how many.
如果您对不会改变原始数据但返回替换了适当值的副本的答案感兴趣,它可能如下所示:
我们测试我们的输入是否是一个对象。如果没有,我们只需将其退回即可。如果是,我们通过包含该对象的所有根属性来创建一个新对象,如果我们有正确的
标签
,则替换value
属性,然后在< code>children 节点。不可变数据有各种各样的好处。我建议您尽可能采用它。
更新:代码解释
评论表明这很难阅读。这是我试图解释的。
首先,我们柯里化该函数,传递
label
和value
来获取另一个函数,该函数接受obj
并返回一个进行更改的对象。这在内部(我们稍后将对deepReplace (label, value)
的引用传递给map
)和外部都很有用,因为我们现在只需传递标签和值即可得到一个我们可以应用于许多不同对象的函数。然后我们开始条件运算符(三元)。我们使用
Object (obj) === obj
来测试输入参数是否是一个对象。有很多方法可以对此进行测试,包括typeof
和对值调用Object.prototype.toString()
。我发现这个非常强大。如果它不是一个对象,那么我们点击最后一行
: obj
,并返回原始值。如果它是一个对象,我们返回一个通过三个步骤创建的对象:这里我们只是将原始对象扩展到新对象中。这是浅克隆的一种形式。
在这里,我们测试对象的
label
属性是否与我们的目标匹配。如果是,我们将{value}
(这是{value: value}
的现代简写)传播到我们的对象中。如果没有,我们将传播一个空对象,从而不添加任何属性。在这里,我们处理
孩子
。如果我们的节点没有children
属性,我们将传播一个空对象。如果确实如此,我们将递归调用映射到它们之上的同一函数,并传播到其children
属性为结果的对象中。这里柯里化很有帮助,因为如果我们的函数看起来像deepReplace = (label, value, obj) => ,我们只需调用
,那么这里我们必须调用map (deepReplace (label, value))
即可。 /* ... */map ((item) => deepReplace (label, value, item))
。这没什么大不了的,但这种方式感觉更干净。因此,在较旧的 JS 中,我们可能会编写如下所示的等效代码:
这本质上是等效的。我发现新的语法更有启发性,但两者都可以。
我们可能应该处理数组的情况,这会给实现增加一点点,(未经测试的)版本可能如下所示:
这里我们首先检查数组,如果是的话,
map
结果,就像我们对孩子
所做的那样。If you're interested in an answer that doesn't mutate your original data but returns a copy with the appropriate values replaced, it might look like this:
We test if our input is an object. If not, we simply return it. If it is, we create a new object by including all the root properties of the object, replacing a
value
property if we have the rightlabel
, and then recurring on thechildren
nodes.There are all sorts of benefits to immutable data. I would recommend adopting it when you can.
Update: Code Explanation
A comment suggested that this was challenging to read. Here's my attempt to explain it.
First, we curry the function, passing
label
andvalue
to get back another function that takesobj
and returns an object with the change made. This is useful both internally (we later will pass a reference todeepReplace (label, value)
tomap
) and externally, as we can now just pass the label and value and get a function we can apply to many different objects.We then begin a conditional operator (ternary). We test whether the input parameter is an object, using
Object (obj) === obj
. There are many ways to test this, includingtypeof
and callingObject.prototype.toString()
on the value. I find this one quite robust.If it is not an object, then we hit the last line
: obj
, and return the original value. If it is an object, we return an object created out of three steps:Here we just spread the original object into our new one. This is a form of shallow cloning.
Here we test if the object's
label
property matches our target. If it does, we spread{value}
, which is modern shorthand for{value: value}
into our object. If it doesn't, we spread an empty object, thereby adding no properties.And here, we handle
children
. If our node has nochildren
property, we spread an empty object. If it does, wemap
a recursive call to the same function over them, and spread in an object whosechildren
property is the result. Here the currying helps, as we can just callmap (deepReplace (label, value))
If our function looked likedeepReplace = (label, value, obj) => /* ... */
, then here we would have to callmap ((item) => deepReplace (label, value, item))
. It's not a big deal, but this way feels cleaner.So in older JS, we might have written the equivalent code like this:
That would have been essentially equivalent. I find the newer syntax more enlightening, but either would work.
We probably should have handled the array case, and that would add a little bit to the implementation, and an (untested) version might look like this:
Here we check for an array first, and if so,
map
the results, as we do forchildren
.