【问题标题】:Tree traversal in javascriptjavascript中的树遍历
【发布时间】:2014-02-13 20:58:41
【问题描述】:

我正在尝试为存储在节点中的 MongoDB 中的页面生成 URL。

使用以下函数,我想遍历一个 javascript 对象并显示每个元素的路径。

我快到了,但我被困住了 - 使用 Async 甚至可能有更好的方法来做到这一点(我必须承认,这让我有点困惑)。

函数:(demo)

function printTree(people, slug) {
    for (var p = 0; p < people.length; p++) {
        var root = people[p];
        slug = slug + root.name + "/";
        console.log(slug);
        if (root.children.length > 0) {
            var childrenCount = root.children.length;
            for (var c = 0; c < childrenCount; c++) {
                if (root.children[c].children.length > 0) {
                    printTree(root.children[c].children, slug + root.children[c].name + "/");
                }
            }
        }
    }
};

输出:

/michael/
/michael/angela/oscar
/michael/meredith/creed
/michael/meredith/creed/kelly

预期输出:

/michael/
/michael/angela/
/michael/angela/oscar/
/michael/meredith/
/michael/meredith/creed/
/michael/meredith/kelly/

对象:

[
  {
    "name": "michael",
    ...
    "children": [
      {
        "name": "angela",
        ...
        "children": [
          {
            "name": "oscar",
            ...
            "children": []
          }
        ]
      },
      {
        "name": "meredith",
        ...
        "children": [
          {
            "name": "creed",
            ...
            "children": []
          },
          {
            "name": "kelly",
            ...
            "children": []
          }
        ]
      },
      { ... }
    ]
  }
]

如果有帮助,数据将使用嵌套集存储:https://github.com/groupdock/mongoose-nested-set 因此,使用嵌套集(否定上述对象)可能有更好的方法来完成上述工作。

【问题讨论】:

    标签: javascript node.js mongodb nested-sets


    【解决方案1】:

    不太喜欢重新发明轮子,所以这里有一个使用object-scan 的解决方案。我们将它用于许多数据处理任务,并且非常喜欢它,因为它使事情更易于维护。但是有一个学习曲线。无论如何,这是您解决问题的方法

    // const objectScan = require('object-scan');
    
    const scanTree = (tree) => objectScan(['**.children'], {
      reverse: false,
      breakFn: ({ isMatch, parents, context }) => {
        if (!isMatch) {
          return
        }
        context.push(
          `/${parents
            .filter((p) => 'name' in p)
            .map(({ name }) => name)
            .reverse()
            .join('/')}/`
        );
      }
    })(tree, []);
    
    const tree = [{ id: '52fc69975ba8400021da5c7a', name: 'michael', children: [{ id: '52fc69975ba8400021da5c7d', parentId: '52fc69975ba8400021da5c7a', name: 'angela', children: [{ id: '52fc69975ba8400021da5c83', parentId: '52fc69975ba8400021da5c7d', name: 'oscar', children: [] }] }, { id: '52fc69975ba8400021da5c7b', parentId: '52fc69975ba8400021da5c7a', name: 'meredith', children: [{ id: '52fc69975ba8400021da5c7f', parentId: '52fc69975ba8400021da5c7b', name: 'creed', children: [] }, { id: '52fc69975ba8400021da5c7e', parentId: '52fc69975ba8400021da5c7b', name: 'kelly', children: [] }] }, { id: '52fc69975ba8400021da5c7c', parentId: '52fc69975ba8400021da5c7a', name: 'jim', children: [{ id: '52fc69975ba8400021da5c82', parentId: '52fc69975ba8400021da5c7c', name: 'dwight', children: [] }, { id: '52fc69975ba8400021da5c80', parentId: '52fc69975ba8400021da5c7c', name: 'phyllis', children: [] }, { id: '52fc69975ba8400021da5c81', parentId: '52fc69975ba8400021da5c7c', name: 'stanley', children: [] }] }] }];
    
    scanTree(tree).map((e) => console.log(e));
    // => /michael/
    // => /michael/angela/
    // => /michael/angela/oscar/
    // => /michael/meredith/
    // => /michael/meredith/creed/
    // => /michael/meredith/kelly/
    // => /michael/jim/
    // => /michael/jim/dwight/
    // => /michael/jim/phyllis/
    // => /michael/jim/stanley/
    .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的作者

    【讨论】:

      【解决方案2】:

      给你。您不需要第二个 for 循环,因为您的 printTree 函数无论如何都会循环所有内容(demo)。

      function printTree(people, slug){
        slug = slug || '/';
        for(var i = 0; i < people.length; i++) {
          console.log(slug + people[i].name + '/');
          if(people[i].children.length){
            printTree(people[i].children, slug + people[i].name + '/')
          }
        }
      }
      

      【讨论】:

      • 嗨,这适用于迈克尔、安吉拉和奥斯卡,但是一旦树到达迈克尔的其他孩子,它就会折断。 see demomeredith 是 angela 的兄弟姐妹,但输出显示 meredith 是 angela 的孩子。
      • 那是因为这条线:slug = slug + people[i].name + '/'; 为你解决了这个问题
      • 谢谢!你们俩!这么多:D
      【解决方案3】:

      你也可以考虑在 ECMA5 中这样的东西,以防你进一步使用 tree 或者想要使用除 / 之外的一些分隔符。 @bioball 的答案没有错,如果需要,这只会给你更多的灵活性。

      function makeTree(people, slug, sep) {
          slug = slug || '/';
          sep = sep || slug;
          return people.reduce(function (tree, person) {
              var slugPerson = slug + person.name + sep;
      
              return tree.concat(slugPerson, makeTree(person.children, slugPerson, sep));
          }, []);
      }
      
      function printTree(tree) {
          tree.forEach(function (path) {
              console.log(path);
          });
      }
      
      printTree(makeTree(data));
      

      开启jsFiddle

      【讨论】:

      • 这是一个不错的解决方案!反转这种方法来生成一个人的路径会更容易吗? IE:给定“Kelly”和树,生成 /michael/meredith/kelly ?我目前正在尝试扭转出色的@bioball 和 Jason 提供的功能,但我失败了。
      • 听起来像是你应该在 SO 上创建的另一个问题,也许你会为一个好问题赢得更多代表。但也许你的意思是像这个jsFiddle?当然,您可以更改过滤器以做任何您想做的事情。我举了两个例子。
      猜你喜欢
      • 1970-01-01
      • 2016-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多