Ruby将对象转换为哈希


127

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


16
@ gift.attributes.to_options会做吗?
L先生

1
1)礼物是ActiveRecord对象吗?2)我们可以假设@ name / @ price不仅是实例变量,而且还是读取器访问器?3)您只想要礼物的名称和价格或所有属性,无论它们是什么?
tokland 2011年

@tokland,1)没有,Gift完全一样@nash定义,除2)确定,实例变量可以有读者访问器。3)礼物中的所有属性。
ma11hew28 2011年

好。有关实例变量/读取器访问的问题是要知道是否需要外部访问(nash)或内部方法(levinalex)。我更新了“内部”方法的答案。
tokland 2011年

Answers:


80
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}

3
您可以使用inject跳过初始化变量:gift.instance_variables.inject({}){| hash,var | hash [var.to_s.delete(“ @”)] = gift.instance_variable_get(var); }
Jordan

8
真好 我换成var.to_s.delete("@")var[1..-1].to_sym得到的符号。
ma11hew28 2011年

3
不要使用注入,使用gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }和摆脱尾随; hash
Narfanator 2013年

1
我永远不会理解红宝石的崇拜eachmap而且inject功能更强大 这是我对Ruby的一种设计标准:, mapinject通过实现each。这简直是​​糟糕的计算机科学。
Nate Symer

稍微简洁一些:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin


48

实施#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,因为#表示类方法。
卡雷布(Caleb)2015年

6
其实没有 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)也#用于实例方法和点。类方法非常一致。
levinalex 2015年

请使用注入代替此反模式。
YoTengoUnLCD

使用each_with_object以下instance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
代码的单线



13

对于活动记录对象

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() 

2
有趣的是,它不是Rails框架的一部分。似乎很有用。
Magne 2012年

属性方法返回一个带有-值的新哈希,因此无需在to_hash方法中创建另一个哈希值。像这样:attribute_names.each_with_object({}){| name,attrs | attrs [name] = read_attribute(name)}。参见此处: github.com/rails/rails/blob/master/activerecord/lib/…–
克里斯·金普顿

您本可以使用map来完成此操作,但您的副作用实现正在伤害我的头脑!
Nate Symer


11
class Gift
  def to_hash
    instance_variables.map do |var|
      [var[1..-1].to_sym, instance_variable_get(var)]
    end.to_h
  end
end

6

您可以使用功能样式编写非常优雅的解决方案。

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

4

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

如果想变得更高级,可以使用object.instance_variables遍历对象的实例变量。


4

递归使用对象转换为哈希“哈希的”宝石(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}}]}

4

可能想尝试instance_values。那对我有用。


1

产生浅表副本作为仅模型属性的哈希对象

my_hash_gift = gift.attributes.dup

检查结果对象的类型

my_hash_gift.class
=> Hash

0

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

# @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 # }}}


0

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

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

然后,要将的实例转换GiftHash,您可以:

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}

0

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

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}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.