Javascript如何循环遍历一个不知道内部有多少嵌套数组的json数组? [英] Javascript how to loop over a json array where we don't know how many nested array there is inside?
问题描述
更新脚本</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.
END OF EDIT
ORIGINAL QUESTION
I have JSON file I need to run a script over it to get 4 main fields:
- dim_label
- dim_name
- field_label
- 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屋!