【问题标题】:How to replace all hash keys having a '.'?如何替换所有具有“。”的哈希键?
【发布时间】:2013-12-13 15:15:36
【问题描述】:

我正在使用 Ruby on Rails 4,我想替换所有哈希键以便更改哈希

h_before = {:"aaa.bbb" => 1, :c => 2, ...}

h_after = {:bbb => 1, :c => 2, ...}

也就是说,我想以某种方式“解调”所有具有. 的哈希键。我怎样才能做到这一点?

【问题讨论】:

  • 请展示您为回答此问题而编写的代码。我们最好使用您已经开始的东西,而不是我们从头开始然后您将其硬塞到位。
  • 如果这是Code Golf,我建议eval(h_before.gsub("aaa.",""))

标签: ruby hash


【解决方案1】:

each_with_object 是一种比从答案中注入更清洁、更短的方法:

h_before.each_with_object({}){|(k, v),h| h[k.to_s.split(".").last.to_sym] = v}
=> {:bbb=>1, :c=>2}

【讨论】:

  • 我喜欢这个each_with_object 一个.. 所以+1
【解决方案2】:
h_before = {:"aaa.bbb" => 1, :c => 2}
h_after =
h_before.inject({}){|h, (k, v)| h[k.to_s.split(".").last.to_sym] = v; h}
# => {:bbb = > 1, :c => 2}

【讨论】:

  • 您能更改(显式)名称以便我了解什么是什么吗?
  • 你是对的,但是,例如,我不明白h 指的是h.keys.each{|k| h[k.to_s.split(".").last.to_sym] = h.delete(k)} 中的内容。 h 指的是重复的哈希?在哪里?
  • 最后用each_with_object 而不是inject 来摆脱那个讨厌的;h 怎么样?
  • 我认为each_with_object 已被弃用。但是任何解决方案都应该使用HashWithIndiferenceAccess;这是一个带有 Rails 标记的问题...
  • @Philip,重新弃用,参见this,尤其是sawa 和chuck 的答案。
【解决方案3】:

由于有很多答案声称做同样的事情,我认为是时候发布一些基准了:

require 'fruity'

h_before = {:"aaa.bbb" => 1, :c => 2}

def cdub_test(hash)
  Hash[hash.map {|k, v| [k.to_s.gsub(/^.*\./,"").to_sym, v]}]
end

def matt_test(old_hash)
  Hash[old_hash.map { |k,v| [ k.to_s.sub(/.*\./,'').to_sym, v ] }]
end

class Hash
  require 'active_support/core_ext/hash/indifferent_access'
  def grep_keys(pattern)
    return inject(HashWithIndifferentAccess.new){|h, (k, v)|
      h[$1 || k] = v  if pattern =~ k.to_s ;  h }
  end
end

def phlip_test(hash)
  hash.grep_keys(/\.(\w+)$/)
end

def bjhaid_test(hash)
  hash.each_with_object({}){|(k, v),h| h[k.to_s.split(".").last.to_sym] = v}
end

def sawa_test(hash)
  hash.inject({}){|h, (k, v)| h[k.to_s.split(".").last.to_sym] = v; h}
end

compare do
    cdub   { cdub_test(h_before)   }
    matt   { matt_test(h_before)   }
    phlip  { phlip_test(h_before)  }
    bjhaid { bjhaid_test(h_before) }
    sawa   { sawa_test(h_before)   }
end

哪些输出:

运行每个测试 1024 次。测试大约需要 1 秒。 bjhaid 类似于 sawa sawa 比 matt 快 60.00000000000001% ± 10.0% matt 比 phlip 快 30.000000000000004% ± 10.0%(结果不同:{:bbb=>1, :c=>2} vs {"bbb"=>1}) phlip 类似于 cdub(结果不同:{"bbb"=>1} vs {:bbb=>1, :c=>2})

请注意,phlip 的代码没有返回所需的结果。

【讨论】:

    【解决方案4】:
    old_hash = {:"aaa.bbb" => 1, :c => 2 }
    new_hash = Hash[old_hash.map { |k,v| [ k.to_s.sub(/.*\./,'').to_sym, v ] }]
    

    【讨论】:

      【解决方案5】:
      1.9.3p448 :001 > hash = {:"aaa.bbb" => 1, :c => 2 }
       => {:"aaa.bbb"=>1, :c=>2} 
      
      1.9.3p448 :002 > Hash[hash.map {|k, v| [k.to_s.gsub(/^.*\./,"").to_sym, v]}]
       => {:bbb=>1, :c=>2} 
      

      【讨论】:

      • 你能不能写一个RegEx,只从:"aaa.bbb"中提取"bbb"。然后代码可以写成Hash[hash.map {|k, v| [k.to_s[/your regex/].to_sym, v]}]
      • 我要求你这样做,因为我在 RegEx 的一周时间太长了。 :)
      • 我觉得有些讽刺......上面gsub中的正则表达式将处理gsub....../^.*\./
      • 也就是说,如果你是认真的......你的答案的正则表达式将是/(?<=\.).*/ ... hash.keys.first.to_s[/(?<=\.).*/] => "bbb"
      • sub 应该被使用而不是gsub。第一个只会进行一次搜索和替换,不像gsub 会尝试查找其他匹配项来替换。对于短字符串来说,这是一个很小的差异,但随着时间的推移确实会增加。对于长字符串,这可能会有很大的不同。
      【解决方案6】:

      我的grep_keys 从来没有让我失望过:

      class Hash
        def grep_keys(pattern)
          return inject(HashWithIndifferentAccess.new){|h, (k, v)| 
            h[$1 || k] = v  if pattern =~ k.to_s ;  h }
        end
      end
      

      它返回哈希的浅拷贝,但仅使用匹配的键。如果输入正则表达式包含() 匹配,则该方法将键替换为匹配的值。 (请注意,这可能会合并两个或多个键,并丢弃除随机值之外的所有键!)我一直使用它来将 Rails param 分割成仅包含某些模块所需键的子参数。

      {:"aaa.bbb" => 1, :c => 2 }.grep_keys(/\.(\w+)$/) 返回{"bbb"=>1}

      此方法将您的实际问题升级为“如何定义一个与您的意思相匹配的正则表达式'有一个'。'。'”

      【讨论】:

      • 您可能希望将您的结果与 OP 想要的结果进行比较。
      猜你喜欢
      • 2010-12-02
      • 2013-03-05
      • 2016-02-24
      • 1970-01-01
      • 1970-01-01
      • 2011-08-22
      • 1970-01-01
      • 2013-07-03
      • 2011-08-23
      相关资源
      最近更新 更多