Ruby конвертувати об'єкт у хеш


127

Скажімо, у мене є Giftоб’єкт із @name = "book"& @price = 15.95. Який найкращий спосіб перетворити це на хеш {name: "book", price: 15.95}у Ruby, а не на Rails (хоча сміливо дайте відповіді Rails також)?


16
Чи зробить @ gift.attributes.to_options?
Містер L

1
1) Чи є подарунок об'єктом ActiveRecord? 2) чи можемо ми вважати, що ціна @ name / @ - це не лише змінні екземпляри, а й читацькі аксесуари? 3) ви хочете лише ім'я та ціну або всі атрибути в подарунку, якими б вони не були?
токленд

@tokland, 1) немає, Giftце так само , як @nash визначив , за винятком того , 2) переконайтеся, що змінні екземпляра можуть мати читача аксессор. 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.instan_variables.inject ({}) {| hash, var | hash [var.to_s.delete ("@")] = gift.instan_variable_get (var); хеш}
Йорданія

8
Приємно. Я замінив var.to_s.delete("@")з , var[1..-1].to_symщоб отримати символи.
ma11hew28

3
Не використовуйте ін'єкцій, не використовуйте gift.instance_variables.each_with_object({}) { |var,hash| hash[var.to_s.delete("@")] = gift.instance_variable_get(var) }та позбавляйтесь від них; hash
Narfanator

1
Я ніколи не зрозумію рубіновий фетиш each. mapі injectнабагато потужніші. Це одна особливість дизайну, який я маю з Ruby: mapі injectреалізований з each. Це просто погана інформатика.
Нейт Симер

Трохи лаконічніше:hash = Hash[gift.instance_variables.map { |var| [var.to_s[1..-1], gift.instance_variable_get(var)] } ]
Марвін

293

Просто скажіть (поточний об’єкт) .attributes

.attributesповертає hashбудь-яку object. І це набагато чистіше.


138
Зауважте, що це специфічний для ActiveModel метод, а не метод Ruby.
цегельник

6
У разі Sequel - використовуйте .values: sequel.jeremyevans.net/rdoc/classes/Sequel/Model/…
dimitarvp

instance_valuesможе використовуватися для всіх об'єктів рубіну для аналогічного виводу.
владика

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, оскільки # вказує на методи класу.
Калеб

6
Власне, ні. Документація RDoc говорить: Use :: for describing class methods, # for describing instance methods, and use . for example code(джерело: ruby-doc.org/documentation-guidelines.html ) Крім того, офіційна документація (як, наприклад, рубін CHANGELOG, github.com/ruby/ruby/blob/v2_1_0/NEWS ) використовує #для прикладу методи та крапку для методів класу досить послідовно.
levinalex

Будь ласка, використовуйте ін’єкцію замість цього антипаттера.
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. Зауважте, що Метт просив рубіновий шлях, конкретно не Рейки.
Крістофер Крейціг

28
Він також сказав, що сміливо дайте відповідь Рейлам ... так я і зробив.
Ерік Редстром

Бог побачив тут обидві версії;) Сподобалось
Себастьян Шюрман

17

Можна використовувати as_jsonметод. Він перетворить ваш об’єкт у хеш.

Але цей хеш прийде як значення для імені цього об'єкта як ключа. У вашому випадку,

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

Якщо вам потрібен хеш, який зберігається в об'єкті використання as_json(root: false). Я думаю, що за замовчуванням root буде хибним. Для отримання додаткової інформації зверніться до офіційного керівництва по рубіну

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


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. Здається, що там є корисна річ.
Магне

Метод атрибутів повертає новий хеш зі значеннями в - тому немає необхідності створювати інший у методі to_hash. Так: attribute_names.each_with_object ({}) {| ім'я, attrs | attrs [ім'я] = read_attribute (ім'я)}. Дивіться тут: github.com/rails/rails/blob/master/activerecord/lib/…
Кріс Кімптон

ви могли це зробити з картою, ваша реалізація побічних ефектів шкодить моїй людині розуму!
Нейт Симер

11

Якщо ви не перебуваєте в середовищі Rails (тобто не маєте ActiveRecord), це може бути корисним:

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

Можна написати дуже елегантне рішення, використовуючи функціональний стиль.

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.instan_variables


4

Рекурсивно конвертуйте ваші об'єкти в хеш, використовуючи "hashable" gem ( 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до 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

Я почав використовувати структури, щоб зробити легкі хеш-конверсії. Замість використання голої структури я створюю власний клас, що виходить з хеша, це дозволяє створювати власні функції, і він документує властивості класу.

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}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.