遍历嵌套的 JavaScript 对象 [重复]

Posted

技术标签:

【中文标题】遍历嵌套的 JavaScript 对象 [重复]【英文标题】:Iterate through Nested JavaScript Objects [duplicate] 【发布时间】:2011-12-26 11:12:08 【问题描述】:

我正在尝试遍历嵌套对象以检索由字符串标识的特定对象。在下面的示例对象中,标识符字符串是“label”属性。我不知道如何遍历树以返回适当的对象。任何帮助或建议将不胜感激。

var cars = 
  label: 'Autos',
  subs: [
    
      label: 'SUVs',
      subs: []
    ,
    
      label: 'Trucks',
      subs: [
        
          label: '2 Wheel Drive',
          subs: []
        ,
        
          label: '4 Wheel Drive',
          subs: [
            
              label: 'Ford',
              subs: []
            ,
            
              label: 'Chevrolet',
              subs: []
            
          ]
        
      ]
    ,
    
      label: 'Sedan',
      subs: []
    
  ]

【问题讨论】:

您想在对象的所有级别中搜索任意标签? (啊哈,traverse,这就是我要找的词。) 【参考方案1】:

如果您想为每个 键和值深度迭代到一个复杂(嵌套)对象中,您可以使用Object.keys() 来实现,递归

const iterate = (obj) => 
    Object.keys(obj).forEach(key => 

    console.log(`key: $key, value: $obj[key]`)

    if (typeof obj[key] === 'object') 
            iterate(obj[key])
        
    )

REPL example.

【讨论】:

如果对象在嵌套项方面是“大”的,您将收到堆栈溢出错误。最好使用迭代技术。 经过大量搜索,我发现这是最清晰、最简洁的答案。我同意“大”物体这可能并不理想。值得一提的是,总共有大约 70 个嵌套键/值对(我不认为“大”,但每个人都会对此有不同的解释)。【参考方案2】:

您可以创建这样的递归函数来深度优先遍历cars 对象。

var findObjectByLabel = function(obj, label) 
    if(obj.label === label)  return obj; 
    for(var i in obj) 
        if(obj.hasOwnProperty(i))
            var foundLabel = findObjectByLabel(obj[i], label);
            if(foundLabel)  return foundLabel; 
        
    
    return null;
;

可以这样称呼

findObjectByLabel(car, "Chevrolet");

【讨论】:

递归对于非常深的对象是不利的。你会得到堆栈溢出。 @ArjunU。拥有数百层深的对象是相当少见的。 我只是想知道。 obj.hasOwnProperty(i) 有什么用。因为如果它在 for (var i in obj) 循环内,这不是意味着对象 100% 具有该属性吗? @Vishal 如果没有obj.hasOwnProperty(i),将包含自定义原型属性。例如,如果您定义Array.prototype.first = function(a) return a[0] ,那么for(var i in []) 将包含原型属性first 我收到此错误RangeError: Maximum call stack size exceeded【参考方案3】:

????-?????????????????,??,?????????????? h2>
function forEachNested(O, f, cur)
    O = [ O ]; // ensure that f is called with the top-level object
    while (O.length) // keep on processing the top item on the stack
        if(
           !f( cur = O.pop() ) && // do not spider down if `f` returns true
           cur instanceof Object && // ensure cur is an object, but not null 
           [Object, Array].includes(cur.constructor) //limit search to [] and 
        ) O.push.apply(O, Object.values(cur)); //search all values deeper inside

要使用上述函数,请将数组作为第一个参数传递,将回调函数作为第二个参数传递。回调函数在调用时将接收 1 个参数:正在迭代的当前项。

(function()"use strict";

var cars = "label":"Autos","subs":["label":"SUVs","subs":[],"label":"Trucks","subs":["label":"2 Wheel Drive","subs":[],"label":"4 Wheel Drive","subs":["label":"Ford","subs":[],"label":"Chevrolet","subs":[]]],"label":"Sedan","subs":[]];

var lookForCar = prompt("enter the name of the car you are looking for (e.g. 'Ford')") || 'Ford';
lookForCar = lookForCar.replace(/[^ \w]/g, ""); // incaseif the user put quotes or something around their input
lookForCar = lookForCar.toLowerCase();

var foundObject = null;
forEachNested(cars, function(currentValue)
    if(currentValue.constructor === Object &&
      currentValue.label.toLowerCase() === lookForCar) 
        foundObject = currentValue;
    
);
if (foundObject !== null) 
    console.log("Found the object: " + JSON.stringify(foundObject, null, "\t"));
 else 
    console.log('Nothing found with a label of "' + lookForCar + '" :(');


function forEachNested(O, f, cur)
    O = [ O ]; // ensure that f is called with the top-level object
    while (O.length) // keep on processing the top item on the stack
        if(
           !f( cur = O.pop() ) && // do not spider down if `f` returns true
           cur instanceof Object && // ensure cur is an object, but not null 
           [Object, Array].includes(cur.constructor) //limit search to [] and 
        ) O.push.apply(O, Object.values(cur)); //search all values deeper inside


)();

“作弊”的替代方法可能是使用JSON.stringify 进行迭代。但是,JSON.stringify 将调用它传递的每个对象的 toString 方法,如果您对 toString 有自己的特殊用途,这可能会产生不良结果。

function forEachNested(O, f, v)
    typeof O === "function" ? O(v) : JSON.stringify(O,forEachNested.bind(0,f));
    return v; // so that JSON.stringify keeps on recursing

(function()"use strict";

var cars = "label":"Autos","subs":["label":"SUVs","subs":[],"label":"Trucks","subs":["label":"2 Wheel Drive","subs":[],"label":"4 Wheel Drive","subs":["label":"Ford","subs":[],"label":"Chevrolet","subs":[]]],"label":"Sedan","subs":[]];

var lookForCar = prompt("enter the name of the car you are looking for (e.g. 'Ford')") || 'Ford';
lookForCar = lookForCar.replace(/[^ \w]/g, ""); // incaseif the user put quotes or something around their input
lookForCar = lookForCar.toLowerCase();

var foundObject = null;
forEachNested(cars, function(currentValue)
    if(currentValue.constructor === Object &&
      currentValue.label.toLowerCase() === lookForCar) 
        foundObject = currentValue;
    
);
if (foundObject !== null)
    console.log("Found the object: " + JSON.stringify(foundObject, null, "\t"));
else
    console.log('Nothing found with a label of "' + lookForCar + '" :(');

function forEachNested(O, f, v)
    typeof O === "function" ? O(v) : JSON.stringify(O,forEachNested.bind(0,f));
    return v; // so that JSON.stringify keeps on recursing

)();

然而,虽然上述方法可能对演示有用,但 Internet Explorer 不支持Object.values,并且代码中有许多性能非常糟糕的地方:

    代码更改输入参数(参数)的值 [第 2 行和第 5 行], 代码在每个项目上调用 Array.prototype.pushArray.prototype.pop [第 5 行和第 8 行], 该代码仅对不适用于窗口外对象的构造函数进行指针比较 [第 7 行], 代码复制了从 Object.values [第 8 行] 返回的数组, 代码未本地化 window.Objectwindow.Object.values [第 9 行], 代码不必要地调用数组上的 Object.values [第 8 行]。

下面是一个比任何其他解决方案都要快得多的版本。下面的解决方案修复了上面列出的所有性能问题。但是,它以一种非常不同的方式进行迭代:它首先迭代所有数组,然后迭代所有对象。它继续迭代其当前类型,直到完全耗尽,包括正在迭代的当前风味的当前列表中的迭代子值。然后,该函数迭代所有其他类型。通过在切换之前迭代直到耗尽,迭代循环变得比其他情况更热并且迭代得更快。这个方法还有一个额外的好处:在每个值上调用的回调传递了第二个参数。第二个参数是从Object.values返回的数组,在父哈希对象上调用,或者是父数组本身。

var getValues = Object.values; // localize
var type_toString = Object.prototype.toString;
function forEachNested(objectIn, functionOnEach)
    "use strict";
    functionOnEach( objectIn );
    
    // for iterating arbitrary objects:
    var allLists = [  ];
    if (type_toString.call( objectIn ) === '[object Object]')
        allLists.push( getValues(objectIn) );
    var allListsSize = allLists.length|0; // the length of allLists
    var indexLists = 0;
    
    // for iterating arrays:
    var allArray = [  ];
    if (type_toString.call( objectIn ) === '[object Array]')
        allArray.push( objectIn );
    var allArraySize = allArray.length|0; // the length of allArray
    var indexArray = 0;
    
    do 
        // keep cycling back and forth between objects and arrays
        
        for ( ; indexArray < allArraySize; indexArray=indexArray+1|0) 
            var currentArray = allArray[indexArray];
            var currentLength = currentArray.length;
            for (var curI=0; curI < currentLength; curI=curI+1|0) 
                var arrayItemInner = currentArray[curI];
                if (arrayItemInner === undefined &&
                    !currentArray.hasOwnProperty(arrayItemInner)) 
                    continue; // the value at this position doesn't exist!
                
                functionOnEach(arrayItemInner, currentArray);
                if (typeof arrayItemInner === 'object') 
                    var typeTag = type_toString.call( arrayItemInner );
                    if (typeTag === '[object Object]') 
                        // Array.prototype.push returns the new length
                        allListsSize=allLists.push( getValues(arrayItemInner) );
                     else if (typeTag === '[object Array]') 
                        allArraySize=allArray.push( arrayItemInner );
                    
                
            
            allArray[indexArray] = null; // free up memory to reduce overhead
        
         
        for ( ; indexLists < allListsSize; indexLists=indexLists+1|0) 
            var currentList = allLists[indexLists];
            var currentLength = currentList.length;
            for (var curI=0; curI < currentLength; curI=curI+1|0) 
                var listItemInner = currentList[curI];
                functionOnEach(listItemInner, currentList);
                if (typeof listItemInner === 'object') 
                    var typeTag = type_toString.call( listItemInner );
                    if (typeTag === '[object Object]') 
                        // Array.prototype.push returns the new length
                        allListsSize=allLists.push( getValues(listItemInner) );
                     else if (typeTag === '[object Array]') 
                        allArraySize=allArray.push( listItemInner );
                    
                
            
            allLists[indexLists] = null; // free up memory to reduce overhead
        
     while (indexLists < allListsSize || indexArray < allArraySize);

(function()"use strict";

var cars = "label":"Autos","subs":["label":"SUVs","subs":[],"label":"Trucks","subs":["label":"2 Wheel Drive","subs":[],"label":"4 Wheel Drive","subs":["label":"Ford","subs":[],"label":"Chevrolet","subs":[]]],"label":"Sedan","subs":[]];

var lookForCar = prompt("enter the name of the car you are looking for (e.g. 'Ford')") || 'Ford';
lookForCar = lookForCar.replace(/[^ \w]/g, ""); // incaseif the user put quotes or something around their input
lookForCar = lookForCar.toLowerCase();





var getValues = Object.values; // localize
var type_toString = Object.prototype.toString;
function forEachNested(objectIn, functionOnEach)
    functionOnEach( objectIn );
    
    // for iterating arbitrary objects:
    var allLists = [  ];
    if (type_toString.call( objectIn ) === '[object Object]')
        allLists.push( getValues(objectIn) );
    var allListsSize = allLists.length|0; // the length of allLists
    var indexLists = 0;
    
    // for iterating arrays:
    var allArray = [  ];
    if (type_toString.call( objectIn ) === '[object Array]')
        allArray.push( objectIn );
    var allArraySize = allArray.length|0; // the length of allArray
    var indexArray = 0;
    
    do 
        // keep cycling back and forth between objects and arrays
        
        for ( ; indexArray < allArraySize; indexArray=indexArray+1|0) 
            var currentArray = allArray[indexArray];
            var currentLength = currentArray.length;
            for (var curI=0; curI < currentLength; curI=curI+1|0) 
                var arrayItemInner = currentArray[curI];
                if (arrayItemInner === undefined &&
                    !currentArray.hasOwnProperty(arrayItemInner)) 
                    continue; // the value at this position doesn't exist!
                
                functionOnEach(arrayItemInner, currentArray);
                if (typeof arrayItemInner === 'object') 
                    var typeTag = type_toString.call( arrayItemInner );
                    if (typeTag === '[object Object]') 
                        // Array.prototype.push returns the new length
                        allListsSize=allLists.push( getValues(arrayItemInner) );
                     else if (typeTag === '[object Array]') 
                        allArraySize=allArray.push( arrayItemInner );
                    
                
            
            allArray[indexArray] = null; // free up memory to reduce overhead
        
         
        for ( ; indexLists < allListsSize; indexLists=indexLists+1|0) 
            var currentList = allLists[indexLists];
            var currentLength = currentList.length;
            for (var curI=0; curI < currentLength; curI=curI+1|0) 
                var listItemInner = currentList[curI];
                functionOnEach(listItemInner, currentList);
                if (typeof listItemInner === 'object') 
                    var typeTag = type_toString.call( listItemInner );
                    if (typeTag === '[object Object]') 
                        // Array.prototype.push returns the new length
                        allListsSize=allLists.push( getValues(listItemInner) );
                     else if (typeTag === '[object Array]') 
                        allArraySize=allArray.push( listItemInner );
                    
                
            
            allLists[indexLists] = null; // free up memory to reduce overhead
        
     while (indexLists < allListsSize || indexArray < allArraySize);





var foundObject = null;
forEachNested(cars, function(currentValue)
    if(currentValue.constructor === Object &&
      currentValue.label.toLowerCase() === lookForCar) 
        foundObject = currentValue;
    
);
if (foundObject !== null) 
    console.log("Found the object: " + JSON.stringify(foundObject, null, "\t"));
 else 
    console.log('Nothing found with a label of "' + lookForCar + '" :(');


)();

如果您在循环引用方面遇到问题(例如,对象 A 的值是对象 A 本身,例如对象 A 包含自身),或者您只需要键,则可以使用以下较慢的解决方案。

function forEachNested(O, f)
    O = Object.entries(O);
    var cur;
    function applyToEach(x)return cur[1][x[0]] === x[1] 
    while (O.length)
        cur = O.pop();
        f(cur[0], cur[1]);
        if (typeof cur[1] === 'object' && cur[1].constructor === Object && 
          !O.some(applyToEach))
            O.push.apply(O, Object.entries(cur[1]));
    

因为这些方法不使用任何类型的递归,所以这些函数非常适合您可能有数千个深度级别的区域。 The stack limit varies greatly from browser to browser,所以在 javascript 中递归到未知深度并不是很明智。

【讨论】:

如何运行函数?你通过什么? @Moekanan forEachNested(name:"VK",function(key,value)console.log(key,value)); @Moekanan 我添加了帮助 sn-p 来演示如何使用它。【参考方案4】:

以下代码假定没有循环引用,并假定subs 始终是一个数组(并且在叶节点中不为空):

function find(haystack, needle) 
  if (haystack.label === needle) return haystack;
  for (var i = 0; i < haystack.subs.length; i ++) 
    var result = find(haystack.subs[i], needle);
    if (result) return result;
  
  return null;

【讨论】:

【参考方案5】:

您可以遍历列表中的每个对象并获取您想要的值。只需将对象作为函数调用中的第一个参数和您想要作为第二个参数的对象属性传递。用你的对象改变对象。

const treeData = [
        "jssType": "fieldset",
        "jssSelectLabel": "Fieldset (with legend)",
        "jssSelectGroup": "jssItem",
        "jsName": "fieldset-715",
        "jssLabel": "Legend",
        "jssIcon": "typcn typcn-folder",
        "expanded": true,
        "children": [
                "jssType": "list-ol",
                "jssSelectLabel": "List - ol",
                "jssSelectGroup": "jssItem",
                "jsName": "list-ol-147",
                "jssLabel": "",
                "jssIcon": "dashicons dashicons-editor-ol",
                "noChildren": false,
                "expanded": true,
                "children": [
                        "jssType": "list-li",
                        "jssSelectLabel": "List Item - li",
                        "jssSelectGroup": "jssItem",
                        "jsName": "list-li-752",
                        "jssLabel": "",
                        "jssIcon": "dashicons dashicons-editor-ul",
                        "noChildren": false,
                        "expanded": true,
                        "children": [
                            "jssType": "text",
                            "jssSelectLabel": "Text (short text)",
                            "jssSelectGroup": "jsTag",
                            "jsName": "text-422",
                            "jssLabel": "Your Name (required)",
                            "jsRequired": true,
                            "jsTagOptions": [
                                    "jsOption": "",
                                    "optionLabel": "Default value",
                                    "optionType": "input"
                                ,
                                
                                    "jsOption": "placeholder",
                                    "isChecked": false,
                                    "optionLabel": "Use this text as the placeholder of the field",
                                    "optionType": "checkbox"
                                ,
                                
                                    "jsOption": "akismet_author_email",
                                    "isChecked": false,
                                    "optionLabel": "Akismet - this field requires author's email address",
                                    "optionType": "checkbox"
                                
                            ],
                            "jsValues": "",
                            "jsPlaceholder": false,
                            "jsAkismetAuthor": false,
                            "jsIdAttribute": "",
                            "jsClassAttribute": "",
                            "jssIcon": "typcn typcn-sort-alphabetically",
                            "noChildren": true
                        ]
                    ,
                    
                        "jssType": "list-li",
                        "jssSelectLabel": "List Item - li",
                        "jssSelectGroup": "jssItem",
                        "jsName": "list-li-538",
                        "jssLabel": "",
                        "jssIcon": "dashicons dashicons-editor-ul",
                        "noChildren": false,
                        "expanded": true,
                        "children": [
                            "jssType": "email",
                            "jssSelectLabel": "Email",
                            "jssSelectGroup": "jsTag",
                            "jsName": "email-842",
                            "jssLabel": "Email Address (required)",
                            "jsRequired": true,
                            "jsTagOptions": [
                                    "jsOption": "",
                                    "optionLabel": "Default value",
                                    "optionType": "input"
                                ,
                                
                                    "jsOption": "placeholder",
                                    "isChecked": false,
                                    "optionLabel": "Use this text as the placeholder of the field",
                                    "optionType": "checkbox"
                                ,
                                
                                    "jsOption": "akismet_author_email",
                                    "isChecked": false,
                                    "optionLabel": "Akismet - this field requires author's email address",
                                    "optionType": "checkbox"
                                
                            ],
                            "jsValues": "",
                            "jsPlaceholder": false,
                            "jsAkismetAuthorEmail": false,
                            "jsIdAttribute": "",
                            "jsClassAttribute": "",
                            "jssIcon": "typcn typcn-mail",
                            "noChildren": true
                        ]
                    ,
                    
                        "jssType": "list-li",
                        "jssSelectLabel": "List Item - li",
                        "jssSelectGroup": "jssItem",
                        "jsName": "list-li-855",
                        "jssLabel": "",
                        "jssIcon": "dashicons dashicons-editor-ul",
                        "noChildren": false,
                        "expanded": true,
                        "children": [
                            "jssType": "textarea",
                            "jssSelectLabel": "Textarea (long text)",
                            "jssSelectGroup": "jsTag",
                            "jsName": "textarea-217",
                            "jssLabel": "Your Message",
                            "jsRequired": false,
                            "jsTagOptions": [
                                    "jsOption": "",
                                    "optionLabel": "Default value",
                                    "optionType": "input"
                                ,
                                
                                    "jsOption": "placeholder",
                                    "isChecked": false,
                                    "optionLabel": "Use this text as the placeholder of the field",
                                    "optionType": "checkbox"
                                
                            ],
                            "jsValues": "",
                            "jsPlaceholder": false,
                            "jsIdAttribute": "",
                            "jsClassAttribute": "",
                            "jssIcon": "typcn typcn-document-text",
                            "noChildren": true
                        ]
                    
                ]
            ,
            
                "jssType": "paragraph",
                "jssSelectLabel": "Paragraph - p",
                "jssSelectGroup": "jssItem",
                "jsName": "paragraph-993",
                "jssContent": "* Required",
                "jssIcon": "dashicons dashicons-editor-paragraph",
                "noChildren": true
            
        ]
        
    ,
    
        "jssType": "submit",
        "jssSelectLabel": "Submit",
        "jssSelectGroup": "jsTag",
        "jsName": "submit-704",
        "jssLabel": "Send",
        "jsValues": "",
        "jsRequired": false,
        "jsIdAttribute": "",
        "jsClassAttribute": "",
        "jssIcon": "typcn typcn-mail",
        "noChildren": true
    ,
    
];




 function findObjectByLabel(obj, label) 
       for(var elements in obj)
           if (elements === label)
                console.log(obj[elements]);
           
            if(typeof obj[elements] === 'object')
            findObjectByLabel(obj[elements], 'jssType');
           
          
       
;

 findObjectByLabel(treeData, 'jssType');

【讨论】:

【参考方案6】:

这是一个简洁的广度优先迭代解决方案,我更喜欢递归:

const findCar = function(car) 
    const carSearch = [cars];

      while(carSearch.length) 
          let item = carSearch.shift();
          if (item.label === car) return true;
          carSearch.push(...item.subs);
      

      return false;

【讨论】:

【参考方案7】:

这是使用object-scan的解决方案

// const objectScan = require('object-scan');

const cars =  label: 'Autos', subs: [  label: 'SUVs', subs: [] ,  label: 'Trucks', subs: [  label: '2 Wheel Drive', subs: [] ,  label: '4 Wheel Drive', subs: [  label: 'Ford', subs: [] ,  label: 'Chevrolet', subs: []  ]  ] ,  label: 'Sedan', subs: []  ] ;

const find = (haystack, label) => objectScan(['**.label'], 
  filterFn: ( value ) => value === label,
  rtn: 'parent',
  abort: true
)(haystack);

console.log(find(cars, 'Sedan'));
// =>  label: 'Sedan', subs: [] 
console.log(find(cars, 'SUVs'));
// =>  label: 'SUVs', subs: [] 
.as-console-wrapper max-height: 100% !important; top: 0
&lt;script src="https://bundle.run/object-scan@13.8.0"&gt;&lt;/script&gt;

免责声明:我是object-scan的作者

【讨论】:

【参考方案8】:

为了进一步提高树操作的性能,最好将树视图转换为行集合视图,例如 [obj1, obj2, obj3]。您可以存储父子对象关系以轻松导航到父/子范围。

在集合内搜索元素比在树内查找元素效率更高(递归、添加动态函数创建、闭包)。

【讨论】:

【参考方案9】:

修改自 Peter Olson 的回答:https://***.com/a/8085118

    可以避免字符串值!obj || (typeof obj === 'string' 可以自定义您的密钥

var findObjectByKeyVal= function (obj, key, val) 
  if (!obj || (typeof obj === 'string')) 
    return null
  
  if (obj[key] === val) 
    return obj
  

  for (var i in obj) 
    if (obj.hasOwnProperty(i)) 
      var found = findObjectByKeyVal(obj[i], key, val)
      if (found) 
        return found
      
    
  
  return null

【讨论】:

【参考方案10】:

下面的 sn-p 将遍历嵌套对象。对象内的对象。随意更改它以满足您的要求。就像如果你想添加数组支持 make if-else 并创建一个遍历数组的函数......

var p = 
    "p1": "value1",
    "p2": "value2",
    "p3": "value3",
    "p4": 
        "p4": 'value 4'
    
;



/**
*   Printing a nested javascript object
*/
function jsonPrinter(obj) 

    for (let key in obj) 
        // checking if it's nested
        if (obj.hasOwnProperty(key) && (typeof obj[key] === "object")) 
            jsonPrinter(obj[key])
         else 
            // printing the flat attributes
            console.log(key + " -> " + obj[key]);
        
    


jsonPrinter(p);

【讨论】:

【参考方案11】:

你可以有一个递归函数和一个内置的解析函数。

这里是如何工作的

// recursively loops through nested object and applys parse function
function parseObjectProperties(obj, parse) 
  for (var k in obj) 
    if (typeof obj[k] === 'object' && obj[k] !== null) 
      parseObjectProperties(obj[k], parse)
     else if (obj.hasOwnProperty(k)) 
      parse(obj, k)
    
  

//**************


// example
var foo = 
  bar:'a',
  child:
    b: 'b',
    grand:
      greatgrand: 
        c:'c'
      
    
  



// just console properties
parseObjectProperties(foo, function(obj, prop) 
  console.log(prop + ':' + obj[prop])
)

// add character a on every property
parseObjectProperties(foo, function(obj, prop) 
  obj[prop] += 'a'
)
console.log(foo)

【讨论】:

【参考方案12】:

在带有对象/通用方式的打字稿中,也可以实现:

export interface INestedIterator<T> 
    getChildren(): T[];


export class NestedIterator 
    private static forEach<T extends INestedIterator<T>>(obj: T, fn: ((obj: T) => void)): void       
        fn(obj);    
        if (obj.getChildren().length) 
            for (const item of obj.getChildren()) 
                NestedIterator.forEach(item, fn);            
            ;
        
    

你可以实现接口INestedIterator&lt;T&gt;:

class SomeNestedClass implements INestedIterator<SomeNestedClass>
    items: SomeNestedClass[];
    getChildren() 
        return this.items;
    

然后打电话

NestedIterator.forEach(someNesteObject, (item) => 
    console.log(item);
)

如果您不想使用接口和强类型类,只需删除类型

export class NestedIterator 
    private static forEach(obj: any, fn: ((obj: any) => void)): void       
        fn(obj);    
        if (obj.items && obj.items.length) 
            for (const item of obj.items) 
                NestedIterator.forEach(item, fn);            
            ;
        
    

【讨论】:

【参考方案13】:

我做了一个像lodash pick这样的pick方法。它不像 lodash _.pick 那样好,但你可以选择任何属性事件任何嵌套属性。

您只需将对象作为第一个参数传递,然后传递一个属性数组,您希望在其中获取它们的值作为第二个参数。

例如:

let car =  name: 'BMW', meta:  model: 2018, color: 'white';
pick(car,['name','model']) // Output will be name: 'BMW', model: 2018

代码:

const pick = (object, props) => 
  let newObject = ;
  if (isObjectEmpty(object)) return ; // Object.keys(object).length <= 0;

  for (let i = 0; i < props.length; i++) 
    Object.keys(object).forEach(key => 
      if (key === props[i] && object.hasOwnProperty(props[i])) 
        newObject[key] = object[key];
       else if (typeof object[key] === "object") 
        Object.assign(newObject, pick(object[key], [props[i]]));
      
    );
  
  return newObject;
;

function isObjectEmpty(obj) 
  for (let key in obj) 
    if (obj.hasOwnProperty(key)) return false;
  
  return true;

export default pick;

这是带有单元测试的link to live example

【讨论】:

【参考方案14】:
var findObjectByLabel = function(objs, label) 
  if(objs.label === label)  
    return objs; 
    
  else
    if(objs.subs)
      for(var i in objs.subs)
        let found = findObjectByLabel(objs.subs[i],label)
        if(found) return found
      
    
  
;

findObjectByLabel(cars, "Ford");

【讨论】:

虽然这可能对 OP 有所帮助,但最好添加更多细节、解释、示例等。 在这个解决方案中,我只是使用递归函数调用。首先检查第一个对象的标签,然后递归检查其子对象的标签。【参考方案15】:
var findObjectByLabel = function(obj, label) 

  var foundLabel=null;
  if(obj.label === label)
   
    return obj; 
  

for(var i in obj) 

    if(Array.isArray(obj[i])==true)
    
        for(var j=0;j<obj[i].length;j++)
        
            foundLabel = findObjectByLabel(obj[i], label);
        
    
    else if(typeof(obj[i])  == 'object')
    
        if(obj.hasOwnProperty(i))
                   
            foundLabel = findObjectByLabel(obj[i], label);     
               
    

    if(foundLabel) 
     
        return foundLabel; 
    



return null;
;

var x = findObjectByLabel(cars, "Sedan");
alert(JSON.stringify(x));

【讨论】:

以上是关于遍历嵌套的 JavaScript 对象 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 循环遍历嵌套的 Javascript 对象

javascript - 验证对象内的嵌套键[重复]

JavaScript,检查嵌套对象属性是不是为空/未定义的优雅方法[重复]

从嵌套地图对象javascript中删除重复项

在Javascript中将嵌套对象测试为未定义[重复]

动态过滤嵌套javascript对象数组中的数据[重复]