【问题标题】:turn a ruby hash into html list将 ruby​​ 哈希转换为 html 列表
【发布时间】:2011-01-03 20:43:25
【问题描述】:

我正在尝试解析这样的 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

现在也准备解析选项。

【问题讨论】:

  • 你能修正你的问题的格式吗?
  • 这对于代码 kata 来说是一个非常好的问题。

标签: ruby-on-rails ruby hash


【解决方案1】:

要输出到纯 HTML,您只需在 each 块内执行同一函数的递归调用(或使用函数 as 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))

【讨论】:

  • 相当不错,但要使其正常工作,我需要将前 3 行修改为: def hash_to_html(map) key = map[0] value = map[1]
  • 我建议返回值而不是执行 put。这样该函数就可以在其他地方使用(而不仅仅是在控制台上)。
  • @JAlberto:我很困惑:map[0]map[1] 在您提供的示例数据中都会返回 nil。
  • @egarcia:是的,你可以这样做。我认为如果适合他的风格,OP 可以很容易地做出改变。
  • @KenBloom 它在这里工作(ruby 1.9),使用示例哈希并使用您的代码执行 map[0] map[1] 我得到一个:ArgumentError: wrong number of arguments (1 for 2) 要么使用每个或地图
【解决方案2】:

我已经完成了这个:

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 比创建一堆字符串并使用+ 连接它们更有效。

【讨论】:

  • 很好,但是这个解决方案的问题是如果你通过HAML::Engine处理生成的输出,它会抱怨非法嵌套:content can't be both given on the same line as %li and nested within it
【解决方案3】:
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)

【讨论】:

  • 请参阅 Nakilon 关于 1 字母变量名称的答案中的 cmets。
【解决方案4】:

这是一个同时处理 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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-08
    • 1970-01-01
    • 2021-01-17
    • 2011-06-29
    • 2016-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多