【问题标题】:iterate through nested hashes using conditionals in Ruby使用 Ruby 中的条件遍历嵌套哈希
【发布时间】:2016-01-18 16:38:00
【问题描述】:

我正在尝试从这个哈希构建一个 new_hash:

languages = {
  :oo => {
    :ruby => {
      :type => "interpreted"
    },
    :javascript => {
      :type => "interpreted"
    },
    :python => {
      :type => "interpreted"
    }  
  },
  :functional => {
    :clojure => {
      :type => "compiled"
    },
    :erlang => {
      :type => "compiled"
    },
    :javascript => {
      :type => "interpreted"
    } 
  }
}

而期望的结果是:

{
  :ruby => {
    :type => "interpreted",
    :style => [:oo]
  },
  :javascript => {
    :type => "interpreted",
    :style => [:oo, :functional]
  },
  :python => {
    :type => "interpreted",
    :style => [:oo]
  },
  :clojure => {
    :type => "compiled",
    :style => [:functional]
  },
  :erlang => {
    :type => "compiled",
    :style => [:functional]
  }  
}

这是我到目前为止所做的:

def reformat_languages(languages)
  new_hash = {}
  languages.each do |k, v|
    v.each do |k1, v1|
      new_hash[k1] = v1
      new_hash[k1][:style] = []
      new_hash[k1][:style] << k     
    end
  end
  new_hash
end

很遗憾,我无法得到想要的结果。我知道当迭代到达第二个 javascript 键时,它会重写第一个迭代给我:

 :javascript => {
    :type => "interpreted",
    :style => [:functional]
  }

代替:

:javascript => {
    :type => "interpreted",
    :style => [:oo, :functional]
  }

这是一个 repl.it 的链接,您可以在其中看到正在运行的代码:https://repl.it/BebC

我知道我需要使用条件,但我不确定在哪里以及在什么情况下使用它。如果有人可以帮助我获得所需的结果并解释一下为什么它会以这种方式工作。

【问题讨论】:

    标签: ruby hash


    【解决方案1】:

    你可以使用类似的东西

    h = {}
    languages.each do |k, v| # oo or func
        v.each do |k1, v1| # ruby/python
            if h[k1]
                h[k1][:style] << k
            else
                h[k1] = {type: v1[:type], style: [k]}
            end
        end
    end
    

    它检查h 是否已定义,如果是,则附加到其数组中。否则,它将使用您的类型和大小为 1 的样式数组定义整个哈希。

    【讨论】:

    • 良好的命名在这里也会有很大帮助,但他会及时了解这一点。 :)
    【解决方案2】:

    您的代码中有太多的无条件覆盖。应该是这样的:

      new_hash[k1] ||= {} # init to empty hash
      new_hash[k1][:type] = v1[:type]
      new_hash[k1][:style] ||= [] # make sure array exists
      new_hash[k1][:style] << k
    

    您应该更改它的各个部分,而不是替换整个 new_hash[k1]

    【讨论】:

      【解决方案3】:

      这不是答案(所以请不要投票)。相反,它是一个扩展注释,可帮助您理解@Martin 建议的代码。 (我发现您是 SO 新手,很可能也是 Ruby 新手。)正如我所做的那样,使用 puts 语句对代码进行盐渍化通常很有帮助,即使在您熟悉该语言之后也是如此。

      languages = {
        :oo => {
          :ruby => {
            :type => "interpreted"
          },
          :javascript => {
            :type => "interpreted"
          }  
        },
        :functional => {
          :clojure => {
            :type => "compiled"
          },
          :javascript => {
            :type => "interpreted"
          } 
        }
      }
      
      h = {}
      languages.each do |k, v| # oo or func
        puts "k=#{k}, v=#{v}"
        v.each do |k1, v1| # ruby/python
          puts "  k1=#{k1}, v1=#{v1}"
          if h[k1]
            puts "  h[#{k1}]=#{h[k1]} (truthy)"
            h[k1][:style] << k
            puts "  h after h[#{k1}][:style] << #{k}: #{h}"
          else
            puts "  h[#{k1}].nil?=true (falsy)"
            h[k1] = {type: v1[:type], style: [k]}
            puts "  h after h[#{k1}] = {type: v1[:type], style: #{k}}: #{h}"
          end
        end
      end
      

      打印:

      k=oo, v={:ruby=>{:type=>"interpreted"}, :javascript=>{:type=>"interpreted"}}
      
        k1=ruby, v1={:type=>"interpreted"}
        h[ruby].nil?=true (falsy)
        h after h[ruby] = {type: v1[:type], :style: oo}:
          {:ruby=>{:type=>"interpreted", :style=>[:oo]}}
      
        k1=javascript, v1={:type=>"interpreted"}
        h[javascript].nil?=true (falsy)
        h after h[javascript] = {type: v1[:type], :style: oo}:
          {:ruby=>{:type=>"interpreted", :style=>[:oo]},
           :javascript=>{:type=>"interpreted", :style=>[:oo]}}
      
      k=functional, v={:clojure=>{:type=>"compiled"}, :javascript=>{:type=>"interpreted"}}
      
        k1=clojure, v1={:type=>"compiled"}
        h[clojure].nil?=true (falsy)
        h after h[clojure] = {type: v1[:type], :style: functional}:
          {:ruby=>{:type=>"interpreted", :style=>[:oo]},
           :javascript=>{:type=>"interpreted", :style=>[:oo]},
           :clojure=>{:type=>"compiled", :style=>[:functional]}}
      
        k1=javascript, v1={:type=>"interpreted"}
        h[javascript]={:type=>"interpreted", :style=>[:oo]} (truthy)
        h after h[javascript][:style] << functional:
          {:ruby=>{:type=>"interpreted", :style=>[:oo]},
           :javascript=>{:type=>"interpreted", :style=>[:oo, :functional]},
           :clojure=>{:type=>"compiled", :style=>[:functional]}}
      

      然后返回:

        #=> {:oo       =>{:ruby=>{:type=>"interpreted"},
        #                 :javascript=>{:type=>"interpreted"}},
        #   :functional=>{:clojure=>{:type=>"compiled"},
        #                 :javascript=>{:type=>"interpreted"}}} 
      

      【讨论】:

      • 谢谢你,卡里。这对我来说非常有价值。
      【解决方案4】:

      您正在覆盖生成的哈希值,这会导致您提到的意外行为。以下代码可以满足您的需要。它只是您的代码的略微修改版本。

      def reformat_languages(languages)
        new_hash = {}
        languages.each do |k, v|
          v.each do |k1, v1|
            new_hash[k1] ||= v1 #ensures we do not overwrite the already generated language hash
            new_hash[k1][:style] ||= [] #protecting against re-initialization of the style array
            new_hash[k1][:style] << k     
          end
        end
        new_hash
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-03
        • 2022-12-11
        • 2013-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多