JS映射对象名称递归

时间:2019-11-15 07:10:31

标签: javascript vue.js recursion

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

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":[

               ]
            }
         ]
      }
   ]
}

2 个答案:

答案 0 :(得分: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的输出转换为数组的方式。 (顺便说一句,可迭代协议也是for-of中的getPaths循环将数组转换为值序列的方式。)生成器函数通过yield设置单个值或使用{{1} }分别产生另一个生成器产生的每个值。在这两个yield * anotherGenerator调用之间,函数被挂起,直到请求下一个值为止。

  • 它是recursive function;函数主体使用更简单的参数再次调用相同的函数。在递归函数中,最常见的情况是您会看到一个显式的基本情况,即当输入足够简单时,无需进行递归调用即可直接返回答案。这里的基本情况是隐式的。当yield为空数组时,xs的主体将不会被调用,因此递归结束。

for-loop接受一个值数组(getPaths是我的默认名称,表示未知类型的值列表; xs也是一个好名字),它接受一个数组代表层次结构中直到当前节点的路径的字符串。因此,它可能例如包含nodes。请注意,这是default parameter;如果您不提供它,它将得到一个空数组。

我们遍历提供给我们的值。对于每一个对象,我们首先得出将当前路径与当前对象的["Top Level", "Sub 1"]属性的name属性进行组合的结果,方法是在它们之间穿插attribute。然后,我们通过传递子代和一系列名称(包括当前名称)来递归,依次产生其每个子代。这个版本的性能可能更高一些,并且更容易阅读:

" > "

如果需要,您可以这样定义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

我希望能有所帮助。如果对此有疑问,请添加评论。

答案 1 :(得分:0)

看看这个方块

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;
}