【问题标题】:Conditional key/value in a ruby hash红宝石哈希中的条件键/值
【发布时间】:2011-08-10 16:22:15
【问题描述】:

有没有一种很好的(一行)方法可以在 ruby​​ 中编写一个哈希值,并且只有在满足条件的情况下才会有一些条目?我想到了

{:a => 'a', :b => ('b' if condition)}

但是,如果条件不满足,则留下:b == nil。我意识到这可以在两行左右轻松完成,但在一行中会更好(例如,将哈希传递给函数时)。

我在这里错过了(还)另一个 ruby​​ 的惊人功能吗? ;)

【问题讨论】:

  • 不满足条件时hash[:b]要什么?在普通哈希中,如果没有匹配的键,你无论如何都会得到nil
  • 如果您遍历哈希,那么即使condition 为假,您也会看到设置了:b

标签: ruby


【解决方案1】:

更新 Ruby 2.4+

从 ruby​​ 2.4.0 开始,您可以使用compact 方法:

{ a: 'a', b: ('b' if cond) }.compact

原始答案(Ruby 1.9.2)

你可以先用 key => nil 来创建不满足条件的散列,然后删除那些值为 nil 的对。例如:

{ :a => 'a', :b => ('b' if cond) }.delete_if{ |k,v| v.nil? }

产量,对于 cond == true:

{:b=>"b", :a=>"a"}

对于 cond == false

{:a=>"a"} 

ruby 1.9.3 更新

这是等价的——更简洁一点,使用 ruby​​ 1.9.3 表示法:

{ a: 'a', b: ('b' if cond) }.reject{ |k,v| v.nil? }

【讨论】:

  • 您可以将(cond ? 'b' : nil) 缩短为('b' if cond)。有趣的想法。
  • 如果我将.delete_if{ |k,v| v.nil? } 放在一个单独的函数中,它会更短。谢谢!
  • 但是你可能会争辩说它不是单行的。我当然不会。 :)
  • 注意:如果没有删除任何键,compact 将返回 nil,而不是原始哈希。在大多数情况下使用 delete_if 可能会更好。
  • @Gripp:这不是真的。请参阅:ruby [1] pry(main)> {a: 1}.compact => {:a=>1} 。不过,compact! 确实如此。
【解决方案2】:

从 Ruby 1.9+ 开始,如果您想基于条件构建哈希,您可以使用 tap,这是我最喜欢的新东西。这会将其分成多行,但恕我直言,更具可读性:

{}.tap do |my_hash| 
  my_hash[:a] = 'a'
  my_hash[:b] = 'b' if condition
end

【讨论】:

  • 我会将任何非条件值放在起始哈希中。但是,是的,这就是我要做的方式 - 即使你想要 nils 作为哈希值,它也可以工作,并且仍然非常可读,与发布的一些解决方案不同。
  • 这应该是正确的答案。这是我的单行版本:{a: "animal"}.tap { |hash| hash[:b] = 'banana' if true }
  • @CameronMartin 也许,但无论是否有条件地“排列”它们也很好。
  • @JoshuaPinter 七年后我同意你的观点。我想我的编码风格已经改变了。
  • “如果我能改变,你也能改变,那么每个人都可以改变。” :) youtube.com/watch?v=GtDvmV4zr-Q
【解决方案3】:

>= Ruby 2.4:

{a: 'asd', b: nil}.compact
=> {:a=>"asd"}

【讨论】:

    【解决方案4】:

    有兴趣查看其他答案,但这是我能想到的最好的单线(我也非常不擅长单线:P)

    {:a => 'a'}.merge( condition ? {:b => 'b'} : {} )
    

    【讨论】:

    • 嗯,好主意,但如果有多个条件条目,这可能会变得很丑……
    • 你是对的。我认为您不会为此找到一个不错的班轮。如果它足够复杂以至于它会变得那么丑陋,那么它可能已经足够复杂到可以提前构建散列。只是我的2美分。 :)
    • 我认为这比接受的答案要好,因为它不会干扰任何故意为 nil 的哈希值。
    【解决方案5】:

    这里有很多聪明的解决方案,但 IMO 最简单也是最好的方法是

    hash = { a: 'a', b: 'b' }
    hash[:c] = 'c' if condition
    

    这违背了 OP 的要求,即分两行执行,但实际上其他似乎只是单行的答案也是如此。让我们面对现实吧,这是最简单的解决方案,而且很容易阅读。

    【讨论】:

      【解决方案6】:

      在 Ruby 2.0 中,类似于用于数组(和位置参数)的旧 splat 运算符 (*),有一个用于哈希(和关键字参数)的双 splat 运算符 (**)。所以你可以说:

      {a: 'b', **(condition ? {b: 'b'} : {})}
      

      【讨论】:

        【解决方案7】:
        Hash[:a, 'a', *([:b, 'b'] if condition1), *([:c, 'c'] if condition2)]
        

        这依赖于 *nil 在 ruby​​ 1.9 中扩展为 vacuity 的事实。在 ruby​​ 1.8 中,您可能需要这样做:

        Hash[:a, 'a', *(condition1 ? [:b, 'b'] : []), *(condition2 ? [:c, 'c'] : [])]
        

        Hash[:a, 'a', *([:b, 'b'] if condition1).to_a, *([:c, 'c'] if condition2).to_a]
        

        【讨论】:

        • 酷,我喜欢!有什么方法可以写得更自然/更简单?
        • 如果你想要一致性,你可以添加一些额外的东西:Hash[*([:a, 'a'] if true), *([:b, 'b'] if condition1), *([:c, 'c'] if condition2)],但这与简单性背道而驰。
        • 这不是我的意思:D 我只是在想是否有某种方法可以使用更类似于哈希的=> 表示法来做到这一点,但我想不出任何东西。
        • 实际上,如果您使用的是 ruby​​ 1.9,则趋势是使用 {a: 'a'} 之类的符号键表示哈希,这接近于 [:a, 'a'],而不是 {:a => 'a'}
        【解决方案8】:

        如果您有多个条件和逻辑需要其他人稍后理解,那么我建议这不是 1 班轮的好人选。根据所需的逻辑正确创建哈希会更有意义。

        【讨论】:

          【解决方案9】:

          这个非常适合多个条件。

          (
            hash = {:a => 'a'}.tap {|h|
              h.store( *[(:b if condition_b), 'b'] )
              h.store( *[(:c if condition_c), 'c'] )
            }
          ).delete(nil)
          

          请注意,我选择 nil 作为“垃圾”键,完成后会被删除。如果您需要使用 nil 键存储实际值,只需将存储条件更改为:

          (condition_b ? :b : garbage_key)
          

          然后在最后删除(garbage_key)。

          此解决方案还将保持现有的 nil 值不变,例如如果原始哈希中有 :a => nil,则不会被删除。

          【讨论】:

            【解决方案10】:

            我的单线解决方案:

            {:a => 'a'}.tap { |h| h.merge!(:b => 'b') if condition }
            

            【讨论】:

              【解决方案11】:
              hash, hash_new = {:a => ['a', true], :b => ['b', false]}, {}
              hash.each_pair{|k,v| hash_new[k] = v[1] ? v : nil }
              puts hash_new
              

              【讨论】:

                【解决方案12】:
                eval("{:a => 'a' #{', :b => \'b\'' if condition }}")
                

                甚至

                eval("{#{[":a => 'a'", (":b=>'b'" if ax)].compact.join(',')}}")
                

                更简单的添加条件

                【讨论】:

                • 永远不要用 eval 做某事 ;)
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2019-04-23
                • 1970-01-01
                • 2012-12-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-10-17
                相关资源
                最近更新 更多