【问题标题】:JS map object names recursivelyJS 递归地映射对象名称
【发布时间】:2019-11-15 07:10:31
【问题描述】:

我有一个对象数组,深度未定义。 现在我希望将名称输出如下:

Top Level
Top Level > Sub 1
Top Level > Sub 1 > Sub 1-2
Top Level > Sub 2

但是,我总是只得到最低的元素。我不知道我还可以更改什么来正确格式化名称。

        parseCategories(categories, stackCounter = 0) {
          categories.forEach(c => {
            this.res[stackCounter] = { name: c.attributes.name, id: c.id };
            if(c.children.length >= 1) {
              this.parseCategories(c.children, stackCounter + 1);
            }
          });
          return this.res;
        }

我的对象如下所示:

{
   "data":[
      {
         "type":"categories",
         "id":"1",
         "attributes":{
            "name":"Top Level"
         },
         "children":[
            {
               "type":"categories",
               "id":"2",
               "attributes":{
                  "name":"Sub 1"
               },
               "children":[
                  {
                     "type":"categories",
                     "id":"4",
                     "attributes":{
                        "name":"Sub 1-2"
                     },
                     "children":[

                     ]
                  }
               ]
            },
            {
               "type":"categories",
               "id":"3",
               "attributes":{
                  "name":"Sub 2"
               },
               "children":[

               ]
            }
         ]
      }
   ]
}

【问题讨论】:

标签: javascript vue.js recursion


【解决方案1】:

生成器函数通常会简化此类遍历。在这里,我们可以编写一个非常简单的生成器,然后将其包装在一个从该生成器创建数组的函数中:

const getPaths = function * (xs, ps = []) {
  for (let x of xs) {
    yield [... ps, x .attributes .name] .join (' > ')
    yield * getPaths (x .children, [...ps, x .attributes .name])
  }
}

const categoryNames = (categories) => 
  [... getPaths (categories .data)]

const categories = {data: [{type: "categories", id: "1", attributes: {name: "Top Level"}, children: [{type: "categories", id: "2", attributes: {name: "Sub 1"}, children: [{type: "categories", id: "4", attributes: {name: "Sub 1-2"}, children: []}]}, {type: "categories", id: "3", attributes: {name: "Sub 2"}, children: []}]}]};

console .log (
  categoryNames(categories)
)

getPaths 可以通过删除join (' > ') 调用并将其添加到map 调用中categoryNames 的末尾而变得更通用一点。但是由于childrenattributes.names 已经是相当具体的问题,我可能不会打扰。


说明

您说您没有完全遵循此代码。这是试图解释它。如果我解释您已经理解的内容,请不要生气。我不清楚具体需要解释什么。

外部函数categoryNamesgetPaths 的一个非常小的包装器。那个做所有的工作。

getPaths 有两个重要的特点需要注意:

  • 它是一个generator function,在function 关键字和参数列表之间由* 注明。生成器函数创建Generator 对象,因为它们符合[可迭代协议],因此可以在let x of generator[...generator] 等结构中使用。这就是categoryNamesgetPaths 的输出转换为数组的方式。 (顺便说一句,可迭代协议也是getPaths 中的for-of 循环将数组转换为值序列的方式。)生成器函数通过yielding 单个值或使用yield * anotherGenerator 单独生成每个值来工作由另一个生成器产生。在这些yield 调用之间,函数会暂停,直到请求下一个值。

  • 这是一个recursive function;函数体再次使用更简单的参数调用相同的函数。大多数情况下,在递归函数中,您会看到一个明确的基本情况,当输入足够简单时,直接返回答案而无需递归调用。在这里,基本情况是隐含的。当xs 为空数组时,for-loop 的主体永远不会被调用,因此递归结束。

getPaths 接受一个值数组(xs 是未知类型值列表的默认名称;nodes 也是一个好名字),它接受一个字符串数组,表示路径直到当前节点的层次结构。例如,它可能包含["Top Level", "Sub 1"]。请注意,这是default parameter;如果你不提供它,它将被赋予一个空数组。

我们循环提供给我们的值。对于每一个,我们首先通过在它们之间穿插" > " 来产生组合当前路径和我们当前对象的attribute 属性的name 属性的结果。然后我们通过传递孩子和一个名称数组(包括当前名称)来递归,依次产生它的每个孩子。这个版本可能性能稍微好一些,也更容易阅读:

const getPaths = function * (xs, paths = []) {
  for (let x of xs) {
    const newPaths = [... paths, x .attributes .name]
    yield newPaths .join (' > ')
    yield * getPaths (x .children, newPaths)
  }
}

如果你愿意,你可以像这样定义newPaths

    const newPaths = paths .concat (node .attributes .name)

我希望这会有所帮助。如果您对此有任何疑问,请添加评论。

【讨论】:

  • 感谢您的代码。我只是将自己读入“遍历树”,并且可能必须开发一些示例。我不太明白你的代码,但我现在会习惯的。
  • @Phil795:添加了解释。希望现在更容易理解了。
【解决方案2】:

看看这个块

categories.forEach(c => {
  this.res[stackCounter] = { name: c.attributes.name, id: c.id };
});

您可以看到res[stackCounter] 总是被categories 的最后一个元素覆盖。为了解决这个问题,res[stackCounter] 也应该是一个数组。

parseCategories(categories, stackCounter = 0) {
  // init res[stackCounter]
  if (this.res[stackCounter]) {
    this.res[stackCounter] = [];
  }
  categories.forEach(c => {
    this.res[stackCounter].push({ name: c.attributes.name, id: c.id }); // push to res[stackCounter]
    if(c.children.length >= 1) {
      this.parseCategories(c.children, stackCounter + 1);
    }
  });
  return this.res;
}

【讨论】:

  • 这会产生:[[{"name":"Top Level","id":"1"}],[{"name":"Sub 1","id":"2"},{"name":"Sub 2","id":"3"}],[{"name":"Sub 1-2","id":"4"}]] 我想连接所有父类别名称
  • 您的问题与树数据结构有关。具体来说,“遍历”树。您应该寻找并学习它,因为它是最重要和最流行的数据结构之一
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-11
  • 2018-05-30
  • 1970-01-01
  • 2011-07-24
  • 1970-01-01
  • 2013-10-31
  • 1970-01-01
相关资源
最近更新 更多