首页 > 解决方案 > 如何使用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?"
                                }
                            }
                        ]
                    }
                ],
            }
        ]
    }
  }
]

有没有更好的方法来处理这个?谢谢!

标签: javascriptrecursion

解决方案


我建议从通用对象映射函数开始。它需要一个对象输入,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)
    )

现在,您可以使用我们的递归对象映射函数构建您的程序独有的对象转换。它需要复杂的嵌套数据 ,graphuid匹配的,以及要应用于匹配节点的转换,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.uidnode.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 = [ {...}, {...}, ... ]

要使用recMapObjecttransformAtUid处理对象数组,我们可以使用Array.prototype.map-

this.sections = this.sections.map(o => transformAtUid(o, "bcd", ...))

推荐阅读