从文件列表中,动态创建dynatree的哈希数组

时间:2014-09-05 08:09:41

标签: ruby-on-rails ruby dynatree

我有一个按数组排序的文件列表,如:

arr = ["./a.txt", "./b.txt", "./bar/z.php", "./foo/hello/y.php", "./foo/x.php"]

如何创建一个dynatree对象呢? 它的dynatree对象应该如下所示:

[{"name"=>".", "title" => ".", "isDir" => true, "children" => 
  [{"name"=>"a.txt", "title" => "a.txt"}, 
    {"name"=>"b.txt", "title" => "b.txt"}, 
    {"name" => "bar", "title"=>"bar", "isDir"=>true, "children" => 
      [{"name" => "z.php", "title" => "z.php"}, 
        {"name" => "foo", "title" => "foo", "isDir" => true, "children" => 
          [{"name" => "x.php", "title" => "x.php"},
            { "name" => "hello", "title" => "hello", "children" => 
              [{"name" => "y.php", "title"=>"y.php"}
              ]
            }
          ]
        }
      ]
    }
  ]
}]
PS:这个问题可能看起来很懒散,但我现在花了20多个小时来解决这个问题。所以任何帮助将不胜感激。感谢。

2 个答案:

答案 0 :(得分:2)

我喜欢选择更模块化的方法。首先,我构建一个方法make_tree,将文件路径列表转换为嵌套哈希:

require 'pathname'

def insert_node(tree, parts)
  head, *tail = parts
  tree[head] ||= {}
  insert_node tree[head], tail unless tail.empty?
  tree
end

def make_tree(paths)
  paths.reduce({}) do |tree, file| 
    insert_node tree, Pathname(file).each_filename.to_a
  end
end

以下是一个示例 - 此输出稍后将仅用作中间结果:

paths = ["./a.txt", "./b.txt", "./bar/z.php", "./foo/hello/y.php", "./foo/x.php"]
tree = make_tree(paths)
#=> {"."=>
#    {"a.txt"=>{},
#     "b.txt"=>{},
#     "bar"=>{"z.php"=>{}},
#     "foo"=>{"hello"=>{"y.php"=>{}}, "x.php"=>{}}}}

然后,我们可以编写一个函数将这个嵌套的哈希转换为“dynatree”表示:

def make_dynatree(tree)
  tree.map do |node, subtree|
    if subtree.empty?
      {"name" => node, "title" => node}
    else
      {"name" => node, "title" => node, "isDir" => true, "children" => make_dynatree(subtree)}
    end
  end
end

最后:

dynatree = make_dynatree(tree)
#=> [{"name"=>".", "title"=>".", "isDir"=>true, "children"=>
#     [{"name"=>"a.txt", "title"=>"a.txt"},
#      {"name"=>"b.txt", "title"=>"b.txt"},
#      {"name"=>"bar", "title"=>"bar", "isDir"=>true, "children"=>[
#        {"name"=>"z.php", "title"=>"z.php"}]},
#      {"name"=>"foo", "title"=>"foo", "isDir"=>true, "children"=>[
#        {"name"=>"hello", "title"=>"hello", "isDir"=>true, "children"=>[
#          {"name"=>"y.php", "title"=>"y.php"}]},
#          {"name"=>"x.php", "title"=>"x.php"}]}]}]

答案 1 :(得分:1)

我的想法是迭代每条路径。每条路径由/分成几部分。最后一部分是文件,其他部分是目录。

除非已添加新目录,否则我将浏览每个部分。在每次迭代结束时,我将上下文切换到下一级 - 最后一个目录的children数组。

文件是最后一部分 - 我简单地将它附加到当前上下文。

arr = ["./a.txt", "./b.txt", "./bar/z.php", "./foo/hello/y.php", "./foo/x.php"]

tree = []

arr.each do |path|
  # start at the beginning on each iteration
  current_level = tree

  # split path by '/'
  splitted_path = path.split('/')

  # remember the size of parts in path to distinct files from folders
  size = splitted_path.size

  # iterate over each part of a path
  splitted_path.each_with_index do |node, i|
    if i != size - 1
      # current node is path
      # detect if it is already in the array
      unless current_level.detect { |n| n['name'] == node }
        # if not - append it to array
        current_level << {
          'name' => node,
          'title' => node,
          'isDir' => true,
          'children' => []
        }
      end
      # set the current level to the new node's children array
      current_level = current_level.detect { |n| n['name'] == node }['children']
    else
      # node is a file - append it to the children array on current level
      current_level << {
        'name' => node,
        'title' => node
      }
    end
  end
end

tree