Search code examples
ruby-on-railsrubydynatree

From a list of files, dynamically create array of hashes for dynatree


I have a list of files sorted in an array, like:

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

How to create a dynatree object out of this? A dynatree object for it, should look like:

[{"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: This question might seem a lazy post, but I have spent more than 20 hrs now solving this. So any help will be greatly appreciated. Thanks.


Solution

  • My idea is to iterate over each paths. Each path is split by / into parts. The last part is the file, others - directories.

    I go through each part adding new directories unless they were already added. In the end of each iteration I switch the context to the next level - children array of the last directory.

    File is the last part - I simple append it to the current context.

    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