在Ruby中将深度第一个遍历节点列表转换回树结构

时间:2013-03-18 23:16:03

标签: ruby algorithm

给出以下输入(来自CSV文件):

input = [
  { :level => 0, :value => "a"   },
  { :level => 1, :value => "1"   },
  { :level => 1, :value => "2"   },
  { :level => 2, :value => "I"   },
  { :level => 2, :value => "II"  },
  { :level => 2, :value => "III" },
  { :level => 0, :value => "b"   },
  { :level => 0, :value => "c"   },
  { :level => 0, :value => "d"   },
  { :level => 1, :value => "3"   },
  { :level => 1, :value => "4"   },
]

如何在“The Ruby Way”中将其转换为以下内容:

expected = [
  { :value => "a", :children => [ { :value => 1, :children => nil },
                                  { :value => 2, :children => [ { :value => "I", :children => nil },
                                                                { :value => "II", :children => nil },
                                                                { :value => "III", :children => nil } ] } ] },
  { :value => "b", :children => nil },
  { :value => "c", :children => nil },
  { :value => "d", :children => [ { :value => 3, :children => nil },
                                  { :value => 4, :children => nil } ] },
  ]

编辑:

我对此的解决方法是回避问题,转换它并让其他人解决它:

require 'yaml'
def linear_to_tree(a)
  yaml_lines = []

  a.each do |el|
    indent = " " * 4 * el[:level]
    yaml_lines << "#{indent}-"
    yaml_lines << "#{indent}  :value: #{(el[:value])}"
    yaml_lines << "#{indent}  :children:"
  end
  yaml_lines << ""  # without this, YAML.load complains
  yaml = yaml_lines.join("\n")
  # open("test_yaml.txt", "w"){|f| f.write(yaml)}
  YAML.load(yaml)
end

但必须有更优雅的方法来解决这个问题。

P.S。我也希望看到这种转变的单线,只是为了看看它是否可能。

1 个答案:

答案 0 :(得分:0)

对于没有子节点的节点,您应该使用空数组,空数组是集合的空对象。否则,在分配它和使用它时,你必须围绕它们跳舞。

def transform(inputs)
  transform! inputs.dup
end

def transform!(inputs, output=[], current_level=0)
  while inputs.any?
    input = inputs.shift
    level, value = input.values_at :level, :value
    value = value.to_i if value =~ /\A\d+\z/
    if level < current_level
      inputs.unshift input
      break
    elsif level == current_level
      next_children = []
      output << {value: value, children: next_children}
      transform! inputs, next_children, current_level.next
    else
      raise "presumably should not have gotten here"
    end
  end
  output
end