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

Der*_*rek 210 ruby

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

params = { :irrelevant => "A String",
           :choice1 => "Oh look, another one",
           :choice2 => "Even more strings",
           :choice3 => "But wait",
           :irrelevant2 => "The last string" }
Run Code Online (Sandbox Code Playgroud)

我想要一种简单的方法来拒绝所有不是选择+ int的键.它可以是choice1,或者choice1到choice10.它有所不同.

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

奖金:

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

lfx*_*ool 304

在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}
Run Code Online (Sandbox Code Playgroud)

  • 注意字符串化的密钥..h1 = {'a'=> 1,:b => 2}只会返回{:b => 2}和h1.slice(:a,:b) (12认同)
  • 但这并没有回答这个问题,因为他想要一种方法来提取"choice + int"键的值.您的解决方案只能提前提取已知密钥. (5认同)

mik*_*kej 256

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

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

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

例:

> { one: 1, two: 2, three: 3 }.slice(:one, :two)
=> {:one=>1, :two=>2}
Run Code Online (Sandbox Code Playgroud)

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


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

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

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

params.delete_if { |key, value| !key.to_s.match(/choice\d+/) }
Run Code Online (Sandbox Code Playgroud)

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

params.keys.select { |key| key.to_s.match(/^choice\d+/) }
Run Code Online (Sandbox Code Playgroud)

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

  • 现在可以使用 IIRC 的正则表达式来解析符号。 (2认同)
  • `.select` 的对应物是 `.reject`,如果这会让你的代码更惯用的话。 (2认同)

Nun*_*sta 47

最简单的方法是包含gem 'activesupport'(或gem 'active_support').

然后,在你的课堂上你只需要

require 'active_support/core_ext/hash/slice'
Run Code Online (Sandbox Code Playgroud)

和打电话

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

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


小智 18

哈希片

{ a: 1, b: 2, c: 3, d: 4 }.slice(:a, :b)
# => {:a=>1, :b=>2}

# If you have an array of keys you want to limit to, you should splat them:
valid_keys = [:mass, :velocity, :time]
search(options.slice(*valid_keys))

Run Code Online (Sandbox Code Playgroud)


小智 13

最简单的方法是包含gem'activesupport'(或gem'active_support').

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

  • 请在答案中添加更多详细信息. (4认同)

Rob*_*ert 11

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

keys = [:foo, :bar]
hash1 = {foo: 1, bar:2, baz: 3}
hash2 = hash1.slice(*keys)
=> {foo: 1, bar:2}
Run Code Online (Sandbox Code Playgroud)

正如其他答案所述,您还可以使用slice!来修改哈希值(并返回已擦除的键/值).


met*_*gfu 9

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

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

但是大多数上述解决方案都解决了一个需要提前知道密钥的情况,使用slice简单的regexp.

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

data = {}
matcher = ->(key,value) { COMPLEX LOGIC HERE }
data.select(&matcher)
Run Code Online (Sandbox Code Playgroud)

现在,这不仅可以在匹配键或值时允许更复杂的逻辑,而且更容易测试,并且您可以在运行时交换匹配的逻辑.

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"
Run Code Online (Sandbox Code Playgroud)


Arn*_*anc 6

Hash::select:

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


ayc*_*ter 6

如果你想要剩下的哈希:

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

或者如果你只想要钥匙:

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


mon*_*ike 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
Run Code Online (Sandbox Code Playgroud)

那你可以做

{a: "1", b: "b", c: "c", d: "d"}.filter(:a, :b) # =>  {a: "1", b: "b"}
Run Code Online (Sandbox Code Playgroud)

要么

{a: "1", b: "b", c: "c", d: "d"}.filter(/^a/)  # =>  {a: "1"}
Run Code Online (Sandbox Code Playgroud)


Puh*_*lze 5

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