开发者问题收集

如何更新 Javascript 中深度嵌套的对象?

2022-01-12
18148

我有一个这种类型的 json 对象。

{
    "id": "001",
    "type": "A",
    "value": "aaaaa",
    "data:": {},
    "path": ["001"],
    "children": [
        {
            "id": "002",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "002"],
            "children": []
        },
        {
            "id": "003",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "003"],
            "children": [
                {
                    "id": "00001",
                    "type": "B",
                    "children": []
                }
            ]
        },
        {
            "id": "004",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "004"],
            "children": [
                {
                    "id": "005",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "004", "005"],
                    "children": []
                },{
                    "id": "005",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "004", "005"],
                    "children": [
                        {
                            "id": "00002",
                            "type": "B",
                            "children": []
                        }
                    ]
                }
            ]
        },
        {
            "id": "00003",
            "type": "B",
            "children": []
        }
    ]
}

我需要用这种(下面提到的)类型的对象替换所有 type: "B" 的对象,我可以从以 id 为类型 B 的键的对象中获取这种类型的对象。这种类型的 B 对象可以嵌套在任何位置,作为嵌套子数组的第一个子对象或第五个子对象

{
    "id": "002",
    "type": "A",
    "value": "aaaaa",
    "data:": {},
    "children": []
},

我该怎么做?这可以深度嵌套,并且没有特定的位置应该事先替换对象。所以,我需要遍历整个对象并执行此操作。我该怎么做?

编辑

我稍微更新了问题中的代码。每个对象中都有一个嵌套的 path 属性,类型为 B 的对象除外。因此,当用其他对象替换类型 B 属性时,我也需要在其中添加路径。

例如:id 的路径:“00001”,类型 B 对象应为:[“001”、“003”、“00001”]

编辑: 预期结果

{
    "id": "001",
    "type": "A",
    "value": "aaaaa",
    "data:": {},
    "path": ["001"],
    "children": [
        {
            "id": "002",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "002"],
            "children": []
        },
        {
            "id": "003",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "003"],
            "children": [
                {
                    "id": "002",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "003", "002"],
                    "children": []
                },
            ]
        },
        {
            "id": "004",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "004"],
            "children": [
                {
                    "id": "005",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "004", "005"],
                    "children": []
                },{
                    "id": "005",
                    "type": "A",
                    "value": "aaaaa",
                    "data:": {},
                    "path": ["001", "004", "005"],
                    "children": [
                        {
                            "id": "002",
                            "type": "A",
                            "value": "aaaaa",
                            "data:": {},
                            "path": ["001", "004", "005", "002"],
                            "children": []
                        }
                    ]
                }
            ]
        },
        {
            "id": "002",
            "type": "A",
            "value": "aaaaa",
            "data:": {},
            "path": ["001", "002"],
            "children": []
        }
    ]
}
3个回答
如果您不介意的话,可以使用

lodash

使用 cloneDeepWith 克隆整个树并替换特定值。

const data = {"id":"001","type":"A","value":"aaaaa","data:":{},"children":[{"id":"002","type":"A","value":"aaaaa","data:":{},"children":[]},{"id":"003","type":"A","value":"aaaaa","data:":{},"children":[{"id":"00001","type":"B","children":[]}]},{"id":"004","type":"A","value":"aaaaa","data:":{},"children":[{"id":"005","type":"A","value":"aaaaa","data:":{},"children":[]},{"id":"005","type":"A","value":"aaaaa","data:":{},"children":[{"id":"00002","type":"B","children":[]}]}]},{"id":"00003","type":"B","children":[]}]};

const result = _.cloneDeepWith(data, (value) => {
  const newObj = {"id": "002", "type": "A", "value": "---NEW VALUE FOR 'B' TYPE---", "data:": {} };
  return (value.type === 'B') ? { ...value, ...newObj} : _.noop();
});


console.dir(result, { depth: null } );
.as-console-wrapper{min-height: 100%!important; top: 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js" integrity="sha512-2iwCHjuj+PmdCyvb88rMOch0UcKQxVHi/gsAml1fN3eg82IDaO/cdzzeXX4iF2VzIIes7pODE1/G0ts3QBwslA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

--- 更新 2--- (不使用 lodash)

使用局部变量存储和组合当前路径。

const data = { "id": "001", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001"], "children": [{ "id": "002", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "002"], "children": [] }, { "id": "003", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "003"], "children": [{ "id": "00001", "type": "B", "children": [] }] }, { "id": "004", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "004"], "children": [{ "id": "005", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "004", "005"], "children": [] }, { "id": "005", "type": "A", "value": "aaaaa", "data:": {}, "path": ["001", "004", "005"], "children": [{ "id": "00002", "type": "B", "children": [] }] }] }, { "id": "00003", "type": "B", "children": [] }] }

const deepReplace = (obj, prevPath = []) => {
    if (obj.type === 'A') {
      if  (obj.children.length) {
        obj.children = obj.children.map((childObj) => deepReplace(childObj, obj.path))
      }
      return obj;
    };
    if (obj.type === 'B') {
      const id = '002';
      return { id, type: "A", value: "aaaaa",  path: [...prevPath, id],  data: {}, children: []};
    };
 };

console.dir(deepReplace(data), { depth: null });
.as-console-wrapper{min-height: 100%!important; top: 0}
A1exandr Belan
2022-01-12

这看起来像是一个树遍历问题。这里有一种使用 深度优先搜索 (无需递归)来处理该问题的方法。

正如 这个 答案中所述,应尽可能避免使用递归,因为它需要更多内存,并且比迭代实现更难调试。

根据相关调整更新

const data = {"id":"001","type":"A","value":"aaaaa","data:":{},"path":["001"],"children":[{"id":"002","type":"A","value":"aaaaa","data:":{},"path":["001","002"],"children":[]},{"id":"003","type":"A","value":"aaaaa","data:":{},"path":["001","003"],"children":[{"id":"00001","type":"B","children":[]}]},{"id":"004","type":"A","value":"aaaaa","data:":{},"path":["001","004"],"children":[{"id":"005","type":"A","value":"aaaaa","data:":{},"path":["001","004","005"],"children":[]},{"id":"005","type":"A","value":"aaaaa","data:":{},"path":["001","004","005"],"children":[{"id":"00002","type":"B","children":[]}]}]},{"id":"00003","type":"B","children":[]}]};

const dfs = () => {
  const stack = [[data, null]];
  
  while(stack.length) {
    const [curr, parent] = stack.pop();
    // check for match on type
    if (curr.type === "B") {
      curr.type = "A";
      curr.id = "002";
      curr.value = "aaaaa";
      curr.data = {};
      curr.path = [...parent?.path.slice() ?? [], "002"];
    }
    curr.children.forEach(child => stack.push([child, curr]));
  }
};

dfs();

const output = document.getElementById("output");
output.innerText = JSON.stringify(data, null, 2);
<pre id="output" />
GenericUser
2022-01-12

我在控制台中尝试了一下,它按照你的要求执行(基于提供的 json 数组,将所有“B”设置为“A”类型)。它是一个递归函数,因此在“children”数组中遇到的任何嵌套子项上,它都会对数组中的每个项目再次调用该函数。

function fixObjects (obj) {
  if (obj["type"] === "B") {
    obj["type"] = "A";
    obj["id"] = "002";
    obj["value"] = "aaaaa";
    obj["data"] = {};
  } 
  if (obj["children"].length > 0) {
     obj["children"].forEach (child => fixObjects (child));
  }
}

fixObjects (_yourArray)

Jesser
2022-01-12