【问题标题】:Tree node using an array of hashes使用哈希数组的树节点
【发布时间】:2017-05-28 13:58:21
【问题描述】:

我有以下哈希数组:

[
  {key: 1, reference: 'reference', path: '.../public/shared/file_1.txt', type: 'public'},
  {key: 2, reference: 'reference', path: '.../public/shared/file_2.txt', type: 'public'},
  {key: 3, reference: 'reference', path: '.../public/shared/sub_folder/file_3.txt', type: 'public'},
  {key: 4, reference: 'reference', path: '.../public/shared/sub_folder/file_4.txt', type: 'public'},
  {key: 5, reference: 'reference', path: '.../log/file_5.txt', type: 'log'},
  {key: 5, reference: 'reference', path: '.../tmp/cache/file_6.txt', type: 'log'},
]

然后使用每个节点的路径结构我想创建一个这样的树:

[
  { 
    key: 'root', folder: true, title: 'root',
    children: [
                {
                  key: 'public', folder: true, title: 'public',
                  children: [
                              {
                                key: 'shared', folder: true, title: 'shared',
                                children: [ 
                                            { key: 1, title: 'file_1.txt' },
                                            { key: 2, title: 'file_2.txt' },
                                            {
                                              key: 'sub_folder', folder: true, title: 'sub_folder',
                                              children: [
                                                { key: 3, title: 'file_3.txt' },
                                                { key: 4, title: 'file_4.txt' }
                                              ]
                                            }
                                          ]
                              }
                            ]
                },
                {
                  key: 'log', folder: true, title: 'log', children: [ { key: 5, title: 'file_5.txt' } ]
                },
                {
                  key: 'tmp', folder: true, title: 'tmp',
                  children: [
                              { key: 'cache', folder: true, title: 'cache', children: [ { key: 6, title: 'file_6.txt' } }
                            ]
                }
              ]
  }
]

是否可以使用每个节点的路径创建该结构?我正在使用 Ruby on Rails。

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4


    【解决方案1】:

    你可以定义这样的类online repl

    class Tree 
      attr_reader :array, :tree
    
      def initialize(array)
        @array = array
        create_tree!
      end
    
      private
    
      def create_tree!
        @tree = []
        array.each do |hash|
          process_path(hash[:path].gsub('...', 'root').split('/'))
        end
      end
    
      def process_path(array)
        current = @tree
        array.each do |folder_or_file|
          if persist = current.find { |hash| hash[:key] == folder_or_file }
            current = persist[:children]
          else
            current << {
              key: folder_or_file,
              folder: folder_or_file['.'].nil?,
              title: folder_or_file,
              children: []
            }
            current = current.last[:children]
          end
        end
      end
    end
    
    arr = [
      {key: 1, reference: 'reference', path: '.../public/shared/file_1.txt', type: 'public'},
      {key: 2, reference: 'reference', path: '.../public/shared/file_2.txt', type: 'public'},
      {key: 3, reference: 'reference', path: '.../public/shared/sub_folder/file_3.txt', type: 'public'},
      {key: 4, reference: 'reference', path: '.../public/shared/sub_folder/file_4.txt', type: 'public'},
      {key: 5, reference: 'reference', path: '.../log/file_5.txt', type: 'log'},
      {key: 5, reference: 'reference', path: '.../tmp/cache/file_6.txt', type: 'log'},
    ]
    
    Tree.new(arr).tree
    

    【讨论】:

    • 如果文件名不需要后缀,似乎需要一个小模块。
    【解决方案2】:

    我提供了一个递归解决方案。

    代码

    def doit(arr)
      a = arr.map do |g|
        *front, last = ['root', *g[:path][4..-1].split('/')]
        [front, { key: g[:key], title: last }]
      end
      recurse a
    end
    
    def recurse(a)
      a.reject { |dirs, _| dirs.empty? }. 
        group_by { |dirs,_| dirs.shift }.
        map do |dir,v|
          empty, non_empty = v.partition { |d,_| d.empty? }
          { key: dir, folder: true, title: dir,
            children: [*empty.map(&:last), *recurse(non_empty)] }
        end
    end
    

    示例

    arr = [
      { key: 1, reference: 'reference', path: '.../public/shared/file_1.txt',
        type: 'public' },
      { key: 2, reference: 'reference', path: '.../public/shared/file_2.txt',
        type: 'public' },
      { key: 3, reference: 'reference', path: '.../public/shared/sub_folder/file_3.txt',
        type: 'public' },
      { key: 4, reference: 'reference', path: '.../public/shared/sub_folder/file_4.txt',
        type: 'public' },
      { key: 5, reference: 'reference', path: '.../log/file_5.txt',
        type: 'log' },
      { key: 6, reference: 'reference', path: '.../tmp/cache/file_6.txt',
        type: 'log' }
    ]
    

    我们现在可以从arr 构造所需的数组:

    doit arr 
      #=> [{:key=>"root", :folder=>true, :title=>"root", :children=>
      #     [{:key=>"public", :folder=>true, :title=>"public", :children=>
      #       [{:key=>"shared", :folder=>true, :title=>"shared", :children=>
      #         [{:key=>1, :title=>"file_1.txt"},
      #          {:key=>2, :title=>"file_2.txt"},
      #          {:key=>"sub_folder", :folder=>true, :title=>"sub_folder",
      #           :children=>[{:key=>3, :title=>"file_3.txt"},
      #                       {:key=>4, :title=>"file_4.txt"}
      #                      ]
      #          }
      #         ]
      #        }
      #       ]
      #      },
      #      {:key=>"log", :folder=>true, :title=>"log",
      #       :children=>[{:key=>5, :title=>"file_5.txt"}]
      #      },
      #      {:key=>"tmp", :folder=>true, :title=>"tmp",
      #       :children=>[{:key=>"cache", :folder=>true, :title=>"cache",
      #                    :children=>[{:key=>6, :title=>"file_6.txt"}]
      #                   }
      #                  ]
      #      }
      #     ]
      #    }
      #   ]
    

    说明

    步骤如下(以arr为例),

    doit

      a = arr.map do |g|
        *front, last = ['root', *g[:path][4..-1].split('/')]
        [front, { key: g[:key], title: last }]
      end
        #=> [[["root", "public", "shared"], {:key=>1, :title=>"file_1.txt"}],
        #    [["root", "public", "shared"], {:key=>2, :title=>"file_2.txt"}],
        #    [["root", "public", "shared", "sub_folder"], {:key=>3, :title=>"file_3.txt"}],
        #    [["root", "public", "shared", "sub_folder"], {:key=>4, :title=>"file_4.txt"}],
        #    [["root", "log"], {:key=>5, :title=>"file_5.txt"}],
        #    [["root", "tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]]
    

    分解后,我们执行以下计算。

    arr 的第一个元素被传递给map's block and becomes the value of the block variableg`:

      g = arr.first
        #=> {:key=>1, :reference=>"reference",
        #    :path=>".../public/shared/file_1.txt", :type=>"public"}
    

    然后执行块计算。

    b = g[:path]
      #=> ".../public/shared/file_1.txt"
    c = b[4..-1]
      #=> "public/shared/file_1.txt"
    d = c.split('/')
      #=> ["public", "shared", "file_1.txt"]
    e = ['root', *d]
      #=> ["root", "public", "shared", "file_1.txt"]
    *front, last = e
      #=> ["root", "public", "shared", "file_1.txt"]
    front
      #=> ["root", "public", "shared"]
    last
      #=> "file_1.txt"
    f = g[:key]
      #=>
    [front, { key: f, title: last }]
      #=> [["root", "public", "shared"], {:key=>1, :title=>"file_1.txt"}]
    

    arr其余元素的映射类似。

    上面的数组a被传递给recurse。第一步是删除 d 为空的所有元素 [d, h]d 是一个目录数组,h 一个哈希)。这是一项技术要求,需要在将一个或多个哈希值添加到 :children 的值的数组中之后进行更深入的递归。

    m = a.reject { |dirs, _| dirs.empty? }
      #=> a (no elements are removed)
    

    下一步是将m 中的元素[dirs, h] 组合成dirs 的第一个元素。我在下面的块中使用了dirs.shift,还从数组dirs 中删除了该元素。

    n = m.group_by { |dirs,_| dirs.shift }
      #=> {"root"=>[ 
      #     [["public", "shared"], {:key=>1, :title=>"file_1.txt"}],
      #     [["public", "shared"], {:key=>2, :title=>"file_2.txt"}],
      #     [["public", "shared", "sub_folder"], {:key=>3, :title=>"file_3.txt"}],
      #     [["public", "shared", "sub_folder"], {:key=>4, :title=>"file_4.txt"}],
      #     [["log"], {:key=>5, :title=>"file_5.txt"}],
      #     [["tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]
      #    ]
      #   }
    

    n 的第一个元素现在被传递给map 的块并分配块变量:

    dir, v = n.first
      #=> ["root", [
      #      [["public", "shared"], {:key=>1, :title=>"file_1.txt"}],
      #      [["public", "shared"], {:key=>2, :title=>"file_2.txt"}], 
      #      [["public", "shared", "sub_folder"], {:key=>3, :title=>"file_3.txt"}],
      #      [["public", "shared", "sub_folder"], {:key=>4, :title=>"file_4.txt"}],
      #      [["log"], {:key=>5, :title=>"file_5.txt"}],
      #      [["tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]
      #    ]
      #   ]
    dir
      #=> "root"
    v #=> [[["public", "shared"], {:key=>1, :title=>"file_1.txt"}],
      #    ...
      #    [["tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]
      #   ]
    

    然后执行块计算。

      empty, non_empty =  v.partition { |d,_| d.empty? }
      empty
        #=> []
      non_empty
        #=> [[["public", "shared"], {:key=>1, :title=>"file_1.txt"}],
        #    [["public", "shared"], {:key=>2, :title=>"file_2.txt"}], 
        #    [["public", "shared", "sub_folder"], {:key=>3, :title=>"file_3.txt"}],
        #    [["public", "shared", "sub_folder"], {:key=>4, :title=>"file_4.txt"}],
        #    [["log"], {:key=>5, :title=>"file_5.txt"}],
        #    [["tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]
        #   ]
      p = empty.map(&:last)
        #=> []
      { key: dir, folder: true, title: dir,
        children: [*p, *recurse(non_empty)] }
        #=> { key: 'root', folder: true, title: 'root',
        #     children: [*[], *recurse(non_empty)] }
    

    最后一个键:children 的值减少到[*recurse(non_empty)]。如图所示,recurse 现在被递归调用,参数为non-empty

    其余的计算是相似的,但是当 recurse 被传递一个具有一个或多个元素的数组时,情况会有所不同,而 dirs 数组包含一个元素,导致关联的哈希被添加到一个数组中是键 :children 的值。要完全理解计算,可能需要在代码中添加一些puts 语句。

    【讨论】:

      猜你喜欢
      • 2017-03-28
      • 2013-11-11
      • 2015-05-12
      • 2015-12-10
      • 2017-04-30
      • 1970-01-01
      • 1970-01-01
      • 2021-03-26
      • 2019-07-14
      相关资源
      最近更新 更多