Ruby 将对象转换为哈希
Posted
技术标签:
【中文标题】Ruby 将对象转换为哈希【英文标题】:Ruby convert Object to Hash 【发布时间】:2011-06-29 04:03:48 【问题描述】:假设我有一个带有@name = "book"
和@price = 15.95
的Gift
对象。将其转换为 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
对红宝石的迷恋。 map
和 inject
更强大。这是我对 Ruby 的一个设计疑虑:map
和 inject
是用 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 将对象转换为哈希的主要内容,如果未能解决你的问题,请参考以下文章