Ruby แปลง Object เป็น Hash


127

สมมติว่าผมมีGiftวัตถุที่มีและ@name = "book" @price = 15.95วิธีที่ดีที่สุดในการแปลงเป็น Hash {name: "book", price: 15.95}ใน Ruby ไม่ใช่ Rails (แต่อย่าลังเลที่จะตอบ Rails ด้วย)


16
@ gift.attributes.to_options จะทำอย่างไร
นาย L

1
1) ของขวัญเป็นวัตถุ ActiveRecord หรือไม่ 2) เราสามารถสมมติว่า @ name / @ price ไม่ได้เป็นเพียงแค่ตัวแปรอินสแตนซ์เท่านั้น แต่ยังรวมถึง access access reader หรือไม่ 3) คุณต้องการเพียงชื่อและราคาหรือคุณลักษณะทั้งหมดในของขวัญไม่ว่าจะเป็นอะไร?
tokland

@tokland 1) ไม่Giftเป็นเหมือน @nash ได้กำหนดยกเว้น 2) ตรวจสอบว่าตัวแปรเช่นสามารถมี accessors อ่าน 3) คุณลักษณะทั้งหมดในของขวัญ
ma11hew28

ตกลง. คำถามเกี่ยวกับการเข้าถึงตัวแปร / ผู้อ่านอินสแตนซ์คือการทราบว่าต้องการการเข้าถึงจากภายนอก (nash) หรือวิธีการภายใน (levinalex) ฉันอัปเดตคำตอบสำหรับวิธีการ "ภายใน"
tokland

คำตอบ:


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); hash}
Jordan

8
ดี ฉันแทนที่var.to_s.delete("@")ด้วยvar[1..-1].to_symเพื่อรับสัญลักษณ์
ma11hew28

3
อย่าใช้ inject ใช้gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }และกำจัด trailing; hash
Narfanator

1
eachฉันไม่เคยจะเข้าใจเครื่องรางทับทิม mapและinjectมีพลังมากขึ้น นี้เป็นหนึ่งในความไม่สบายใจการออกแบบที่ฉันมีกับทับทิม mapและจะดำเนินการกับinject eachมันเป็นวิทยาศาสตร์คอมพิวเตอร์ที่ไม่ดีเลย
Nate Symer

รัดกุมมากขึ้นเล็กน้อย:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin

293

แค่พูด (วัตถุปัจจุบัน) .attributes

.attributesผลตอบแทนใด ๆhash objectและก็สะอาดกว่าด้วย


138
โปรดทราบว่านี่เป็นวิธีการเฉพาะของ ActiveModel ไม่ใช่วิธีการของ Ruby
ช่างก่ออิฐ

6
ในกรณีของภาคต่อ - ใช้.values: sequel.jeremyevans.net/rdoc/classes/Sequel/Model/ …
dimitarvp

instance_valuesสามารถใช้สำหรับวัตถุทับทิมทั้งหมดสำหรับผลลัพธ์ที่คล้ายกัน
bishal

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

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

โปรดใช้การฉีดแทนการใช้ antipattern นี้
YoTengoUnLCD

ชุดตัวเลือกแบบซับเดียวใช้each_with_object:instance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
อีก

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

10
instance_valuesนี่คือทางรถไฟทับทิมตัวเองไม่ได้ โปรดทราบว่า Matt ขอวิธี Ruby โดยเฉพาะไม่ใช่ Rails
Christopher Creutzig

28
เขายังบอกด้วยว่ารู้สึกอิสระที่จะตอบคำถามของ Rails ด้วย ... ดังนั้นฉันก็ทำเช่นนั้น
Erik Reedstrom

พระเจ้าจะเห็นทั้งสองรุ่นที่นี่;) ชอบ
Sebastian Schürmann

17

คุณสามารถใช้as_jsonวิธีการ มันจะแปลงวัตถุของคุณเป็นแฮช

แต่แฮชนั้นจะมาเป็นค่าของชื่อของวัตถุนั้นเป็นกุญแจสำคัญ ในกรณีของคุณ

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

as_json(root: false)หากคุณต้องการกัญชาที่เก็บไว้ในการใช้งานวัตถุ ฉันคิดว่าโดยค่าเริ่มต้นจะเป็นเท็จ สำหรับข้อมูลเพิ่มเติมดูคู่มือทับทิมอย่างเป็นทางการ

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


13

สำหรับ Active Record Objects

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
ตลกมันไม่ได้เป็นส่วนหนึ่งของกรอบทางรถไฟ ดูเหมือนว่าสิ่งที่มีประโยชน์ที่จะมี
Magne

วิธีการแอตทริบิวต์ส่งกลับแฮชใหม่ที่มีค่าใน - ดังนั้นไม่จำเป็นต้องสร้างใหม่ในวิธีการ to_hash ไลค์ดังนี้: attribute_names.each_with_object ({}) {| name, attrs | attrs [name] = read_attribute (name)} ดูที่นี่: github.com/rails/rails/blob/master/activerecord/lib/…
Chris Kimpton

คุณสามารถทำสิ่งนี้ได้ด้วยแผนที่การใช้ผลข้างเคียงของคุณกำลังทำร้ายจิตใจของฉัน!
Nate Symer

11

หากคุณไม่ได้อยู่ในสภาพแวดล้อมของ Rails (เช่น. ไม่มี ActiveRecord ให้ใช้งาน) สิ่งนี้อาจมีประโยชน์:

JSON.parse( object.to_json )


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

แปลงวัตถุของคุณซ้ำเป็นแฮชโดยใช้อัญมณี 'hashable' ( 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}}]}


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

จากนั้นในการแปลงอินสแตนซ์ของGiftเป็น a 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}

0

ฉันเริ่มใช้ structs เพื่อทำให้ง่ายต่อการแปลงแฮช แทนที่จะใช้โครงสร้างเปล่าฉันสร้างคลาสของตัวเองที่ได้มาจากการแฮชนี่ช่วยให้คุณสร้างฟังก์ชั่นของคุณเองและมันบันทึกคุณสมบัติของคลาส

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}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.