首页 > 解决方案 > Javascript如何循环一个我们不知道里面有多少嵌套数组的json数组?

问题描述

更新的脚本

这是我正在处理的更新脚本:

function recursive(data, append_name) {
  for (parent_key in data) {
    var dim_type = data[parent_key]["type"];
    var dim_label = "";
    var dim_name = data[parent_key]["name"];
    if (typeof(data[parent_key]["label"])=="object") {
        dim_label = data[parent_key]["label"]["english"];
    }
    else {
        dim_label = data[parent_key]["label"];
    }
    
    for (child_key in data[parent_key]["children"]){
        //console.log(data[parent_key]["children"][child_key])
      var child_label = data[parent_key]["children"][child_key]["label"]["english"];

      if (append_name == "" || append_name == undefined) {
        var child_name = data[parent_key]["children"][child_key]["name"];
      } else {
        var child_name = append_name+"/"+data[parent_key]["children"][child_key]["name"];
      }
      if("children" in data[parent_key]["children"][child_key]) {
        recursive(data[parent_key]["children"][child_key]["children"], dim_name)
      }
      else {
        outputArray.push({"dim_label": dim_label,
                                            "dim_name": dim_name,
                          "child_name": dim_name+"/"+child_name,
                          "child_label": child_label})
      }
      console.log(outputArray, "")
    }
    //console.log(key, dim_label, dim_name, dim_type);
  }
}

结果仅显示 6 条记录中的 3 条记录,它们仅是前 3 行。

这是一个小提琴。

编辑结束


原始问题

我有 JSON 文件,我需要在它上面运行一个脚本来获取 4 个主要字段:

  1. 暗淡标签
  2. 暗淡名称
  3. 字段标签
  4. 字段名

JSON数组的结构如下:

{
    "name": "Info",
    "title": "Info",
    "default_language": "default",
    "id_string": "...",
    "type": "survey",
    "children": [
        {
            "type": "text",
            "name": "basic_info",
            "label": "Basic Info",
            "children": [
                {
                    "type": "text",
                    "name": "name",
                    "label": {
                        "english": "What is your name"
                    }
                },
                {
                    "type": "text",
                    "name": "address",
                    "label": {
                        "english": "What is your address?"
                    }
                }
            ]
        },
        {
            "type": "text",
            "name": "more_data",
            "label": "More Data",
            "children": [
                {
                    "type": "text",
                    "name": "favourite_food",
                    "label": {
                        "english": "What is your favourite food?"
                    }
                },
                {
                    "type": "text",
                    "name": "favourite_destination",
                    "label": {
                        "english": "What is your favourite destination?"
                    },
                    "children": [
                        {
                            "type": "text",
                            "name": "france",
                            "label": {
                                "english": "France"
                            },
                            "type": "text",
                            "name": "usa",
                            "label": {
                                "english": "USA"
                            }
                        }
                    ]
                }
            ]
        },
        {
            "type": "number",
            "name": "estimated_income",
            "label": "What is your annual estimated income?"
        }
    ]
}

所需的输出应如下所示:

在此处输入图像描述

请注意,最后一条记录的字段相似,因为里面没有子数组。

请注意,对于 favourite_destination 部分,在另一个数组中有一个子数组,这就是为什么最后有以下字段:

more_data/favourite_destination/法国

more_data/favourite_destination/美国

所以最后的 field_name 将保存其父名称的完整路径。

我试过这个 JavaScript 脚本:

function recursive(arr, dim_label, dim_name){
    
    for (prop in arr) {
    var title = arr["title"];
    if (prop == "children") {
        for (child in arr[prop]){
        var dim_name = "";
        var dim_label = "";
        var field_name = "";
        var field_label = "";
        dim_name = arr[prop][child]["name"] +"/"+dim_name;
        type = arr[prop][child]["type"];
        field_name = dim_name+arr[prop][child]["name"];
        dim_label =arr[prop][child]["name"]["english"];
        field_label = "";
        if ("label" in arr[prop][child] ) {
         dim_label  = arr[prop][child]["label"]["english"];
         field_label = arr[prop][child]["label"]["english"]
        }
        else {
            dim_label = dim_name;
        }
        array.push({label: dim_label, name: dim_name});
        /* if (type != "calculate" && type != "select one") {
          if ("children" in arr[prop][child]) {
            recursive(arr[prop][child], dim_label, dim_name);
          }
        } */
        console.log(dim_name, dim_label, field_name, field_label)
      }
    }
  }
}

结果只有 3 条记录:

“basic_info/”,未定义,“basic_info/basic_info”,未定义

“更多数据/”,未定义,“更多数据/更多数据”,未定义

“estimated_income/”,未定义,“estimated_income/estimated_income”,未定义

这是一个jsfiddle

如何遍历一个我不知道里面有多少嵌套数组的数组来获取所需的信息?

标签: javascriptrecursion

解决方案


这是一种将数据遍历与输出格式分开的方法:

const getPaths = (obj) =>
  obj .children
    ? obj .children .flatMap (getPaths) .map (p => [obj, ...p])
    : [[obj]]

const extract = (data) =>
  getPaths (data) .map ((path) => ((
    field = path [path .length - 1], 
    parent = path .length > 2 ? path [path .length - 2] : path [path .length - 1]
  ) => ({
    dim_label: parent .label .english || parent .label,
    dim_name: path .slice (1, path .length > 2 ? -1 : Infinity) .map (n => n.name) .join('/'),
    field_label: field .label .english || field .label,
    field_name: path .slice(1) .map (n => n .name) .join('/')
  }))())

const data = {name: "Info", title: "Info", default_language: "default", id_string: "...", type: "survey", children: [{type: "text", name: "basic_info", label: "Basic Info", children: [{type: "text", name: "name", label: {english: "What is your name"}}, {type: "text", name: "address", label: {english: "What is your address?"}}]}, {type: "text", name: "more_data", label: "More Data", children: [{type: "text", name: "favourite_food", label: {english: "What is your favourite food?"}}, {type: "text", name: "favourite_destination", label: {english: "What is your favourite destination?"}, children: [{type: "text", name: "france", label: {english: "France"}}, {type: "text", name: "usa", label: {english: "USA"}}]}]}, {type: "number", name: "estimated_income", label: "What is your annual estimated income?"}]}

console .log (extract (data))

const display = (objs) => `<table><thead><tr>${Object.keys(objs[0]).map(k => `<th>${k}</th>`).join('')}</tr></thead><tbody>${objs.map(o => `<tr>${Object.values(o).map(v => `<td>${v}</td>`).join('')}</tr>`).join('')}</tbody></table>`

document.getElementById('output').innerHTML = display (extract (data))
.as-console-wrapper {max-height: 50% !important; bottom: 0}
table {border-collapse: collapse}
td, th { border: 1px solid #ccc}
th {background: #eee}
<div id="output"></div>

getPaths将这样的嵌套对象转换为路径数组,每个路径都是沿子树向下的对象列表,以在叶节点处结束,此处定义为没有children属性的对象。

我们的主要函数,extract调用getPaths并将结果路径映射到对象中,方法是找到最后两个节点作为我们的fieldparent( dim?) 对象,从这些节点和整个路径中提取相关数据到新对象中。

我们通过记录此对象列表并调用display将数据转换为 HTML 表的函数来演示这一点。

请注意,输出字段定义中的复杂性说明您的数据中存在一些严重的不一致。如果它不存在,我们需要检查something. label .english并默认为。something .label当我们列出路径时,我们需要忽略最外层的容器。我们需要对只有一个节点和最外层容器的路径进行奇怪的处理。如果您对该数据格式有任何控制权,我建议进行一些清理是值得的。

更新

用户Thankyou 指出,如果我们使用call函数而不是上面的IIFE,这可能会更简单一些。

这个版本,使用相同的getPaths功能,应该同样好用:

const call = (fn, ...args) => 
  fn (...args)

const extract = (data) =>
  getPaths (data) .map ((path) => call ((
    field = path [path .length - 1], 
    parent = path .length > 2 ? path [path .length - 2] : path [path .length - 1]
  ) => ({
    dim_label: parent .label .english || parent .label,
    dim_name: path .slice (1, path .length > 2 ? -1 : Infinity) .map (n => n.name) .join('/'),
    field_label: field .label .english || field .label,
    field_name: path .slice(1) .map (n => n .name) .join('/')
  })))

也可以这样写:

const extract = (data) =>
  getPaths (data) .map (
    (path) => call 
      ((field, parent) => ({
        dim_label: parent .label .english || parent .label,
        dim_name: path .slice (1, path .length > 2 ? -1 : Infinity) .map (n => n.name) .join('/'),
        field_label: field .label .english || field .label,
        field_name: path .slice(1) .map (n => n .name) .join('/')
      }),
      path [path .length - 1],
      path .length > 2 ? path [path .length - 2] : path [path .length - 1]
  ))

推荐阅读