javascript - 如何使用Javascript更新深度嵌套的未知大小对象数组中的属性?
问题描述
我有大量对象,我需要递归循环并找到我需要通过它更新的对象uid
并更新该data
对象中的属性。这个阵列的大小和形状未知。以下方法有效,但我想知道是否有一种“更清洁”的方式来执行此过程。
以下代码循环遍历每个对象/数组,直到找到具有属性的对象uid
,如果这uid
等于我需要更新的块,则设置data
该对象的属性。有一个更好的方法吗?
遍历方法
function traverse(x) {
if (isArray(x)) {
traverseArray(x)
} else if ((typeof x === 'object') && (x !== null)) {
traverseObject(x)
} else {
}
}
function traverseArray(arr) {
arr.forEach(function (x) {
traverse(x)
})
}
function traverseObject(obj) {
for (var key in obj) {
// check if property is UID
if (key == "uid") {
// check if this uid is equal to what we are looking for
if (obj[key] == "bcd") {
// confirm it has a data property
if (obj.hasOwnProperty('data')) {
// assign new data to the data property
obj['data'] = newData;
return;
}
}
}
if (obj.hasOwnProperty(key)) {
traverse(obj[key])
}
}
}
function isArray(o) {
return Object.prototype.toString.call(o) === '[object Array]'
}
// usage:
traverse(this.sections)
样本数据
this.sections = [
{
uid: "abc",
layout: {
rows: [
{
columns: [
{
blocks: [
{
uid: "bcd",
data: {
content: "how are you?"
}
}
]
},
{
blocks: [
{
uid: "cde",
data: {
content: "how are you?"
}
}
]
},
],
},
{
columns: [
{
blocks: [
{
uid: "def",
data: {
content: "how are you?"
}
}
]
}
],
}
]
}
}
]
有没有更好的方法来处理这个?谢谢!
解决方案
我建议从通用对象映射函数开始。它需要一个对象输入,o
和一个转换来应用,t
-
const identity = x =>
x
const mapObject = (o = {}, t = identity) =>
Object.fromEntries(Object.entries(o).map(([ k, v ]) => [ k, t(v) ]))
然后使用您的浅函数构建递归对象映射函数。它需要一个对象来转换,o
和一个转换函数,t
-
const recMapObject = (o = {}, t = identity) =>
mapObject // <-- using mapObject
( o
, node =>
Array.isArray(node) // <-- recur arrays
? node.map(x => recMapObject(t(x), t))
: Object(node) === node // <-- recur objects
? recMapObject(t(node), t)
: t(node)
)
现在,您可以使用我们的递归对象映射函数构建您的程序独有的对象转换。它需要复杂的嵌套数据 ,graph
要uid
匹配的,以及要应用于匹配节点的转换,t
-
const transformAtUid = (graph = {}, uid = "", t = identity) =>
recMapObject // <-- using recMapObject
( graph
, (node = {}) =>
node.uid === uid // if uid matches,
? { ...node, data: t(node.data) } // then transform node.data,
: node // otherwise no change
)
上述步骤很重要,因为它将特定逻辑node.uid
与node.data
通用对象转换代码的其余部分分开。
现在我们在输入 , 上调用我们的函数data
来变换节点匹配node.uid
等于"bcd"
使用示例变换 -
const result =
transformAtUid
( data // <-- input
, "bcd" // <-- query
, node => ({ ...node, changed: "x" }) // <-- transformation
)
console.log(result)
输出 -
{
"uid": "abc",
"layout": {
"rows": [
{
"columns": [
{
"blocks": [
{
"uid": "bcd",
"data": {
"content": "how are you?",
"changed": "x" // <-- transformed
}
}
]
}
// ...
}
展开下面的代码片段以在您自己的浏览器中验证结果 -
const identity = x =>
x
const mapObject = (o = {}, t = identity) =>
Object.fromEntries(Object.entries(o).map(([ k, v ]) => [ k, t(v) ]))
const recMapObject = (o = {}, t = identity) =>
mapObject // <-- using mapObject
( o
, node =>
Array.isArray(node) // <-- recur arrays
? node.map(x => recMapObject(t(x), t))
: Object(node) === node // <-- recur objects
? recMapObject(t(node), t)
: t(node) // <-- transform
)
const transformAtUid = (graph = {}, uid = "", t = identity) =>
recMapObject
( graph
, (node = {}) =>
node.uid === uid
? { ...node, data: t(node.data) }
: node
)
const data =
{uid:"abc",layout:{rows:[{columns:[{blocks:[{uid:"bcd",data:{content:"how are you?"}}]},{blocks:[{uid:"cde",data:{content:"how are you?"}}]},],},{columns:[{blocks:[{uid:"def",data:{content:"how are you?"}}]}],}]}}
const result =
transformAtUid
( data
, "bcd"
, node => ({ ...node, changed: "x" })
)
console.log(JSON.stringify(result, null, 2))
在您最初的问题中,我看到您有一系列要更改的对象-
// your original
this.sections = [ {...}, {...}, ... ]
要使用recMapObject
和transformAtUid
处理对象数组,我们可以使用Array.prototype.map
-
this.sections = this.sections.map(o => transformAtUid(o, "bcd", ...))
推荐阅读
- azure - 如何在 Azure AD B2C 中创建仅电子邮件页面?
- python-3.x - 在 tkinter 中添加滚动条
- javascript - JS reducer 返回对象
- haskell - 如何加入两个 Haskell IO monad
- angular - 如果表单仍然相同,则阻止用户提交表单
- javascript - 在需要回调(例如 Array.map)的循环中使用 async/await 时,究竟是什么影响了 fs.writeFile 的行为?
- ruby - 使 Ruby 文件在 Windows 10 中可执行
- azure - 在 Azure 逻辑应用程序中,我们如何验证 json 正文中是否存在任何字段
- python - 如何根据字典列表中另一个键的值获取键的最小值?
- jenkins - Jenkins管道:仅当管道成功时才接受合并请求