Ruby 创建递归目录树

发布于 2024-10-29 04:05:11 字数 195 浏览 1 评论 0原文

我需要递归遍历目录并创建要与 jsTree 控件一起使用的树。该控件接受 JSON 格式像这样。我需要一些红宝石魔法来让这一切干净而快速地发生。

任何帮助表示赞赏。

I need to recursively traverse a directory and create a tree to be used with the jsTree control. The control accepts a JSON format like so. I need some ruby magic to make this happen cleanly and quickly.

Any help is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

深海里的那抹蓝 2024-11-05 04:05:11

您可能想要这样的东西(未经测试):

def directory_hash(path, name=nil)
  data = {:data => (name || path)}
  data[:children] = children = []
  Dir.foreach(path) do |entry|
    next if (entry == '..' || entry == '.')
    full_path = File.join(path, entry)
    if File.directory?(full_path)
      children << directory_hash(full_path, entry)
    else
      children << entry
    end
  end
  return data
end

递归地沿着树走下去,建立一个散列。使用您最喜欢的序列化库将其转换为 json。

You probably want something like this (untested):

def directory_hash(path, name=nil)
  data = {:data => (name || path)}
  data[:children] = children = []
  Dir.foreach(path) do |entry|
    next if (entry == '..' || entry == '.')
    full_path = File.join(path, entry)
    if File.directory?(full_path)
      children << directory_hash(full_path, entry)
    else
      children << entry
    end
  end
  return data
end

Recursively walk down the tree, building up a hash. Turn it into json with your favourite serialisation library.

一江春梦 2024-11-05 04:05:11

首先获取你的树,将其转换为叶子的路径列表,类似于:(

def leaves_paths tree
  if tree[:children]
    tree[:children].inject([]){|acc, c|
      leaves_paths(c).each{|p|
        acc += [[tree[:name]] + p]
      }
      acc
    }
  else
    [[tree[:name]]]
  end
end

不确定上面是否完全遵循你的 jsTree 结构,但原理是相同的。)

这是输入和输出的示例:

tree = {name: 'foo', children: [
      {name: 'bar'},
      {name: 'baz', children: [
        {name: 'boo'}, 
        {name: 'zoo', children: [
          {name: 'goo'}
        ]}
      ]}
    ]}

p leaves_paths tree
#=> [["foo", "bar"], ["foo", "baz", "boo"], ["foo", "baz", "zoo", "goo"]]

然后,对于每个路径,调用 FileUtils#mkdir_p

paths = leaves_paths tree
paths.each do |path|
  FileUtils.mkdir_p(File.join(*path))
end

你应该没问题。

编辑:更简单的版本:

您不需要创建叶子列表,只需遍历整个树并为每个节点创建一个目录:

# executes block on each tree node, recursively, passing the path to the block as argument
def traverse_with_path tree, path = [], &block
  path += [tree[:name]]
  yield path
  tree[:children].each{|c| traverse_with_path c, path, &block} if tree[:children]
end

traverse_with_path tree do |path|
  FileUtils.mkdir(File.join(*path))
end

Edit2:

哦,对不起,我误解了。因此,这里有一种基于磁盘上的目录树创建哈希的方法:

Dir.glob('**/*'). # get all files below current dir
  select{|f|
    File.directory?(f) # only directories we need
  }.map{|path|
    path.split '/' # split to parts
  }.inject({}){|acc, path| # start with empty hash
    path.inject(acc) do |acc2,dir| # for each path part, create a child of current node
      acc2[dir] ||= {} # and pass it as new current node
    end
    acc
  }

因此,对于以下结构:

#$ mkdir -p foo/bar
#$ mkdir -p baz/boo/bee
#$ mkdir -p baz/goo

上面的代码返回此哈希:

{
  "baz"=>{
    "boo"=>{
      "bee"=>{}},
    "goo"=>{}},
  "foo"=>{
    "bar"=>{}}}

希望您能设法满足您的需求。

First take your tree, convert it to a list of paths to leaves, similar to:

def leaves_paths tree
  if tree[:children]
    tree[:children].inject([]){|acc, c|
      leaves_paths(c).each{|p|
        acc += [[tree[:name]] + p]
      }
      acc
    }
  else
    [[tree[:name]]]
  end
end

(Not sure if above exactly follows your jsTree structure, but the principle is the same.)

Here's a sample of input and output:

tree = {name: 'foo', children: [
      {name: 'bar'},
      {name: 'baz', children: [
        {name: 'boo'}, 
        {name: 'zoo', children: [
          {name: 'goo'}
        ]}
      ]}
    ]}

p leaves_paths tree
#=> [["foo", "bar"], ["foo", "baz", "boo"], ["foo", "baz", "zoo", "goo"]]

Then, for each path, call FileUtils#mkdir_p:

paths = leaves_paths tree
paths.each do |path|
  FileUtils.mkdir_p(File.join(*path))
end

And you should be ok.

Edit: Simpler version:

You don't need to create list of leaves, just traverse whole tree and create a directory for every node:

# executes block on each tree node, recursively, passing the path to the block as argument
def traverse_with_path tree, path = [], &block
  path += [tree[:name]]
  yield path
  tree[:children].each{|c| traverse_with_path c, path, &block} if tree[:children]
end

traverse_with_path tree do |path|
  FileUtils.mkdir(File.join(*path))
end

Edit2:

Oh, sorry, I misunderstood. So, here's a way to make a Hash based on directory tree on disk:

Dir.glob('**/*'). # get all files below current dir
  select{|f|
    File.directory?(f) # only directories we need
  }.map{|path|
    path.split '/' # split to parts
  }.inject({}){|acc, path| # start with empty hash
    path.inject(acc) do |acc2,dir| # for each path part, create a child of current node
      acc2[dir] ||= {} # and pass it as new current node
    end
    acc
  }

So, for the following structure:

#$ mkdir -p foo/bar
#$ mkdir -p baz/boo/bee
#$ mkdir -p baz/goo

code above returns this hash:

{
  "baz"=>{
    "boo"=>{
      "bee"=>{}},
    "goo"=>{}},
  "foo"=>{
    "bar"=>{}}}

Hope you'll manage to suit it to your needs.

浮华 2024-11-05 04:05:11

Ruby 的查找模块(require 'find')是极简主义的,但可以很好地处理目录递归:http://www.ruby-doc.org/stdlib/libdoc/find/rdoc/classes/Find.html

Ruby's Find module (require 'find') is minimalist but handles directory recursion well: http://www.ruby-doc.org/stdlib/libdoc/find/rdoc/classes/Find.html

陌生 2024-11-05 04:05:11

截至 2015 年 6 月,接受的答案不起作用。我将密钥 :data 更改为 'text'。我还概括了代码以排除目录和文件。

def directory_hash(path, name=nil, exclude = [])                                
  exclude.concat(['..', '.', '.git', '__MACOSX', '.DS_Store'])                  
  data = {'text' => (name || path)}                                             
  data[:children] = children = []                                               
  Dir.foreach(path) do |entry|                                                  
    next if exclude.include?(entry)                                             
    full_path = File.join(path, entry)                                          
    if File.directory?(full_path)                                               
      children << directory_hash(full_path, entry)                              
    else                                                                        
      children << {'icon' => 'jstree-file', 'text' => entry}                    
    end                                                                         
  end                                                                           
  return data                                                                   
end  

The accepted answer did not work as of June 2015. I changed the key :data to 'text'. I also generalized the code to exclude directories and files.

def directory_hash(path, name=nil, exclude = [])                                
  exclude.concat(['..', '.', '.git', '__MACOSX', '.DS_Store'])                  
  data = {'text' => (name || path)}                                             
  data[:children] = children = []                                               
  Dir.foreach(path) do |entry|                                                  
    next if exclude.include?(entry)                                             
    full_path = File.join(path, entry)                                          
    if File.directory?(full_path)                                               
      children << directory_hash(full_path, entry)                              
    else                                                                        
      children << {'icon' => 'jstree-file', 'text' => entry}                    
    end                                                                         
  end                                                                           
  return data                                                                   
end  
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文