Ruby:过滤哈希密钥的最简单方法?

时间:2011-09-15 11:52:13

标签: 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。它各不相同。

如何使用单词选择和后面的一个或多个数字来单独输出键?

加成:

将哈希值转换为带有制表符(\ t)作为分隔符的字符串。我这样做了,但需要几行代码。通常,掌握的Rubician可以在一行中完成。

13 个答案:

答案 0 :(得分:296)

在Ruby中,Hash #select是一个正确的选择。如果你使用Rails,你可以使用Hash#slice和Hash#slice!。例如(rails 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}

答案 1 :(得分:231)

编辑为原始答案:即使这是答案(截至本评论时),也是所选答案,此答案的原始版本已过时。

我在这里添加了一个更新,以帮助其他人避免像我一样被这个答案所牵制。

正如另一个回答所提到的,Ruby> = 2.5添加了Hash#slice方法,该方法以前只在Rails中可用。

示例:

> { one: 1, two: 2, three: 3 }.slice(:one, :two)
=> {:one=>1, :two=>2}

编辑结束。以下是原始答案,如果您使用的是Ruby< 2.5没有Rails,虽然我认为这种情况在这一点上非常罕见。


如果您使用的是Ruby,则可以使用select方法。您需要将键从符号转换为字符串以执行正则表达式匹配。这将为您提供一个新的Hash,只包含其中的选项。

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

或者您可以使用delete_if并修改现有的Hash,例如

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

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

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

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

答案 2 :(得分:45)

最简单的方法是加入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"}

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

答案 3 :(得分:11)

最简单的方法是加入gem' activesupport' (或gem' active_support')。

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

答案 4 :(得分:10)

如果您使用rails并且您将密钥放在单独的列表中,则可以使用--debug表示法:

*

正如其他答案所述,您还可以使用keys = [:foo, :bar] hash1 = {foo: 1, bar:2, baz: 3} hash2 = hash1.slice(*keys) => {foo: 1, bar:2} 修改哈希值(并返回已删除的键/值)。

答案 5 :(得分:9)

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

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

但是大多数上述解决方案都是使用slice或简单的正则表达式来解决您需要提前知道密钥的情况。

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

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

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

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"

答案 6 :(得分:6)

将其放入初始值设定项

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"}

答案 7 :(得分:5)

使用Hash::select

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

答案 8 :(得分:5)

如果你想要剩下的哈希:

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

或者如果你只想要钥匙:

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

答案 9 :(得分:5)

params.select{ |k,v| k =~ /choice\d/ }.map{ |k,v| v}.join("\t")

答案 10 :(得分:4)

关于奖金问题:

  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")
    

答案 11 :(得分:2)

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"}

答案 12 :(得分:0)

我遇到了类似的问题,在我的情况下,解决方案是使用一根衬板,即使键不是符号,它也可以工作,但是您需要将条件键放在数组中

criteria_array = [:choice1, :choice2]

params.select { |k,v| criteria_array.include?(k) } #=> { :choice1 => "Oh look another one",
                                                         :choice2 => "Even more strings" }

另一个例子

criteria_array = [1, 2, 3]

params = { 1 => "A String",
           17 => "Oh look, another one",
           25 => "Even more strings",
           49 => "But wait",
           105 => "The last string" }

params.select { |k,v| criteria_array.include?(k) } #=> { 1 => "A String"}