Ruby 将对象转换为哈希

Posted

技术标签:

【中文标题】Ruby 将对象转换为哈希【英文标题】:Ruby convert Object to Hash 【发布时间】: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) 不,Gift 是 exactly like @nash has defined,除了 2) 当然,实例变量可以有读取器访问器。 3) 礼物中的所有属性。 好的。关于实例变量/读取器访问的问题是要知道是否需要外部访问(nash)或内部方法(levinalex)。我更新了“内部”方法的答案。 【参考方案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
 

【讨论】:

以上是关于Ruby 将对象转换为哈希的主要内容,如果未能解决你的问题,请参考以下文章

如何将 JSON 转换为 Ruby 哈希

Rails:如何将对象数组的哈希转换为 json

如何表示将 ruby​​ 哈希转换为 yaml 的 aws 内部函数

将 Ruby 哈希转换为 YAML

将 Nokogiri 文档转换为 Ruby 哈希

ruby 将嵌套数组转换为哈希