我正在尝试解析这样的yaml文件:
a:
a1:
a2:
b:
b1:
b11:
b2:
我得到这样的哈希:
{"a"=>{"a1"=>nil, "a2"=>nil}, "b"=>{"b1"=>{"b11"=>nil}, "b2"=>nil}}
我想把它变成一个列表:
%ul
%li a
%ul
%li a1
%li a2
%li b
%ul
%li b1
%ul
%li b11
%li b2
我正在尝试以最有效的方式搜索并不重要哈希的深度
最后我这样做了:
KeyWords = %w(url)
# Convert a multilevel hash into haml multilevel tree
# Special KeyWords
# url : item url
def hash_to_haml(hash, url = nil)
haml_tag(:ul) do
hash.each do |key, value|
unless KeyWords.include?(key)
url = get_url(key, value)
haml_tag(:li) do
haml_tag(:a, :href => url ) do
haml_concat(key)
end
hash_to_haml(value) if value.is_a?(Hash) && !value.empty?
end
end
end
end
end
private
def get_url(key, hash)
# TODO: get full url from hash
if hash.nil?
"/#{key}"
else
hash.include?("url") ? hash.delete("url") : "/#{key}"
end
end
现在也准备解析选项了。
答案 0 :(得分:4)
要输出到纯HTML,您只需在each
块中执行相同函数的递归调用(或使用函数作为 each
块,因为我有在这里完成):
def hash_to_html key,value
if value.nil?
puts "<li>#{key}</li>"
elsif value.is_a?(Hash)
puts "<li>#{key}"
puts "<ul>"
value.each(&method(:hash_to_html))
puts "</ul></li>"
else
fail "I don't know what to do with a #{value.class}"
end
end
puts "<ul>"
yourhash.each(&method(:hash_to_html))
puts "</ul>"
要输出你正在使用的任何模板语言(HAML,我认为),我们需要跟踪缩进,所以事情稍微复杂一点 - 我们将使用一个采用缩进深度的函数作为参数,并返回另一个要在每个键/值对上调用的函数,该键/值对使用适当的缩进打印该键/值对(递归)。 (在函数式编程中,以这种方式调用函数称为“部分应用函数”,它们通常比在Ruy中更容易定义。)
def hash_to_haml depth
lambda do |key,value|
puts " "*depth + "%li #{key}"
if value.nil?
# do nothing
# (single this case out, so as not to raise an error here)
elsif value.is_a?(Hash)
puts " "*(depth+1) + "%ul"
value.each(&hash_to_haml(depth+2))
else
fail "I don't know what to do with a #{value.class}"
end
end
end
puts "%ul"
yourhash.each(&hash_to_haml(1))
答案 1 :(得分:3)
我做过这个:
INDENT = ' ' # use 2 spaces for indentation
def hash_to_haml(hash, level=0)
result = [ "#{INDENT * level}%ul" ]
hash.each do |key,value|
result << "#{INDENT * (level + 1)}%li #{key}"
result << hash_to_haml(value, level + 2) if value.is_a?(Hash)
end
result.join("\n")
end
用法:
hash = {"a"=>{"a1"=>nil, "a2"=>nil}, "b"=>{"b1"=>{"b11"=>nil}, "b2"=>nil}
string = hash_to_haml(hash)
puts string
输出:
%ul
%li a
%ul
%li a1
%li a2
%li b
%ul
%li b1
%ul
%li b11
%li b2
编辑 - 澄清:
string * n
重复string
n
次。array.join
比创建一堆字符串并使用+
加入它们更有效。答案 2 :(得分:2)
require 'yaml'
yaml = <<EOS
a:
a1:
a2:
b:
b1:
b11:
b2:
EOS
# unless using ruby 1.9 make sure you use an ordered hash such as ActiveSupport::OrderedHash
hash = YAML::load yaml
def hash_to_haml(hash, indent)
puts " " * indent + "%ul"
indent += 1
hash.each do |key, value|
puts " " * indent + "%li " + key
hash_to_haml(value, indent + 1) if value.is_a? Hash
end
end
#start with 0 indent
hash_to_haml(hash, 0)
答案 3 :(得分:1)
这是一个同时兼顾HTML和Haml的解决方案。略显冗长,但又易读。
class ListMaker
def initialize(hash)
@hash = hash
@indent = " "
@level = 0
@out = []
end
def append(tag,value=nil)
str = @indent * @level + "#{tag}"
str += @tag_space + value unless value.nil?
str += "\n"
@out << str
end
def ul(hash)
open_tag('ul') { li(hash) }
end
def li(hash)
@level += 1
hash.each do |key,value|
open_tag('li',key) { ul(value) if value.is_a?(Hash) }
end
@level -= 1
end
def list
ul(@hash)
@out.join
end
end
class HtmlListMaker < ListMaker
def initialize(hash)
super
@tag_space = ""
end
def open_tag(tag,value=nil,&block)
append("<#{tag}>",value)
yield if block_given?
append("</#{tag}>")
end
end
class HamlListMaker < ListMaker
def initialize(hash)
super
@tag_space = " "
end
def open_tag(tag,value=nil,&block)
append("%#{tag}",value)
yield if block_given?
end
end
require 'yaml'
yaml = <<EOS
a:
a1:
a2:
b:
b1:
b11:
b2:
EOS
hash = YAML.load(yaml) # {"a"=>{"a1"=>nil, "a2"=>nil}, "b"=>{"b1"=>{"b11"=>nil}, "b2"=>nil}}
puts HamlListMaker.new(hash).list
# %ul
# %li a
# %ul
# %li a1
# %li a2
# %li b
# %ul
# %li b1
# %ul
# %li b11
# %li b2
puts HtmlListMaker.new(hash).list
# <ul>
# <li>a
# <ul>
# <li>a1
# </li>
# <li>a2
# </li>
# </ul>
# </li>
# <li>b
# <ul>
# <li>b1
# <ul>
# <li>b11
# </li>
# </ul>
# </li>
# <li>b2
# </li>
# </ul>
# </li>
# </ul>