从嵌套对象树获取路径

时间:2014-04-16 12:52:11

标签: javascript recursion coffeescript

我目前遇到的一个问题是,当我开始时似乎并不难为我解决,但我现在卡住了几个小时,所以我们走了:

给定这个对象数组:

groups = [
  {
    name: 'Custard apple',
    slug: 'custard-apple',
    children: [
      {
        name: 'Vanilla',
        slug: 'vanilla',
        children: [
          {
            name: 'Strawberry',
            slug: 'strawberry',
            children: []
          }, {
            name: 'Pineapple',
            slug: 'pineapple',
            children: []
          }
        ]
      }, {
        name: 'Chocolate',
        slug: 'chocolate',
        children: []
      }
    ]
  }, {
    name: 'Raspberry',
    slug: 'raspberry',
    children: []
  }, {
    name: 'Lemon',
    slug: 'lemon',
    children: [
      {
        name: 'Orange',
        slug: 'orange',
        children: [
          {
            name: 'Coconut',
            slug: 'coconut',
            children: []
          }
        ]
      }, {
        name: 'Almond',
        slug: 'almond',
        children: []
      }
    ]
  }
];

我正在尝试找到一个函数,它在给定slug的帮助下为我提供了一个对象的路径:

var find_path = function(groups, slug) { /* looking for a solution to this */ };
result = find_path(groups, 'pineapple'); 

console.log(result);
// [{ name: 'Custard Apple', slug: 'custard-apple' }, { name: 'Vanilla', slug: 'vanilla'}, { name: 'Pineapple', slug: 'pinapple' }]

// another example
result = find_path(groups, 'lemon');
console.log(result);
// [{ name: 'Lemon', slug: 'lemon' }]

我尝试了几种递归方法,其中我试图在函数调用中保存路径,但我通常最终得到重复/一般不是所需的结果。我主要围绕一个递归查找和(失败)尝试来保存路径。

那么,是否有一种递归方法来解决这个问题?还是我觉得太复杂了?

1 个答案:

答案 0 :(得分:5)

您正在处理树,因此递归是一种自然的解决方案。一个简单的深度优先搜索(查看当前节点然后查看其子节点)可能是最简单的解决方案。像这样:

slice = (o, properties...) ->
    ret = { }
    ret[p] = o[p] for p in properties
    ret

find_path = (a, slug) ->
    for o in a
        # Bail out now if this is what we're looking for.
        if(o.slug == slug)
            return [ slice(o, 'name', 'slug') ]
        # Scan the children if not.
        if(sub = find_path(o.children, slug))
            return [ slice(o, 'name', 'slug') ].concat(sub)
    # Explicitly return `undefined` to make sure the caller
    # gets The Right Thing back.
    return

演示:http://jsfiddle.net/ambiguous/3FNZy/

递归中的每个步骤都不会为您提供任何内容或从当前节点到您正在寻找的路径的路径。然后展开递归构建通过concat调用的路径。当然,这里有相当多的阵列复制,但对于像这样的小型数据集来说并不值得担心(如果你有更多的数据,那么你想要切换到某种索引类型)结构)。

slice功能只是为了复制e而不是e.children"逻辑更具可读性;遗憾的是,您无法在destructured assignment中使用{ x.a, x.b } = obj之类的复合结构,因此slice函数与您即将获得的功能一样好(您 可以{a,b} = obj,但你无法添加额外的嵌套级别来获取对象切片。)