【问题标题】:Ruby convert Object to HashRuby 将对象转换为哈希
【发布时间】:2011-06-29 04:03:48
【问题描述】:

假设我有一个带有@name = "book"@price = 15.95Gift 对象。将其转换为 Ruby 中的 Hash {name: "book", price: 15.95} 而不是 Rails 的最佳方法是什么(尽管也可以随意给出 Rails 的答案)?

【问题讨论】:

  • @gift.attributes.to_options 可以吗?
  • 1) 礼物是 ActiveRecord 对象吗? 2)我们可以假设@name/@price 不仅是实例变量而且是读取器访问器? 3) 你只想要名称和价格,或者礼物中的所有属性,不管它们是什么?
  • @tokland, 1) 不,Giftexactly like @nash has defined,除了 2) 当然,实例变量可以有读取器访问器。 3) 礼物中的所有属性。
  • 好的。关于实例变量/读取器访问的问题是要知道是否需要外部访问(nash)或内部方法(levinalex)。我更新了“内部”方法的答案。

标签: ruby object hashmap instance-variables


【解决方案1】:

您应该覆盖对象的 inspect 方法以返回所需的哈希值,或者只实现类似的方法而不覆盖默认对象行为。

如果你想变得更高级,你可以使用object.instance_variables迭代对象的实例变量

【讨论】:

    【解决方案2】:
    class Gift
      def initialize
        @name = "book"
        @price = 15.95
      end
    end
    
    gift = Gift.new
    hash = {}
    gift.instance_variables.each {|var| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }
    p hash # => {"name"=>"book", "price"=>15.95}
    

    或者each_with_object:

    gift = Gift.new
    hash = gift.instance_variables.each_with_object({}) { |var, hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }
    p hash # => {"name"=>"book", "price"=>15.95}
    

    【讨论】:

    • 您可以使用 inject 跳过初始化变量:gift.instance_variables.inject({}) { |hash,var| hash[var.to_s.delete("@")] = gift.instance_variable_get(var);哈希}
    • 不错。我将var.to_s.delete("@") 替换为var[1..-1].to_sym 以获取符号。
    • 不要使用inject,使用gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }并去掉尾随的; hash
    • 我永远无法理解each 对红宝石的迷恋。 mapinject 更强大。这是我对 Ruby 的一个设计疑虑:mapinject 是用 each 实现的。这简直是​​糟糕的计算机科学。
    • 稍微简洁一点:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
    【解决方案3】:
    class Gift
      def to_hash
        instance_variables.map do |var|
          [var[1..-1].to_sym, instance_variable_get(var)]
        end.to_h
      end
    end
    

    【讨论】:

      【解决方案4】:

      实现#to_hash?

      class Gift
        def to_hash
          hash = {}
          instance_variables.each { |var| hash[var.to_s.delete('@')] = instance_variable_get(var) }
          hash
        end
      end
      
      
      h = Gift.new("Book", 19).to_hash
      

      【讨论】:

      • 技术上应该是.to_hash,因为#表示类方法。
      • 其实,没有。 RDoc 文档说:Use :: for describing class methods, # for describing instance methods, and use . for example code(来源:ruby-doc.org/documentation-guidelines.html)此外,官方文档(如 ruby​​ CHANGELOG,github.com/ruby/ruby/blob/v2_1_0/NEWS)使用 # 作为实例方法,点表示类方法。
      • 请使用inject 而不是这个反模式。
      • 使用each_with_object的单线变体:instance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
      【解决方案5】:

      对于 Active Record 对象

      module  ActiveRecordExtension
        def to_hash
          hash = {}; self.attributes.each { |k,v| hash[k] = v }
          return hash
        end
      end
      
      class Gift < ActiveRecord::Base
        include ActiveRecordExtension
        ....
      end
      
      class Purchase < ActiveRecord::Base
        include ActiveRecordExtension
        ....
      end
      

      然后打电话

      gift.to_hash()
      purch.to_hash() 
      

      【讨论】:

      • 有趣的是它不是 Rails 框架的一部分。在那里似乎很有用。
      • attributes 方法返回一个带有值的新哈希 - 因此无需在 to_hash 方法中创建另一个。像这样:attribute_names.each_with_object({}) { |name, attrs| attrs[name] = read_attribute(name) } 。见这里:github.com/rails/rails/blob/master/activerecord/lib/…
      • 你可以用 map 做到这一点,你的副作用实现伤害了我的头脑!
      【解决方案6】:
      Gift.new.instance_values # => {"name"=>"book", "price"=>15.95}
      

      【讨论】:

      • 这是 Rails,Ruby 本身没有instance_values。请注意,Matt 要求的是 Ruby 方式,尤其是 Rails。
      • 他还说请随意给 Rails 答案......所以我做到了。
      • 上帝在这里看到了这两个版本;)喜欢它
      【解决方案7】:

      只要说(当前对象).attributes

      .attributes 返回任何object 中的hash。而且它也干净得多。

      【讨论】:

      • 请注意,这是特定于 ActiveModel 的方法,而不是 Ruby 方法。
      • 在Sequel的情况下——使用.values:sequel.jeremyevans.net/rdoc/classes/Sequel/Model/…
      • instance_values 可用于类似输出的所有 ruby​​ 对象。
      • 不是红宝石专家,但这不是比其他所有方法更清洁的方法吗?这对我来说似乎是最好的答案,还是我错过了什么?
      【解决方案8】:

      使用 'hashable' gem (https://rubygems.org/gems/hashable) 递归地将您的对象转换为散列 示例

      class A
        include Hashable
        attr_accessor :blist
        def initialize
          @blist = [ B.new(1), { 'b' => B.new(2) } ]
        end
      end
      
      class B
        include Hashable
        attr_accessor :id
        def initialize(id); @id = id; end
      end
      
      a = A.new
      a.to_dh # or a.to_deep_hash
      # {:blist=>[{:id=>1}, {"b"=>{:id=>2}}]}
      

      【讨论】:

        【解决方案9】:

        产生一个浅拷贝作为模型属性的散列对象

        my_hash_gift = gift.attributes.dup
        

        检查结果对象的类型

        my_hash_gift.class
        => Hash
        

        【讨论】:

          【解决方案10】:

          您可以使用函数式风格编写一个非常优雅的解决方案。

          class Object
            def hashify
              Hash[instance_variables.map { |v| [v.to_s[1..-1].to_sym, instance_variable_get v] }]
            end
          end
          

          【讨论】:

            【解决方案11】:

            如果您不在 Rails 环境中(即没有可用的 ActiveRecord),这可能会有所帮助:

            JSON.parse( object.to_json )
            

            【讨论】:

              【解决方案12】:

              如果您还需要转换嵌套对象。

              # @fn       to_hash obj {{{
              # @brief    Convert object to hash
              #
              # @return   [Hash] Hash representing converted object
              #
              def to_hash obj
                Hash[obj.instance_variables.map { |key|
                  variable = obj.instance_variable_get key
                  [key.to_s[1..-1].to_sym,
                    if variable.respond_to? <:some_method> then
                      hashify variable
                    else
                      variable
                    end
                  ]
                }]
              end # }}}
              

              【讨论】:

                【解决方案13】:

                可能想试试instance_values。这对我有用。

                【讨论】:

                  【解决方案14】:

                  您可以使用as_json 方法。它会将您的对象转换为哈希。

                  但是,该哈希值将作为该对象名称的值作为键。在你的情况下,

                  {'gift' => {'name' => 'book', 'price' => 15.95 }}
                  

                  如果您需要存储在对象中的哈希,请使用as_json(root: false)。我认为默认情况下 root 将是错误的。有关更多信息,请参阅官方 ruby​​ 指南

                  http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json

                  【讨论】:

                    【解决方案15】:

                    Gift.new.attributes.symbolize_keys

                    【讨论】:

                      【解决方案16】:

                      要在没有 Rails 的情况下执行此操作,一种简洁的方法是将属性存储在常量上。

                      class Gift
                        ATTRIBUTES = [:name, :price]
                        attr_accessor(*ATTRIBUTES)
                      end
                      

                      然后,要将Gift 的实例转换为Hash,您可以:

                      class Gift
                        ...
                        def to_h
                          ATTRIBUTES.each_with_object({}) do |attribute_name, memo|
                            memo[attribute_name] = send(attribute_name)
                          end
                        end
                      end
                      

                      这是一个很好的方法,因为它只会包含您在 attr_accessor 上定义的内容,而不是每个实例变量。

                      class Gift
                        ATTRIBUTES = [:name, :price]
                        attr_accessor(*ATTRIBUTES)
                      
                        def create_random_instance_variable
                          @xyz = 123
                        end
                      
                        def to_h
                          ATTRIBUTES.each_with_object({}) do |attribute_name, memo|
                            memo[attribute_name] = send(attribute_name)
                          end
                        end
                      end
                      
                      g = Gift.new
                      g.name = "Foo"
                      g.price = 5.25
                      g.to_h
                      #=> {:name=>"Foo", :price=>5.25}
                      
                      g.create_random_instance_variable
                      g.to_h
                      #=> {:name=>"Foo", :price=>5.25}
                      

                      【讨论】:

                        【解决方案17】:

                        我开始使用结构来简化哈希转换。 我没有使用裸结构,而是创建了自己的从哈希派生的类,这允许您创建自己的函数并记录类的属性。

                        require 'ostruct'
                        
                        BaseGift = Struct.new(:name, :price)
                        class Gift < BaseGift
                          def initialize(name, price)
                            super(name, price)
                          end
                          # ... more user defined methods here.
                        end
                        
                        g = Gift.new('pearls', 20)
                        g.to_h # returns: {:name=>"pearls", :price=>20}
                        

                        【讨论】:

                          【解决方案18】:

                          抄袭@Mr. L 在上面的评论中,尝试@gift.attributes.to_options

                          【讨论】:

                            【解决方案19】:

                            按照我无法编译的 Nate 的回答:

                            选项 1

                            class Object
                                def to_hash
                                    instance_variables.map{ |v| Hash[v.to_s.delete("@").to_sym, instance_variable_get(v)] }.inject(:merge)
                               end
                            end
                            

                            然后你这样称呼它:

                            my_object.to_hash[:my_variable_name]
                            

                            选项 2

                            class Object
                                def to_hash
                                    instance_variables.map{ |v| Hash[v.to_s.delete("@"), instance_variable_get(v)] }.inject(:merge)
                               end
                            end
                            

                            然后你这样称呼它:

                            my_object.to_hash["my_variable_name"]
                            

                            【讨论】:

                              【解决方案20】:

                              您可以使用symbolize_keys,如果您有嵌套属性,我们可以使用deep_symbolize_keys

                              gift.as_json.symbolize_keys => {name: "book", price: 15.95}
                               
                              

                              【讨论】:

                                猜你喜欢
                                • 2017-04-04
                                • 1970-01-01
                                • 1970-01-01
                                • 2020-09-15
                                • 2011-03-12
                                • 1970-01-01
                                • 2015-01-04
                                • 2011-03-13
                                • 1970-01-01
                                相关资源
                                最近更新 更多