【问题标题】:Convert array in to specific format recursively递归地将数组转换为特定格式
【发布时间】:2020-10-01 06:35:33
【问题描述】:

我有一个包含层次结构数据的数组,我想将其转换为层次结构,但格式不同。我尝试使用递归方法,但它没有提供正确的数据

下面是我尝试实现的代码

static convertConfigJson(data, finalResults = []) {

        const configData = JSON.parse(JSON.stringify(data));

        return configData.map((item) => {
            const { name, type, value, path } = item;
            let children = {}
            if (type === 'Object') {
              children = this.convertConfigJson(value, finalResults);
            } else {

            finalResults.push({
                id: name,
                name: name,
                children: children
            })
            }




            console.log('>>>>>>>>>', finalResults)

            return finalResults;
          });   
    }

以下是我从 API 获取的 JSON 数组

[
        {
            "name": "info",
            "type": "Object",
            "value": [
                {
                    "name": "app",
                    "type": "Object",
                    "value": [
                        {
                            "path": "info.app",
                            "name": "encoding",
                            "type": "TEXT",
                            "value": "@project.build.sourceEncoding@"
                        },
                        {
                            "name": "java",
                            "type": "Object",
                            "value": [
                                {
                                    "path": "info.app.java",
                                    "name": "source",
                                    "type": "TEXT",
                                    "value": "@java.version@"
                                },
                                {
                                    "path": "info.app.java",
                                    "name": "target",
                                    "type": "TEXT",
                                    "value": "java.version@"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]

我想将上面的数组转换为以下格式,即将值数组更改为子数组

[
    {
        "name": "info",
        "type": "Object",
        "children": [
            {
                "name": "app",
                "type": "Object",
                "children": [
                    {
                        "path": "info.app",
                        "name": "encoding",
                        "type": "TEXT",
                        "value": "@project.build.sourceEncoding@"
                    },
                    {
                        "name": "java",
                        "type": "Object",
                        "children": [
                            {
                                "path": "info.app.java",
                                "name": "source",
                                "type": "TEXT",
                                "value": "@java.version@"
                            },
                            {
                                "path": "info.app.java",
                                "name": "target",
                                "type": "TEXT",
                                "value": "java.version@"
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

【问题讨论】:

    标签: javascript arrays recursion multidimensional-array


    【解决方案1】:

    此技术类似于 user120242 的答案,但它不会改变您的输入结构,而是创建一个新结构:

    const transform = ({value, ...rest}) => 
      Array .isArray (value)
        ? {...rest, children: value .map (transform)}
        : {...rest, value}
    
    const data = [{name: "info", type: "Object", value: [{name: "app", type: "Object", value: [{path: "info.app", name: "encoding", type: "TEXT", value: "@project.build.sourceEncoding@"}, {name: "java", type: "Object", value: [{path: "info.app.java", name: "source", type: "TEXT", value: "@java.version@"}, {path: "info.app.java", name: "target", type: "TEXT", value: "java.version@"}]}]}]}]
    
    console .log (data .map (transform))

    更新

    借用from sivako的技术,我们可以很好地简化这个:

    const transform = ({value, ...rest}) => ({
      ... rest, 
      ... (Array .isArray (value) ? {children: value .map (transform)} : {value})
    })
    
    const data = [{name: "info", type: "Object", value: [{name: "app", type: "Object", value: [{path: "info.app", name: "encoding", type: "TEXT", value: "@project.build.sourceEncoding@"}, {name: "java", type: "Object", value: [{path: "info.app.java", name: "source", type: "TEXT", value: "@java.version@"}, {path: "info.app.java", name: "target", type: "TEXT", value: "java.version@"}]}]}]}]
    
    console .log (data .map (transform))

    【讨论】:

      【解决方案2】:

      对 children 属性中的所有元素进行简单的递归遍历。将 .value 设置为 .children 并删除 .value

      walk = node => {
        if(!node.value || typeof node.value!=='object') return
        node.children = node.value
        delete node.value
        node.children.forEach(walk)
      }
      
      data.forEach(walk)
      
      console.log(data)
      <script>
      data=[
              {
                  "name": "info",
                  "type": "Object",
                  "value": [
                      {
                          "name": "app",
                          "type": "Object",
                          "value": [
                              {
                                  "path": "info.app",
                                  "name": "encoding",
                                  "type": "TEXT",
                                  "value": "@project.build.sourceEncoding@"
                              },
                              {
                                  "name": "java",
                                  "type": "Object",
                                  "value": [
                                      {
                                          "path": "info.app.java",
                                          "name": "source",
                                          "type": "TEXT",
                                          "value": "@java.version@"
                                      },
                                      {
                                          "path": "info.app.java",
                                          "name": "target",
                                          "type": "TEXT",
                                          "value": "java.version@"
                                      }
                                  ]
                              }
                          ]
                      }
                  ]
              }
          ]
      </script>

      【讨论】:

      • @Disfigure,10 000 个元素会得到什么?
      • @Disfigure:你认为多少性能提升值得额外的代码复杂性?这是一个不错的简单解决方案。
      • @user120242 我同意,您说“几乎可以忽略不计”,所以存在差异:)
      • 对,但正如您在 V8 文章中看到的示例,forEach 具有原生级别的优化。在某些情况下,根据您的操作,它实际上可以更快
      【解决方案3】:

      编写transform 方法,当对象的“值”类型为数组时,递归调用transform

      const transform = ({ value, ...rest }) => {
        const item = { ...rest };
        Array.isArray(value)
          ? (item["children"] = value.map(transform))
          : (item["value"] = value);
        return item;
      };
      
      // Update: Based on @user120242 suggestion, creating another object is not required and using `Object.assign` will simplify.
      
      const transform2 = ({ value, ...rest }) =>
        Object.assign(
          rest,
          Array.isArray(value) ? { children: value.map(transform2) } : { value }
        );
      
      const items = [
        {
          name: "info",
          type: "Object",
          value: [
            {
              name: "app",
              type: "Object",
              value: [
                {
                  path: "info.app",
                  name: "encoding",
                  type: "TEXT",
                  value: "@project.build.sourceEncoding@",
                },
                {
                  name: "java",
                  type: "Object",
                  value: [
                    {
                      path: "info.app.java",
                      name: "source",
                      type: "TEXT",
                      value: "@java.version@",
                    },
                    {
                      path: "info.app.java",
                      name: "target",
                      type: "TEXT",
                      value: "java.version@",
                    },
                  ],
                },
              ],
            },
          ],
        },
      ];
      
      const output = items.map(transform);
      console.log(output);
      
      console.log(items.map(transform2));

      【讨论】:

      • @ScottSauyet,是的,在发布之前没有看到您的答案。甚至想出了相同的方法名称transform :)
      • @user120242,同意,不需要创建新的克隆。应该更新rest 本身。刚刚使用Object.assign 以替代方式更新了答案。谢谢。
      • 那个更新的版本非常好!我想我会添加一个更新来匹配。
      【解决方案4】:

      最简单的方法是在字符串化之后将所有"value": [ 替换为"children": [

      function convertConfigJson(data) {
        const configData = JSON.parse(JSON.stringify(data).replace(/"value":\[/g, '"children":['));
        return configData
      }
      

      您也可以一一遍历嵌套对象的所有键,并将值键替换为值中的数组给孩子

      【讨论】:

      • 这个解决方案有一个缺点,如果它们在这个对象的任何键的值中,它也会替换单词“value”。
      • @matvs,不,因为包含方括号...对于 ts 结构它是可以的,但作为通用解决方案它不应该这样使用,我只是建议在代码意义上最简单的方法.但是对所有元素进行循环的解决方案当然更合理
      • 谢谢@DmitryReutov,我需要一种递归方法调用的方法,我需要更新其他属性或对象结构
      猜你喜欢
      • 1970-01-01
      • 2020-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多