Javascript如何循环遍历一个不知道内部有多少嵌套数组的json数组? [英] Javascript how to loop over a json array where we don't know how many nested array there is inside?

查看:47
本文介绍了Javascript如何循环遍历一个不知道内部有多少嵌套数组的json数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新脚本<​​/strong>

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

 函数递归(数据,append_name){用于(数据中的"parent_key"){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"];}别的 {dim_label = data [parent_key] ["label"];}for(data [parent_key] ["children"]中的child_key){//console.log(data[parent_key][children"][child_key])var child_label = data [parent_key] ["children"] [child_key] ["label"] [[english]];if(append_name ==&"; || append_name ==未定义){var child_name = data [parent_key] ["children"] [child_key] ["name"];} 别的 {var child_name = append_name +"/" + data [parent_key] ["children"] [child_key] ["name"];}if("data [parent_key] ["children"] [child_key]中的"children"){递归的(data [parent_key] ["children"] [child_key] ["children"],dim_name)}别的 {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行.

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

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

more_data/favourite_destination/france

more_data/favourite_destination/美国

因此,field_name的末尾将保留其父名称的完整路径.

我尝试了以下JavaScript脚本:

 函数递归(arr,dim_label,dim_name){为(以arr为单位){var title = arr ["title"];if(prop =="children"){为(arr [prop]中的孩子){var dim_name =";var dim_label =";var field_name =";var field_label =";dim_name = arr [prop] [child] [名称"] +"/" + dim_name;type = arr [prop] [child] ["type"];field_name = dim_name + arr [prop] [child] ["name"];dim_label = arr [prop] [child] [名称"] [英语"];field_label =";if(arr [prop] [child]中的"label"){dim_label = arr [prop] [child] ["label"] ["english"];field_label = arr [prop] [child] ["label"] ["english"]}别的 {dim_label = dim_name;}array.push({label:dim_label,name:dim_name});/*如果(类型!=计算"&&类型!=选择一个"){if(arr [prop] [child]中的"children"){递归(arr [prop] [child],dim_label,dim_name);}} */console.log(dim_name,dim_label,field_name,field_label)}}}} 

结果只有3个后退:

"basic_info/",未定义,"basic_info/basic_info",未定义

"more_data/",未定义,"more_data/more_data",未定义

"estimated_income/",未定义,"estimated_income/estimated_income",未定义

这是 jsfiddle .

如何遍历不知道内部有多少个嵌套数组的数组以获取所需的信息?

解决方案

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

  const getPaths =(obj)=>obj .children?obj .children .flatMap(getPaths).map(p => [obj,... p]):[[obj]]const extract =(数据)=>getPaths(data).map((path)=>((字段=路径[路径.length-1],父=路径.length>2?路径[path .length-2]:路径[path .length-1])=>({dim_label:父级.label .english ||父.label,dim_name:path .slice(1,path .length> 2?-1:Infinity).map(n => n.name).join('/'),field_label:字段.label .english ||字段.label,field_name:路径.slice(1).map(n => n .name).join('/')}))())const data = {名称:信息",标题:信息",默认语言:默认",id_string:"...",类型:调查",子级:[{类型:文本",名称:"basic_info",标签:基本信息",子级:[{{type:"text",名称:"name",标签:{english:您叫什么名字"}}},{type:"text",名称:"address",标签:{英语:您的地址是什么?"}}]},{类型:文本",名称:"more_data",标签:更多数据",子级:[{类型:文本",名称:"favourite_food",标签:{english:您最喜欢的食物是什么?"}},{类型:文本",名称:"favourite_destination",标签:{english:您最喜欢的目的地是什么?"},孩子们:[{{type:"text",名称:"france",标签:{english:"France"}}},{type:"text",名称:"usa",标签:{english:"USA"}}}]}]},{类型:数字",名称:"estimated_income",标签:您的预计年收入是多少?"}]}控制台.log(提取(数据))const display =(objs)=>`< table>< thead>< tr> $ {Object.keys(objs [0]).map(k =>`< $ {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;底部:0}表格{border-collapse:崩溃}td,th {border:1px solid #ccc}{background:#eee}  

 < div id ="output"></div>  

getPaths 将这样的嵌套对象转换为路径数组,每个路径都是子树下的对象列表,并以叶节点结尾,此处定义为不包含 children 属性.

我们的主要功能 extract 调用 getPaths ,然后通过找到最后两个节点作为 field parent ( dim ?)对象,从这些对象中以及从整个路径中提取相关数据到新对象中.

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

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

更新

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

使用相同的 getPaths 函数的该版本应同样有效:

  const call =(fn,... args)=>fn(... args)const extract =(数据)=>getPaths(data).map((path)=>调用(((字段=路径[路径.length-1],父=路径.length>2?路径[path .length-2]:路径[path .length-1])=>({dim_label:父级.label .english ||父.label,dim_name:path .slice(1,path .length> 2?-1:Infinity).map(n => n.name).join('/'),field_label:字段.label .english ||字段.label,field_name:路径.slice(1).map(n => n .name).join('/')}))) 

它也可以这样写:

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

UPDATED SCRIPT

Here is an updated script I am working on:

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);
  }
}

The result is only showing 3 records out of 6, which are the first 3 rows only.

Here is a fiddle.

END OF EDIT


ORIGINAL QUESTION

I have JSON file I need to run a script over it to get 4 main fields:

  1. dim_label
  2. dim_name
  3. field_label
  4. field_name

The structure of the JSON array is as follows:

{
    "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?"
        }
    ]
}

And the desired output should look like that:

Note that the last record is having the fields similar because this is no children array inside.

Notice that for the favourite_destination part, there a children array inside another one that's why at the end there is the following fields:

more_data/favourite_destination/france

more_data/favourite_destination/usa

So the field_name at the end will hold the full path of its parents names.

I tried this JavaScript script:

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)
      }
    }
  }
}

And the result was only 3 recrods:

"basic_info/", undefined, "basic_info/basic_info", undefined

"more_data/", undefined, "more_data/more_data", undefined

"estimated_income/", undefined, "estimated_income/estimated_income", undefined

Here is a jsfiddle.

How can I loop over an array that I don't know how many nested arrays there is inside to get the required information?

解决方案

Here's an approach which separates the data traversal from the output formatting:

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 turns a nested object like this into an array of paths, each path being the list of objects down the tree of children to end at a leaf node, defined here as one that has no children property.

Our main function, extract calls getPaths and then maps the resulting paths into objects by finding the last two nodes as our field and parent (dim?) objects, extracting the relevant data from those and from the entire path into new objects.

We demonstrate this both by logging this list of objects and calling a display function which turns the data into an HTML table.

Note that the complexities in the output field definitions speaks to some strong inconsistencies in your data. We need to check something. label .english and default to something .label if it doesn't exist. We need to ignore the outermost container when we list paths. We need to do odd handling for paths that have only one node and that outermost container. If you have any control over that data format, I'd suggest that it would be worthwhile to do some cleanup.

Update

User Thankyou points out that this might be a bit simpler if we used a call function rather than the IIFE in the above.

This version, using the same getPaths function, should work equally well:

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('/')
  })))

It could also be written like this:

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]
  ))

这篇关于Javascript如何循环遍历一个不知道内部有多少嵌套数组的json数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆