ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

而且我想要一种简单的方法来拒绝所有不是选择+整数的键。它可以是choice1,或choice1 到choice10。它因人而异。

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

奖金:

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

就奖金问题而言,您能否阐明您希望字符串的外观。
当然,上面的示例哈希会产生:“哦,看,另一个\t甚至更多字符串\t但是等等”(字符串末尾不是 \t,仅在它们之间)
您的示例字符串已在评论中被截断。您可以使用编辑链接来编辑您的问题并在其中添加示例。您还可以发布到目前为止您想出的红宝石作为您想要实现的目标的示例。
Ruby Hash Filter 的可能重复项

J
Jason Swett

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

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

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

例子:

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

编辑结束。下面是最初的答案,我想如果你在没有 Rails 的情况下使用 Ruby < 2.5,它会很有用,尽管我认为这种情况在这一点上相当罕见。

如果您使用的是 Ruby,则可以使用 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]


出色的!当我看到它时,这太简单了!非常感谢,也感谢其他花时间回答的人。
现在 IIRC 可以用正则表达式解析符号。
@Andrew,我认为你是对的。当我测试这个问题的答案时,我在 1.8.7 上。
.select 的对应项是 .reject,如果这使您的代码更惯用的话。
即使没有主动支持,#slice 方式也适用于 2.5.3。
l
lfx_cool

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

注意字符串化的键..h1 = {'a'=>1,:b=>2} 只会返回 {:b=>2} 和 h1.slice(:a,:b)
很棒的解决方案。我在 rspec 中使用它来返回结果,我想在其中解析哈希中的哈希值。 ``` test = {:a=>1, :b=>2, c=>{:A=>1, :B=>2}} 解决方案==> test.c.slice(:B) `` `
用 ActiveSupport 替换 Rails,这是完美的;)
但这并不能回答这个问题,因为他想要一种方法来提取“choice+int”键的值。您的解决方案只能提取提前知道的密钥。
N
Nuno Costa

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

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


至少在 2.5 上不需要 Activerecord。
R
Robert

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

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

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


翟英昌

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

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


请在您的答案中添加更多详细信息。
m
metakungfu

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

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

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

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

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

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

前解决原来的问题:

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"

我真的很喜欢匹配器
w
webgoesviral

Hash Slice

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


A
Arnaud Le Blanc

使用 Hash::select

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

a
ayckoster

如果你想要剩余的哈希:

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

或者如果你只想要钥匙:

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

根据您有多少个choiceX 和irrelevantX select OR delete_if 可能会更好。如果你有更多选择X delete_if 更好。
m
montrealmike

把它放在初始化器中

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

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

M
MBO

至于奖金问题:

如果你有这样的#select 方法的输出(2 元素数组的列表): [[:choice1, "Oh look, another one"], [:choice2, "Even more strings"], [:choice3, "但是等待"]] 然后简单地获取这个结果并执行:filtered_params.join("\t") # 或者如果你只想要值而不是键值对,filtered_params.map(&:last).join("\t")如果你有这样的#delete_if 方法的输出(散列):{:choice1=>“哦,看,另一个”,:choice2=>“更多字符串”,:choice3=>“但是等等”} 那么:filtered_params。 to_a.join("\t") # 或 filters_params.values.join("\t")


很棒的解决方案。一个问题:filtered_params.map(&:last).join("\t") 是filtered_params.map(|i| i.last).join("\t") 的缩写吗?如果是这样,什么是“最后”?我可以使用 &:value 来获取哈希值吗?
map 将块作为它的参数。您可以使用 &:method 构造动态创建块,这将创建块 {|v| v.method }。在我的例子中,&:last 在数组(2 元素数组)参数上被调用。如果你想玩它,首先检查你在块中接收到什么参数(获取 1 个参数,不要将其解构为多个参数!)并尝试找到 1 个方法(你不能用 &:method 链接方法)会返回你想要什么。如果可以,那么你可以使用快捷方式,如果没有,那么你需要完整的块
A
Arup Rakshit
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"}

J
Jose Paez

我有一个类似的问题,在我的情况下,解决方案是一个单行,即使键不是符号也可以工作,但是您需要将标准键放在数组中

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