【问题标题】:Hash.except() doesn't work with symbol keysHash.except() 不适用于符号键
【发布时间】:2012-08-16 08:13:43
【问题描述】:

我有一段代码在这行:

user.attributes.except('created_at', 'created_by', 'updated_at', 'updated_by', 'id')

有效(返回作为参数传递的键的散列),而将其更改为:

user.attributes.except(:created_at, :created_by, :updated_at, :updated_by, :id)

没有(返回的哈希仍然包含所有键)。这怎么可能?

【问题讨论】:

  • 这可能是因为符号不必与字符串相同,它并不总是相等
  • @Marcin AFAIK,符号永远不等于字符串。

标签: ruby-on-rails ruby ruby-on-rails-3.2


【解决方案1】:

因为attributes返回一个Hash,键是字符串而不是符号。

http://apidock.com/rails/ActiveRecord/Base/attributes

正如其他人所说,字符串!=符号。

puts :a == 'a'
# => false

【讨论】:

  • 我真的不敢相信这是多么愚蠢。出于某种原因,我相信 Ruby 哈希内部有一些黑魔法,它允许您使用相同键的字符串或哈希“表示”。我刚刚发现我的所有哈希都起作用的唯一原因是因为我一直在使用符号!
  • 您还可以将任何Hash 转换为HashWithIndifferentAccess,然后您可以使用字符串和符号进行访问。 Rails 使用了很多(对用户/开发人员非常友好),这可能是混乱的一部分:)
  • 另一个混乱的部分是 Rails api 文档,在 Hash#except 部分,用 symbols 给出示例
【解决方案2】:

这是因为user.attributes 中的键是字符串。您可以使用symbolize_keys 方法对它们进行符号化,然后将except 与这样的符号一起使用。

user.attributes.symbolize_keys.except(:created_at, :created_by, :updated_at, :updated_by, :id)

【讨论】:

    【解决方案3】:

    我不希望这种行为在某些情况下(比如处理 http json 响应)

    所以我添加了改进。

    module HashUtil
      refine Hash do
        def eager_except(*args)
          proc = Proc.new do |key|
            case
              when key.is_a?(Symbol)
                key.to_s
              when key.is_a?(String)
                key.to_sym
              else
                nil
            end
          end
          eager_args = (args + args.map(&proc)).compact
          except(*eager_args)
        end
      end
    end
    
    
    using HashUtil
    hash = { a: 'a', "b" => 'b' }
    hash.eager_except('a', :b)
    # => {}
    

    ※ 附加信息

    在上面写了之后,我找到了其他方法。

    使用 Hash#deep_symbolize_keys 将所有键转换为符号!

    {"a" => "hoge"}.deep_symbolize_keys!.except(:a) # => {}
    

    使用 ActiveSupport::HashWithIndifferentAccess

    {"a" => "hoge"}.with_indifferent_access.except(:a) # => {}
    

    谢谢

    【讨论】:

      【解决方案4】:

      Ruby 3 添加了 Hash#except 以返回不包括给定键及其值的哈希:

      irb(main):001:0> user_details = { name: 'Akhil', age: 25, address: 'India', password: 'T:%g6R' }
      
      irb(main):002:0> puts user_details.except(:password)
      => { name: 'Akhil', age: 25, address: 'India' }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-01-05
        • 2013-11-07
        • 1970-01-01
        • 2014-10-01
        • 2016-04-29
        • 2017-06-30
        • 2016-12-27
        • 1970-01-01
        相关资源
        最近更新 更多