【问题标题】:How to get the desired formatted hash from a string in an efficient way with Ruby?如何使用 Ruby 以有效的方式从字符串中获取所需的格式化哈希?
【发布时间】:2023-03-27 10:32:01
【问题描述】:

当字符串以以下格式出现时,我想以特定格式散列:

给定字符串:

str = 'A
A = B
A = B = C
A = B = D
A = E = F
G = H
G = I
G = J'

# 变成这样的哈希(必需的哈希模式):

{
  "A" => {
    "B" => {
      "C" => nil,
      "D" => nil
    },
    "E" => {
      "F" => nil
    },
  },
  "G" => {
     "H" => nil,
     "I" => nil,
     "J" => nil
  }
}

我尝试了很多方法,但这是最接近的:

output = Hash.new
line_hash = Hash.new
str.each_line do |line|
  arr = line.split("=")
  e = arr.first.strip
  line_hash[e] = {}
  arr.each_with_index do |ele, i|
    break unless arr[i+1]
    line_hash[ele.strip] = arr[i+1] unless output.keys.include?(ele.strip)
  end
  output[e] = line_hash unless output.keys.include?(e)
end

【问题讨论】:

    标签: ruby string hash


    【解决方案1】:
    str = "A\nA = B\nA = B = C\nA = B = D\nA = E = F\nG = H\nG = I\nG = J"
    
    curr = h = {}
    
    str.each_line { |l| 
      l.chomp.split(/\s*=\s*/m).each { |c| 
        curr = curr[c] ||= {}; 
      }
      curr = h 
    }
    
    puts h
    # => {
    #  "A" => {
    #    "B" => {
    #      "C" => {},
    #      "D" => {}
    #    },
    #    "E" => {
    #      "F" => {}
    #    }
    #  },
    #  "G" => {
    #    "H" => {},
    #    "I" => {},
    #    "J" => {}
    #  }
    # }
    

    为了解决方案的清晰性,我希望你能原谅我留下空哈希而不是空值。

    使叶子无效:

    def leaves_nil! hash
       hash.each { |k,v| v.empty? ? hash[k] = nil : leaves_nil!(hash[k]) }  
    end
    

    【讨论】:

    • 感谢您的回答。我会尝试改为 null。
    【解决方案2】:

    你也可以通过这样的方式获得输出

    str = 'A
    A = B
    A = B = C
    A = B = D
    A = E = F
    G = H
    G = I
    G = J'
    
    curr = h = {}
    lines = str.split("\n").map{|t| t.split(/\s*=\s*/m) }
    lines.each do |line| 
      line.each { |c| curr = curr[c.strip] = curr[c.strip] ||  ((line.last == c) ? nil : {});  }
      curr = h
    end
    

    输出

    #=>  {
    #        "A" => {
    #            "B" => {
    #                "C" => nil,
    #                "D" => nil
    #            }, "E" => {
    #                "F" => nil
    #            }
    #        }, "G" => {
    #            "H" => nil,
    #            "I" => nil,
    #            "J" => nil
    #        }
    #    }
    

    【讨论】:

    • 你确定你的意思是= curr[c.strip] = curr[c.strip]
    【解决方案3】:

    这是另一种需要较少数据来构建散列的方法。例如,如果行

    A = B = C = D
    

    存在,不需要以下任何一种:

    A = B
    A = B = C
    

    行的顺序并不重要。

    代码

    def hashify(str)
      str.lines.each_with_object({}) { |line, h|
        line.split(/\s*=\s*/).reduce(h) { |g,w|
          (w[-1] == "\n") ? g[w.chomp] = nil : g[w] ||= {} } }
    end
    

    示例

    str =<<_
    A = B = C
    G = I
    A = B = D
    A = E = F
    G = H
    A = K
    G = J
    _
    
    hashify(str)
      #=> {"A"=>{"B"=>{"C"=>nil, "D"=>nil}, "E"=>{"F"=>nil}, "K"=>nil},
      #    "G"=>{"I"=>nil, "H"=>nil, "J"=>nil}} 
    

    说明

    以上str

    a = str.lines
      #=> ["A = B = C\n", "A = B = D\n", "A = E = F\n",
      #    "G = H\n", "G = I\n", "G = J\n"]
    

    请注意,String#linessplit(/'\n'/) 不同,它保留换行符。将它们保留在这一点上是有意的。它们有一个重要的用途,如下所示。

    enum = a.each_with_object({})
      #=> #<Enumerator: ["A = B = C\n", "A = B = D\n", "A = E = F\n", "G = H\n",
      #                  "G = I\n", "G = J\n"]:each_with_object({})>
    

    我们可以将枚举器转换为数组,以查看Array#each 将传递给块的元素:

    enum.to_a
      #=> [["A = B = C\n", {}], ["A = B = D\n", {}], ["A = E = F\n", {}],
      #    ["G = H\n", {}], ["G = I\n", {}], ["G = J\n", {}]]
    

    enum 现在调用 each 将每个元素传递到块中:

    enum.each { |line, h| line.split(/\s*=\s*/).reduce(h) { |g,w|
                (w[-1] == '\n') ? g[w.chomp] = nil : g[w] ||= {} } }
      #=> {"A"=>{"B"=>{"C\n"=>{}, "D\n"=>{}}, "E"=>{"F\n"=>{}}},
      #    "G"=>{"H\n"=>{}, "I\n"=>{}, "J\n"=>{}}}
    

    Array#each 传入块的第一个值是:

    ["A = B = C\n", {}]
    

    将其分解或“消除歧义”为两个元素并分配给块变量:

    line = "A = B = C\n"
    h = {}
    

    我们现在执行块中的代码:

    b = line.split(/\s*=\s*/)
      #=> ["A", "B", "C\n"]
    b.reduce(h) { |g,w|
      (w[-1] == '\n') ? g[w.chomp] = nil : g[w] ||= {} }
      #=> {}
    

    reduce 的初始值是我们正在构建的哈希 h,最初是空的。当h"A" 被传入块时,

    g = h #=> {}
    w = "A"
    

    所以(注意"\n" 需要双引号)

    w[-1] == "\n"
      #=> "A" == '\n'
      #=> false
    

    所以我们执行

    g[w] ||= {}
      #=> g['A'] ||= {} 
      #=> g['A'] = g['A'] || {}
      #=> g['A'] = nil || {}
      #=> {}
    

    现在

    h #=> {"A"=>{}}
    

    g[w] =&gt; {} 然后被传回reduce 并且传递给块的第二个元素的块变量是:

    g = g["A"] #=> {}
    w = "B"
    

    自从

    w[-1] == "\n" #=> false
    

    我们再次执行

    g[w] ||= {}
     #=> g["B"] ||=> {} => {}
    

    现在

    h #=> {"A"=>{"B"=>{}}}
    

    最后,[g["B"], "C\n"] 被传递到块中,分解并分配给块变量:

    g = g["B"] #=> {}
    w = "C\n"
    

    w 中出现换行符会导致

    w[-1] == "\n" #=> true
    

    告诉我们这是行中的最后一个单词,所以我们需要去掉换行符并将值设置为nil

    g[w.chomp] = nil
      #=> g["C"] = nil 
    

    导致:

    h #=> {"A"=>{"B"=>{"C"=>nil}}}
    

    在字符串中保留换行符提供了所需的“标志”,用于以不同于其他方式处理每行上的最后一个单词。

    其他行类似处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-18
      • 2015-09-18
      • 1970-01-01
      • 2021-12-22
      • 2012-10-31
      • 2017-09-20
      • 2013-08-28
      • 1970-01-01
      相关资源
      最近更新 更多