Ruby chuyển đổi Object thành Hash


127

Hãy nói rằng tôi có một Giftđối tượng với @name = "book"& @price = 15.95. Cách tốt nhất để chuyển đổi nó thành Hash {name: "book", price: 15.95}trong Ruby, không phải Rails (mặc dù cũng cảm thấy thoải mái khi trả lời Rails)?


16
@ Gift.attribut.to_options sẽ làm gì?
Ông L

1
1) Quà tặng có phải là đối tượng ActiveRecord không? 2) chúng ta có thể giả sử @ name / @ price không chỉ là các biến thể mà còn là các trình truy cập của người đọc không? 3) bạn chỉ muốn tên và giá hoặc tất cả các thuộc tính trong một món quà bất kể chúng là gì?
tokland

@tokland, 1) không có, Giftchính xác như @nash đã xác định , trừ, 2) chắc chắn, các biến thể có độc giả accessors. 3) Tất cả các thuộc tính trong món quà.
ma11hew28

Đồng ý. Câu hỏi về truy cập biến đối tượng / người đọc là để biết liệu muốn truy cập bên ngoài (nash) hay phương thức bên trong (levinalex). Tôi đã cập nhật câu trả lời của mình cho phương pháp "bên trong".
tokland

Câu trả lời:


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}

Hoặc với 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
Bạn có thể sử dụng chích để bỏ qua việc khởi tạo biến: gift.instance_variables.inject ({}) {| hash, var | hàm băm [var.to_s.delete ("@")] = gift.instance_variable_get (var); băm}
Jordan

8
Đẹp. Tôi thay thế var.to_s.delete("@")với var[1..-1].to_symđể có được các biểu tượng.
ma11hew28

3
Đừng sử dụng chích, sử dụng gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }và thoát khỏi dấu vết; hash
Narfanator

1
Tôi sẽ không bao giờ hiểu được tôn sùng ruby ​​cho each. mapinjectmạnh mẽ hơn nhiều. Đây là một phẩm chất thiết kế tôi có với Ruby: mapinjectđược triển khai cùng each. Nó đơn giản là khoa học máy tính tồi.
Symate Nate

Hơi súc tích hơn một chút:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin

293

Chỉ cần nói (đối tượng hiện tại) .attributes

.attributestrả về hashbất kỳ object. Và nó cũng sạch sẽ hơn nhiều.


138
Lưu ý rằng đây là phương thức dành riêng cho ActiveModel, không phải phương thức Ruby.
thợ nề

6
Trong trường hợp của Sequel - sử dụng .values: sequel.jeremyevans.net/rdoc/groupes/Sequel/Model/ trộm
dimitarvp

instance_valuescó thể được sử dụng cho tất cả các đối tượng ruby ​​cho đầu ra tương tự.
giám mục

48

Thực hiện #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

Về mặt kỹ thuật, nó phải là .to_hash, vì # chỉ ra các phương thức lớp.
Caleb

6
Thật ra, không. Tài liệu RDoc cho biết: Use :: for describing class methods, # for describing instance methods, and use . for example code(nguồn: ruby-doc.org/documentation-guferences.html ) Ngoài ra, tài liệu chính thức (như ruby ​​CHANGELOG, github.com/ruby/ruby/blob/v2_1_0/NEWS ) sử dụng #cho các phương thức ví dụ và dấu chấm cho các phương thức lớp khá nhất quán.
levinalex

Vui lòng sử dụng tiêm thay vì antipotype này.
YoTengoUnLCD

Biến thể một lớp lót sử dụng each_with_object:instance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
Anothermh

43
Gift.new.instance_values # => {"name"=>"book", "price"=>15.95}

10
Đây là Rails, bản thân Ruby không có instance_values. Lưu ý rằng Matt yêu cầu một cách Ruby, đặc biệt không phải Rails.
Christopher Creutzig

28
Anh ấy cũng nói rằng hãy thoải mái đưa ra câu trả lời của Rails ... vì vậy tôi đã làm.
Erik Reedstrom

Chúa sẽ thấy cả hai phiên bản ở đây;) Thích nó
Sebastian Schürmann

17

Bạn có thể sử dụng as_jsonphương pháp. Nó sẽ chuyển đổi đối tượng của bạn thành hàm băm.

Nhưng, hàm băm đó sẽ trở thành một giá trị cho tên của đối tượng đó làm khóa. Trong trường hợp của bạn,

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

Nếu bạn cần một hàm băm được lưu trữ trong đối tượng sử dụng as_json(root: false). Tôi nghĩ theo mặc định root sẽ là sai. Để biết thêm thông tin tham khảo hướng dẫn ruby ​​chính thức

http://api.rubyonrails.org/groupes/ActiveModel/Serialulators/JSON.html#method-i-as_json


13

Đối với các đối tượng bản ghi hoạt động

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

và sau đó chỉ cần gọi

gift.to_hash()
purch.to_hash() 

2
buồn cười, nó không phải là một phần của khung Rails. Có vẻ như một điều hữu ích để có ở đó.
Magne

Phương thức thuộc tính trả về một hàm băm mới với các giá trị trong - vì vậy không cần tạo cái khác trong phương thức to_hash. Giống như vậy: property_names.each_with_object ({}) {| name, attrs | attrs [name] = read_attribution (name)}. Xem tại đây: github.com/rails/rails/blob/master/activerecord/lib/ mẹo
Chris Kimpton

bạn có thể đã làm điều này với bản đồ, việc thực hiện tác dụng phụ của bạn đang làm tổn thương tâm trí của tôi!
Nate Symer

11

Nếu bạn không ở trong môi trường Rails (ví dụ: không có ActiveRecord khả dụng), điều này có thể hữu ích:

JSON.parse( object.to_json )

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

Bạn có thể viết một giải pháp rất thanh lịch bằng cách sử dụng một phong cách chức năng.

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

4

Bạn nên ghi đè inspectphương thức của đối tượng để trả về hàm băm mong muốn hoặc chỉ thực hiện một phương thức tương tự mà không ghi đè hành vi đối tượng mặc định.

Nếu bạn muốn nhận fancier, bạn có thể lặp lại các biến đối tượng của đối tượng với object.instance_variables


4

Chuyển đổi đệ quy các đối tượng của bạn thành hàm băm bằng cách sử dụng đá quý 'có thể băm' ( https://rubygems.org/gems/hashable ) Ví dụ

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

Có thể muốn thử instance_values. Điều đó làm việc cho tôi.


1

Tạo một bản sao nông làm đối tượng băm chỉ các thuộc tính mô hình

my_hash_gift = gift.attributes.dup

Kiểm tra loại đối tượng kết quả

my_hash_gift.class
=> Hash

0

Nếu bạn cần các đối tượng lồng nhau cũng được chuyển đổi.

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

Để làm điều này mà không có Rails, một cách rõ ràng là lưu trữ các thuộc tính trên một hằng số.

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

Và sau đó, để chuyển đổi một thể hiện của Gifta Hash, bạn có thể:

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

Đây là một cách tốt để làm điều này bởi vì nó sẽ chỉ bao gồm những gì bạn xác định attr_accessorvà không phải mọi biến thể hiện.

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

Tôi bắt đầu sử dụng các cấu trúc để dễ dàng chuyển đổi băm. Thay vì sử dụng một cấu trúc trần, tôi tạo lớp riêng của mình xuất phát từ hàm băm, điều này cho phép bạn tạo các hàm của riêng mình và nó ghi lại các thuộc tính của một lớp.

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}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.