Ruby konwertuje Object na Hash


131

Powiedzmy, że mam Giftobiekt z @name = "book"& @price = 15.95. Jaki jest najlepszy sposób na przekonwertowanie tego na Hash {name: "book", price: 15.95}w Ruby, a nie Railsy (chociaż możesz też udzielić odpowiedzi Railsom)?


17
Czy @ gift.attributes.to_options zrobi?
Pan L

1
1) Czy prezent jest obiektem ActiveRecord? 2) Czy możemy założyć, że @ nazwa / @ cena to nie tylko zmienne instancji, ale także akcesory czytników? 3) chcesz tylko nazwę i cenę lub wszystkie atrybuty prezentu, czymkolwiek one są?
tokland

@tokland, 1) nie, Giftjest dokładnie takie, jak zdefiniował @nash , z wyjątkiem 2) oczywiście , zmienne instancji mogą mieć dostęp do czytnika. 3) Wszystkie atrybuty prezentu.
ma11hew28

Ok. Pytanie o dostęp do zmiennych instancji / czytników polegało na tym, aby wiedzieć, czy potrzebna jest metoda dostępu zewnętrznego (nash), czy metody wewnętrznej (levinalex). Zaktualizowałem swoją odpowiedź dotyczącą podejścia „wewnętrznego”.
tokland

Odpowiedzi:


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}

Alternatywnie z 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
Możesz użyć inject, aby pominąć inicjalizację zmiennej: gift.instance_variables.inject ({}) {| hash, var | hash [var.to_s.delete ("@")] = gift.instance_variable_get (var); hash}
Jordan,

8
Ładny. Wymieniłem var.to_s.delete("@")z var[1..-1].to_symdostać symboli.
ma11hew28

3
Nie używaj wstrzykiwań, używaj gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }i pozbądź się końcowego; hash
Narfanator,

1
Nigdy nie zrozumiem fetyszu rubinów each. mapi injectsą znacznie potężniejsze. To jedna z wad projektowych, które mam w Rubim: mapi injectsą one realizowane z each. To po prostu zła informatyka.
Nate Symer

Nieco bardziej zwięźle:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Marvin

294

Po prostu powiedz (bieżący obiekt) .attributes

.attributeszwraca hashz any object. Jest też dużo czystszy.


141
Zauważ, że jest to metoda specyficzna dla ActiveModel, a nie metoda Rubiego.
Bricker

6
W przypadku Sequela.values
dimitarvp

instance_valuesmoże być użyty dla wszystkich obiektów ruby ​​dla podobnego wyniku.
bishal

48

Wdrożyć #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

Technicznie powinno to być .to_hash, ponieważ # wskazuje metody klas.
Caleb

6
Właściwie nie. Dokumentacja RDoc mówi: Use :: for describing class methods, # for describing instance methods, and use . for example code(źródło: ruby-doc.org/documentation-guidelines.html ) Ponadto oficjalna dokumentacja (taka jak ruby CHANGELOG , github.com/ruby/ruby/blob/v2_1_0/NEWS ) używa #na przykład metod i kropki dla metod klasowych dość konsekwentnie.
levinalex

Proszę użyć wstrzyknięcia zamiast tego anty-wzoru.
YoTengoUnLCD

Wariant each_with_objectinstance_variables.each_with_object(Hash.new(0)) { |element, hash| hash["#{element}".delete("@").to_sym] = instance_variable_get(element) }
jednoliniowy

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

10
To jest Rails, sam Ruby nie ma instance_values. Zauważ, że Matt poprosił o sposób w Ruby, a konkretnie nie o Railsy.
Christopher Creutzig

28
Powiedział również, że nie krępuj się udzielić odpowiedzi Railsom ... więc tak zrobiłem.
Erik Reedstrom

Boże, żeby zobaczyć obie wersje tutaj;) Podobało mi się
Sebastian Schürmann

17

Możesz użyć as_jsonmetody. Przekształci twój obiekt w hash.

Ale ten skrót będzie wartością dla nazwy tego obiektu jako klucza. W Twoim przypadku,

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

Jeśli potrzebujesz skrótu przechowywanego w obiekcie, użyj as_json(root: false). Myślę, że domyślnie root będzie fałszywy. Aby uzyskać więcej informacji, zapoznaj się z oficjalnym przewodnikiem po Rubim

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


13

Dla obiektów 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

a potem po prostu zadzwoń

gift.to_hash()
purch.to_hash() 

2
zabawne, że nie jest częścią frameworka Rails. Wydaje się, że warto tam mieć.
Magne,

Metoda atrybutów zwraca nowy hash z wartościami w - więc nie ma potrzeby tworzenia kolejnego w metodzie to_hash. Tak jak to: nazwy_atrybutów.each_with_object ({}) {| nazwa, atrybuty | attrs [name] = read_attribute (name)}. Zobacz tutaj: github.com/rails/rails/blob/master/activerecord/lib/…
Chris Kimpton

mogłeś to zrobić z mapą, twój efekt uboczny rani mój umysł!
Nate Symer

11

Jeśli nie jesteś w środowisku Railsów (tj. Nie masz dostępnego ActiveRecord), może to być pomocne:

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

Możesz napisać bardzo eleganckie rozwiązanie używając funkcjonalnego stylu.

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

4

Powinieneś zastąpić inspectmetodę obiektu, aby zwrócić żądany skrót, lub po prostu zaimplementować podobną metodę bez zastępowania domyślnego zachowania obiektu.

Jeśli chcesz być bardziej wyrafinowany, możesz iterować zmienne instancji obiektu za pomocą object.instance_variables


4

Rekurencyjnie konwertuj swoje obiekty na hash za pomocą klejnotu „hashable” ( https://rubygems.org/gems/hashable ) Przykład

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

Może zechcesz spróbować instance_values. To zadziałało dla mnie.


1

Tworzy płytką kopię jako obiekt skrótu tylko atrybutów modelu

my_hash_gift = gift.attributes.dup

Sprawdź typ wynikowego obiektu

my_hash_gift.class
=> Hash

0

Jeśli potrzebujesz również konwersji zagnieżdżonych obiektów.

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

Aby to zrobić bez Railsów, czystym sposobem jest przechowywanie atrybutów w stałej.

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

Następnie, aby przekonwertować wystąpienie Giftna a Hash, możesz:

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

Jest to dobry sposób na zrobienie tego, ponieważ będzie obejmował tylko to, co zdefiniujesz attr_accessor, a nie każdą zmienną instancji.

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

Zacząłem używać struktur, aby ułatwić konwersje haszujące. Zamiast używać czystej struktury, tworzę własną klasę wywodzącą się z skrótu, co pozwala na tworzenie własnych funkcji i dokumentuje właściwości klasy.

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}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.