Ruby:最简单的方法来过滤哈希键?

我有一个哈希,看起来像这样:

params = { :irrelevant => "A String", :choice1 => "Oh look, another one", :choice2 => "Even more strings", :choice3 => "But wait", :irrelevant2 => "The last string" } 

我想要一个简单的方法来拒绝所有不是选项+ int的键。 它可能是choice1,或者choice1到choice10。 它有所不同。

我如何select单词select和一个数字或数字之后?

奖金:

将哈希变成带有制表符(\ t)的string作为分隔符。 我这样做了,但是它花了几行代码。 通常,大师级的Rubists可以做到这一点。

注意:如果您使用的是Rails,可以使用下面的答案中的Hash.slice指出的Hash.slice。

如果您使用的是Ruby,则可以使用select方法。 您需要将密钥从符号转换为string来执行正则expression式匹配。 这将给你一个新的哈希只是在它的select。

 choices = params.select { |key, value| key.to_s.match(/^choice\d+/) } 

或者你可以使用delete_if和修改现有的哈希例如

 params.delete_if { |key, value| !key.to_s.match(/choice\d+/) } 

或者如果它只是键而不是你想要的值,那么你可以这样做:

 params.keys.select { |key| key.to_s.match(/^choice\d+/) } 

这将只给出一个数组的键[:choice1, :choice2, :choice3]

在Ruby中,Hash#select是一个正确的选项。 如果你使用Rails,你可以使用Hash#slice和Hash#slice !. 例如(导轨3.2.13)

 h1 = {:a => 1, :b => 2, :c => 3, :d => 4} h1.slice(:a, :b) # return {:a=>1, :b=>2}, but h1 is not changed h2 = h1.slice!(:a, :b) # h1 = {:a=>1, :b=>2}, h2 = {:c => 3, :d => 4} 

最简单的方法是包括gem 'activesupport' (或gem 'active_support' )。

那么,在你的课堂上,你只需要

 require 'active_support/core_ext/hash/slice' 

并致电

 params.slice(:choice1, :choice2, :choice3) # => {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"} 

我认为不值得声明其他可能存在缺陷的函数,最好使用在过去几年中调整过的方法。

最简单的方法是包括gem“有效支持”(或gem'active_support')。

params.slice(:choice1, :choice2, :choice3)

这是解决完整原始问题的一条线:

 params.select { |k,_| k[/choice/]}.values.join('\t') 

但上面的大多数解决scheme正在解决一个案例,你需要提前知道密钥,使用slice或简单的正则expression式。

这是另一种适用于简单和更复杂的用例的方法,可以在运行时交换

 data = {} matcher = ->(key,value) { COMPLEX LOGIC HERE } data.select(&matcher) 

现在,不仅可以在匹配键或值时使用更复杂的逻辑,而且testing也更容易,并且可以在运行时交换匹配逻辑。

Ex解决原来的问题:

 def some_method(hash, matcher) hash.select(&matcher).values.join('\t') end params = { :irrelevant => "A String", :choice1 => "Oh look, another one", :choice2 => "Even more strings", :choice3 => "But wait", :irrelevant2 => "The last string" } some_method(params, ->(k,_) { k[/choice/]}) # => "Oh look, another one\\tEven more strings\\tBut wait" some_method(params, ->(_,v) { v[/string/]}) # => "Even more strings\\tThe last string" 

如果你想要剩下的哈希:

 params.delete_if {|k, v| ! k.match(/choice[0-9]+/)} 

或者如果你只是想要的键:

 params.keys.delete_if {|k| ! k.match(/choice[0-9]+/)} 

把它放在初始化程序中

 class Hash def filter(*args) return nil if args.try(:empty?) if args.size == 1 args[0] = args[0].to_s if args[0].is_a?(Symbol) self.select {|key| key.to_s.match(args.first) } else self.select {|key| args.include?(key)} end end end 

那你可以做

 {a: "1", b: "b", c: "c", d: "d"}.filter(:a, :b) # => {a: "1", b: "b"} 

要么

 {a: "1", b: "b", c: "c", d: "d"}.filter(/^a/) # => {a: "1"} 
 params.select{ |k,v| k =~ /choice\d/ }.map{ |k,v| v}.join("\t") 

Hash::select

 params = params.select { |key, value| /^choice\d+$/.match(key.to_s) } 

至于奖金问题:

  1. 如果你有像这样的#select方法的输出(2元素数组列表):

     [[:choice1, "Oh look, another one"], [:choice2, "Even more strings"], [:choice3, "But wait"]] 

    然后简单地采取这个结果并且执行:

     filtered_params.join("\t") # or if you want only values instead of pairs key-value filtered_params.map(&:last).join("\t") 
  2. 如果你有像这样(散列) #delete_if方法的输出:

     {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"} 

    然后:

     filtered_params.to_a.join("\t") # or filtered_params.values.join("\t") 
 params = { :irrelevant => "A String", :choice1 => "Oh look, another one", :choice2 => "Even more strings", :choice3 => "But wait", :irrelevant2 => "The last string" } choices = params.select { |key, value| key.to_s[/^choice\d+/] } #=> {:choice1=>"Oh look, another one", :choice2=>"Even more strings", :choice3=>"But wait"}