【问题标题】:Not assigning nil values to a hash不将 nil 值分配给哈希
【发布时间】:2012-01-09 06:30:24
【问题描述】:

当它们在 ruby​​ 中为 nil 时,是否有将事物分配给哈希的简写或最佳实践?例如,我的问题是我正在使用另一个哈希来构建它,如果其中的某些内容为 nil,它会将 nil 分配给该键,而不是仅仅将其单独放置。我理解为什么会发生这种情况,所以我的解决方案是:

hash1[:key] = hash2[:key] unless hash2[:key].nil?

因为我不能在键实际指向 nil 的 has 中有值。 (我宁愿有一个空的哈希而不是一个有 {:key => nil} 的,这是不可能的)

我的问题是有没有更好的方法来做到这一点?我不想在作业结束时执行 delete_if。

【问题讨论】:

  • 您的解决方案对我来说看起来不错。如果你以整个循环为例,我敢肯定,有几种好方法可以把它变成一行。

标签: ruby-on-rails ruby hash ruby-1.9 null


【解决方案1】:

如果你否定“除非”语句,会短一点

hash1[:key] = hash2[:key] if hash2[:key]   # same as   if ! hash2[:key].nil?

您也可以按照 Michael 或 Marc-Andre 的其他答案中的建议在 && 语句中进行比较

这完全取决于您,您认为最适合您阅读的内容。按照设计,Ruby 中总是有多种方法可以解决问题。

你也可以修改 hash2 :

hash1 = hash2.reject{|k,v| v.nil?}

hash2.reject!{|k,v| v.nil?}   # even shorter, if in-place editing of hash2

这会从 hash2 中删除键/值对 :key => nil (如果你使用了拒绝!)

【讨论】:

  • > false.nil? => 错误
  • 当心,reject! 如果没有进行任何更改,则返回 nil。
【解决方案2】:

我最喜欢这个,循环和条件覆盖都在一行中!

h1 = {:foo => 'foo', :bar => 'bar'}
h2 = {:foo => 'oof', :bar => nil}

h1.merge!(h2) { |key, old_val, new_val| new_val.nil? ? old_val : new_val }

#=> {:foo => 'oof', :bar => 'bar'}

这会将 h1 中的每个值替换为 h2 的值,其中键相同且 h2 值不为零。

【讨论】:

  • yes and no :/ 如果 h2 中有 nil 值而 h1 为空,它会将 h1 中的值设置为 nil。
【解决方案3】:

我不确定这是否真的更好,但是

hash2[:key] && hash[:key] = hash2[:key]

可以工作。请注意,如果这不是您想要的,这对于 falsenil 的行为方式相同

!hash2[:key].nil? && hash[:key] = hash2[:key]

会更好。所有这一切都假设:key 是一个您可能无法控制的任意值。

【讨论】:

  • +1 是正确的。但基本上和Red的解决方案一样
  • 当然也是一样的。我理解这个问题的方式是,他对代码的风格而不是功能不满意。
【解决方案4】:

这样的事情怎么样?

hash2.each_pair do |key, value|
  next if value.nil?
  hash1[key] = value
end

如果你只做一个作业,这可能会减少一些字符:

hash2[:key] && hash1[:key] = hash2[:key]

我的第一个例子也可以再剃一点:

hash2.each_pair{ |k,v| v && hash1[k] = v }

我认为第一个是最容易阅读/理解的。此外,示例 2 和 3 将跳过任何评估为假的内容(nilfalse)。最后一个示例是一行,不会跳过 false 值:

hash2.each_pair{ |k,v| v.nil? || hash1[k] = v }

【讨论】:

    【解决方案5】:

    我认为最佳做法是将nil 值复制到哈希中。例如,如果通过选项:foo => nil,它可能意味着什么,应该覆盖默认的:foo42。这也使得设置默认为 true 的选项变得更容易,尽管在这些情况下应该使用 fetch

    opt = hash.fetch(:do_cool_treatment, true) # => will be true if key is not present
    

    有很多方法可以复制值,包括nilfalse

    对于单个键,您可以使用has_key? 而不是查找:

    hash1[:key] = hash2[:key] if hash2.has_key? :key
    

    对于所有(或许多)键,使用merge!

    hash1.merge!(hash2)
    

    如果您只想对hash2 的几个键执行此操作,则可以对其进行切片:

    hash1.merge!(hash2.slice(:key, ...))
    

    【讨论】:

    • 如果 hash2 有带有 nil 值的键,merge! 将无法正常工作,因为 hash1 将拥有相同的键和它们的 nil 值......正是 OP 想要的避免。
    • 合并的缺点是它会将一个 :key => nil 键/值对复制到 hash1 如果它存在于 hash2 中。我认为 OP 不希望这样。
    • @Tilo:编辑以反映我认为最好复制 nil 值的观点。
    【解决方案6】:

    好的,如果由于您想要更多控制权而导致合并不起作用:

    hash1[:key] = hash2.fetch(:key, hash1[:key])
    

    这会将 hash1 的 :key 设置为 hash2,除非它不存在。在这种情况下,它将使用默认值(获取的第二个参数),即 hash1 的键

    【讨论】:

    • 如果 :key 不在 hash1 中(右手边),这将不起作用
    • @RickR 你说得对……哎呀! ... 也需要在右侧获取:hash1[:key] = hash2.fetch(:key, hash1.fetch(:key, nil)) ... 这看起来有点难看
    【解决方案7】:

    将此添加到您的初始化程序 hash.rb

    class Hash
      def set_safe(key,val)
        if val && key
          self[key] = val
        end
      end
    end
    

    使用

    hash = {}
    hash.set_safe 'key', value_or_nil
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-07-20
      • 2015-07-05
      • 1970-01-01
      • 1970-01-01
      • 2014-06-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多