首页 文章

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

提问于
浏览
172

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

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可以在一个左右的行中完成 .

12 回答

  • 10

    Edit to original answer :即使这是答案(截至本评论时)是选定的答案,此答案的原始版本已过时 .

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

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

    例:

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

    End of edit. 以下是原始答案,如果您使用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]

  • 9

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

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

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

  • 5

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

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

  • 202

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

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

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

  • 4

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

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

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

    这是另一种方法 that works for simple and more complex use cases, that is swappable at runtime

    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"
    
  • 45

    把它放在初始化器中

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

    使用Hash::select

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

    如果你想要剩下的哈希:

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

    或者如果你只想要钥匙:

    params.keys.delete_if {|k| ! k.match(/choice[0-9]+/)}
    
  • 5
    params.select{ |k,v| k =~ /choice\d/ }.map{ |k,v| v}.join("\t")
    
  • 11

    至于奖金问题:

    • 如果你有像这样的 #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")
    
    • 如果您有 #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")
    
  • 5
    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"}
    

相关问题