【问题标题】:Looping multilevel Object循环多级对象
【发布时间】:2017-10-21 00:30:58
【问题描述】:

假设我们有以下对象,为了获得每个对象的名称属性,将它迭代到结束的最佳方法是什么? 请注意,Object 的大小可能会有所不同,浏览应按以下顺序进行:a、b、a1、a2、b1、a21、b11、b12 ...

var obj = {
  a: {
    name: 'a',
    a1: {
      name: 'a1'
    },
    a2: {
      name: 'a2',
      a21: {
        name: 'a21'
      }
    }
  },

  b: {
    name: 'b'
    b1: {
      name: 'b1',
      b11: {
        name: 'b11'
      },
      b12: {
        name: 'b12'
      }
    }
  }
};

【问题讨论】:

标签: javascript loops object recursion


【解决方案1】:

您可以使用breadth-first search。它是一种先迭代树的每一层然后再下一层的算法。

此实现适用于节点队列,这意味着,要调用函数breadthFirst,对象/单个节点必须包装在一个数组中。

function breadthFirst(queue) {
    var newQueue = [];
    queue.forEach(function (node) {
        ('name' in node) && console.log(node.name);
        Object.keys(node).forEach(function (k) {
            node[k] && typeof node[k] === 'object' && newQueue.push(node[k]);
        });
    });
    newQueue.length && breadthFirst(newQueue);
}

var object = { a: { name: 'a', a1: { name: 'a1' }, a2: { name: 'a2', a21: { name: 'a21' } } }, b: { name: 'b', b1: { name: 'b1', b11: { name: 'b11' }, b12: { name: 'b12' } } } };

breadthFirst([object]); // a b a1 a2 b1 a21 b11 b12
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • 很好的答案。这正是我一直在寻找的。非常感谢你。
【解决方案2】:

您正在寻找的是 Nina 正确提到的 breadth-first 解决方案。这是我的实现。在此解决方案中,您可以将结果存储在数组中,然后再执行console.log

var obj = {
  a: {
    name: 'a',
    a1: {
      name: 'a1'
    },
    a2: {
      name: 'a2',
      a21: {
        name: 'a21'
      }
    }
  },
  b: {
    name: 'b',
    b1: {
      name: 'b1',
      b11: {
        name: 'b11'
      },
      b12: {
        name: 'b12'
      }
    }
  }
};

var ans = [];
var q = [];
q.push(obj);

function getAllKeys() {
  if (q.length == 0) {
    return;
  }
  var obj = q.shift();

  var keys = Object.keys(obj);
  ans = ans.concat(keys);
  var index = ans.indexOf('name');
  if (index != -1) {
    ans.splice(index, 1);
  }

  for (var i = 0; i < keys.length; i++) {
    if (typeof obj[keys[i]] == 'object') {
      q.push(obj[keys[i]]);
    }

  }
  getAllKeys();
}
getAllKeys();
console.log(ans);

【讨论】:

    【解决方案3】:

    你需要一个递归的东西。您可以随意更改 console.log 以推送到某个地方或其他任何地方...

    var obj = {
        a: {
            name: 'a',
            a1: {
                name: 'a1'
            },
            a2: {
                name: 'a2',
                a21: {
                    name: 'a21'
                }
            }
        },
            b: {
            name: 'b',
            b1: {
                name: 'b1',
                b11: {
                    name: 'b11'
                },
                b12: {
                    name: 'b12'
                }
            }
        }
    };
    
    var looping = function(obj) {
        var keys = Object.keys(obj);
        for (var i = 0; i < keys.length; i++) {
            if(typeof obj[keys[i]] === 'string') console.log(obj[keys[i]]);
            else looping(obj[keys[i]]);
        }
    }
    
    looping(obj);
    

    【讨论】:

    • 您的解决方案没有提供 OP 正在寻找的输出!
    【解决方案4】:

    这是一个简单的递归函数,用于以 广度优先 的顺序获取所有 name 属性。我正在使用一个帮助程序pairs,它可以更轻松地处理每个对象提供的键值对。从那里开始,这是一个关于递归函数应该如何响应的简单案例分析:

    • 基本情况 - 返回累加器
    • key为name,将value追加到累加器
    • value 是一个对象,将valuepairs 添加到要处理的对列表中
    • 默认情况 - 什么都不做并处理下一对

    此答案与其他答案的不同之处在于运行它没有副作用。 loop 不是在 loop 函数本身中对某些行为进行硬编码,而是返回一个由 name 属性值组成的数组,然后您可以根据需要执行任何操作。

    const pairs = o =>
      Object.keys(o).map(k => ({key: k, value: o[k]}))
      
    const loop = o => {
      const aux = (acc, [x,...xs]) => {
        if (x === undefined)
          return acc
        else if (x.key === 'name')
          return aux([...acc, x.value], xs)
        else if (Object(x.value) === x.value)
          return aux(acc, xs.concat(pairs(x.value)))
        else
          return aux(acc, xs)
      }
      return aux([], pairs(o))
    }
    
    const obj = { a: { name: 'a', a1: { name: 'a1' }, a2: { name: 'a2', a21: { name: 'a21' } } }, b: { name: 'b', b1: { name: 'b1', b11: { name: 'b11' }, b12: { name: 'b12' } } } }
      
    console.log(loop(obj))
    // [ 'a', 'b', 'a1', 'a2', 'b1', 'a21', 'b11', 'b12' ]

    或者,您可以使用生成器实现loop,这样您就可以在 迭代时对值进行操作。如果您对此感兴趣,请告诉我,我会写一篇文章。


    编辑

    原始答案以错误的顺序处理了对象。上面的代码现在正确回答了这个问题^_^

    【讨论】:

    • 很有趣,所有其他答案,因为你的也使用深度优先,这没有被问到......
    • 天哪!我没有引起足够的重视。我醒来后会尝试修复它^_^ thx Nina
    • @NinaScholz 感谢您了解这一点。我更新了答案
    【解决方案5】:

    您正在寻找breadth-first traversal:

    // Breadh-first object traversal:
    function traverse(...objs) {
      for (let obj of objs) {
        let next = Object.values(obj).filter(val => val && typeof val === 'object');
        objs.push(...next);
      }
      return objs;
    }
    
    // Example:
    let obj = {
      a:{name:"a",a1:{name:"a1"},a2:{name:"a2",a21:{name:"a21"}}},
      b:{name:"b",b1:{name:"b1",b11:{name:"b11"},b12:{name:"b12"}}}
    };
    
    for (let child of traverse(obj)) {
      if (child.name) console.log(child.name);
    }

    【讨论】:

      猜你喜欢
      • 2017-05-30
      • 1970-01-01
      • 2021-11-06
      • 2015-06-14
      • 1970-01-01
      • 2016-02-21
      • 1970-01-01
      • 2018-03-05
      • 2018-09-14
      相关资源
      最近更新 更多