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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7430343/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:19:58  来源:igfitidea点击:

Ruby: Easiest Way to Filter Hash Keys?

ruby

提问by Derek

I have a hash that looks something like this:

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

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

And I want a simple way to reject all the keys that aren't choice+int. It could be choice1, or choice1 through choice10. It varies.

我想要一种简单的方法来拒绝所有不是choice+int 的键。它可以是选择 1 或选择 1 到选择 10。它因人而异。

How do I single out the keys with just the word choice and a digit or digits after them?

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

Bonus:

奖金:

Turn the hash into a string with tab (\t) as a delimiter. I did this, but it took several lines of code. Usually master Rubicians can do it in one or so lines.

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

回答by lfx_cool

In Ruby, the Hash#select is a right option. If you work with Rails, you can use Hash#slice and Hash#slice!. e.g. (rails 3.2.13)

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

回答by mikej

Edit to original answer: Even though this is answer (as of the time of this comment) is the selected answer, the original version of this answer is outdated.

编辑原始答案:即使这是答案(截至本评论发布时)是选定的答案,但此答案的原始版本已过时。

I'm adding an update here to help others avoid getting sidetracked by this answer like I did.

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

As the other answer mentions, Ruby >= 2.5 added the Hash#slicemethod which was previously only available in Rails.

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

Example:

例子:

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

End of edit.What follows is the original answer which I guess will be useful if you're on Ruby < 2.5 without Rails, although I imagine that case is pretty uncommon at this point.

编辑结束。接下来是原始答案,如果您使用 Ruby < 2.5 且不使用 Rails,我想这会很有用,尽管我认为这种情况在这一点上并不常见。



If you're using Ruby, you can use the selectmethod. You'll need to convert the key from a Symbol to a String to do the regexp match. This will give you a new Hash with just the choices in it.

如果您使用的是 Ruby,则可以使用该select方法。您需要将键从 Symbol 转换为 String 以进行正则表达式匹配。这会给你一个新的哈希,只有其中的选择。

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

or you can use delete_ifand modify the existing Hash e.g.

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

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

or if it is just the keys and not the values you want then you can do:

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

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

and this will give the just an Array of the keys e.g. [:choice1, :choice2, :choice3]

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

回答by Nuno Costa

The easiest way is to include the gem 'activesupport'(or gem 'active_support').

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

Then, in your class you only need to

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

require 'active_support/core_ext/hash/slice'

and to call

并打电话

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

I believe it's not worth it to be declaring other functions that may have bugs, and it's better to use a method that has been tweaked during last few years.

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

回答by u6091682

The easiest way is to include the gem 'activesupport' (or gem 'active_support').

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

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

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

回答by Robert

If you work with rails and you have the keys in a separate list, you can use the *notation:

如果您使用 rails 并且您在单独的列表中有键,则可以使用*表示法:

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

As other answers stated, you can also use slice!to modify the hash in place (and return the erased key/values).

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

回答by metakungfu

This is a one line to solve the complete original question:

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

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

But most the solutions above are solving a case where you need to know the keys ahead of time, using sliceor simple regexp.

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

Here is another approach that works for simple and more complex use cases, that is swappable at runtime

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

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

Now not only this allows for more complex logic on matching the keys or the values, but it is also easier to test, and you can swap the matching logic at runtime.

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

Ex to solve the original issue:

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"

回答by Arnaud Le Blanc

With Hash::select:

Hash::select

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

回答by ayckoster

If you want the remaining hash:

如果你想要剩余的哈希:

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

or if you just want the keys:

或者如果你只想要钥匙:

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

回答by montrealmike

Put this in an initializer

把它放在一个初始化程序中

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

Then you can do

然后你可以做

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

or

或者

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

回答by Puhlze

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