Ruby converte Object in Hash


127

Diciamo che ho un Giftoggetto con @name = "book"& @price = 15.95. Qual è il modo migliore per convertirlo in Hash {name: "book", price: 15.95}in Ruby, non in Rails (anche se sentiti libero di dare anche la risposta a Rails)?


16
Farebbe @ gift.attributes.to_options?
Sig. L

1
1) Gift è un oggetto ActiveRecord? 2) possiamo supporre che @ name / @ price non siano solo variabili di istanza ma anche accessor di lettori? 3) vuoi solo il nome e il prezzo o tutti gli attributi in un regalo qualunque essi siano?
tokland,

@tokland, 1) no, Giftè esattamente come @nash ha definito , tranne, 2) certo, le variabili di istanza possono avere accessi al lettore. 3) Tutti gli attributi in regalo.
ma11hew28,

Ok. La domanda sull'accesso alle variabili / lettori dell'istanza era sapere se si voleva un accesso esterno (nash) o un metodo interno (levinalex). Ho aggiornato la mia risposta per l'approccio "interno".
tokland,

Risposte:


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}

In alternativa con 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
Puoi usare inject per saltare l'inizializzazione della variabile: gift.instance_variables.inject ({}) {| hash, var | hash [var.to_s.delete ("@")] = gift.instance_variable_get (var); hash}
Giordania,

8
Bello. Ho sostituito var.to_s.delete("@")con var[1..-1].to_symper ottenere simboli.
ma11hew28,

3
Non usare iniettare, usare gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }e sbarazzarsi del finale; hash
Narfanator,

1
Non capirò mai il feticcio rubino per each. mape injectsono molto più potenti. Questa è una qualità del design che ho con Ruby: mape injectsono implementata con each. È semplicemente una cattiva informatica.
Nate Symer,

Leggermente più conciso:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin,


48

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

Tecnicamente, dovrebbe essere .to_hash, poiché # indica i metodi di classe.
Caleb,

6
In realtà no. La documentazione RDoc dice: Use :: for describing class methods, # for describing instance methods, and use . for example code(fonte: ruby-doc.org/documentation-guidelines.html ) Inoltre, la documentazione ufficiale (come il ruby ​​CHANGELOG, github.com/ruby/ruby/blob/v2_1_0/NEWS ) utilizza #ad esempio i metodi e il punto per metodi di classe abbastanza coerenti.
levinalex,

Si prega di utilizzare inject invece di questo antipattern.
YoTengoUnLCD

Variante con una each_with_objectinstance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
sola riga

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

10
Questo è Rails, lo stesso Ruby non ha instance_values. Nota che Matt ha chiesto un modo Ruby, in particolare non Rails.
Christopher Creutzig,

28
Ha anche detto di sentirsi libero di dare anche la risposta a Rails ... così ho fatto.
Erik Reedstrom,

Dio per vedere entrambe le versioni qui;) Mi è piaciuto
Sebastian Schürmann,

17

Puoi usare il as_jsonmetodo. Converte il tuo oggetto in hash.

Ma quell'hash arriverà come valore al nome di quell'oggetto come chiave. Nel tuo caso,

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

Se hai bisogno di un hash memorizzato nell'oggetto usa as_json(root: false). Penso che per impostazione predefinita root sia falso. Per maggiori informazioni consultare la guida ufficiale ruby

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


13

Per oggetti record attivi

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

e poi chiama

gift.to_hash()
purch.to_hash() 

2
divertente non fa parte del framework Rails. Sembra una cosa utile avere lì.
Magne,

Il metodo degli attributi restituisce un nuovo hash con i valori in - quindi non è necessario crearne un altro nel metodo to_hash. In questo modo: attributi_names.each_with_object ({}) {| name, attrs | attrs [nome] = read_attribute (nome)}. Vedi qui: github.com/rails/rails/blob/master/activerecord/lib/…
Chris Kimpton

avresti potuto farlo con la mappa, l'implementazione degli effetti collaterali mi sta facendo male alla mente!
Nate Symer,

11

Se non ti trovi in ​​un ambiente Rails (ad es. Non hai ActiveRecord disponibile), questo può essere utile:

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

Puoi scrivere una soluzione molto elegante usando uno stile funzionale.

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

4

Dovresti sovrascrivere il inspectmetodo dell'oggetto per restituire l'hash desiderato o semplicemente implementare un metodo simile senza sovrascrivere il comportamento dell'oggetto predefinito.

Se vuoi diventare più elaborato, puoi scorrere le variabili di istanza di un oggetto con object.instance_variables


4

Converti ricorsivamente i tuoi oggetti in hash usando la gemma "hashable" ( https://rubygems.org/gems/hashable ) Esempio

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

Potrebbe voler provare instance_values. Questo ha funzionato per me.


1

Produce una copia superficiale come oggetto hash solo degli attributi del modello

my_hash_gift = gift.attributes.dup

Controlla il tipo di oggetto risultante

my_hash_gift.class
=> Hash

0

Se hai bisogno di convertire anche oggetti nidificati.

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

Per fare questo senza Rails, un modo pulito è quello di memorizzare gli attributi su una costante.

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

Quindi, per convertire un'istanza di Giftin a Hash, puoi:

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

Questo è un buon modo per farlo perché includerà solo ciò che si definisce attr_accessore non tutte le variabili di istanza.

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

Ho iniziato a utilizzare le strutture per semplificare le conversioni di hash. Invece di usare una struttura nuda, creo la mia classe derivante da un hash che ti permette di creare le tue funzioni e documenta le proprietà di una classe.

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}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.