Ruby конвертирует объект в хэш


127

Допустим, у меня есть Giftобъект с @name = "book"& @price = 15.95. Как лучше всего преобразовать это в хэш {name: "book", price: 15.95}в Ruby, а не в Rails (хотя не стесняйтесь давать и ответ Rails)?


16
Подойдет ли @ gift.attributes.to_options?
Mr. L

1
1) Является ли подарок объектом ActiveRecord? 2) можем ли мы предположить, что @ name / @ price - это не только переменные экземпляра, но и средства доступа для чтения? 3) вы хотите только имя и цену или все атрибуты в подарке, какими бы они ни были?
Tokland

@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.instance_variables.inject ({}) {| hash, var | хэш [var.to_s.delete ("@")] = gift.instance_variable_get (var); hash}
Jordan

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
следа

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

6
В случае с Sequel - используйте .values: sequel.jeremyevans.net/rdoc/classes/Sequel/Model/…
dimitarvp

instance_valuesможет использоваться для всех объектов Ruby для аналогичного вывода.
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, поскольку # указывает методы класса.
Калеб

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

Пожалуйста, используйте инъекцию вместо этого антипаттерна.
YoTengoUnLCD

each_with_objectinstance_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
Это Rails, которого у самого Ruby нет instance_values. Обратите внимание, что Мэтт попросил использовать Ruby, а не Rails.
Christopher Creutzig

28
Он также сказал, что не стесняйтесь давать ответ Rails ... я так и сделал.
Эрик Ридстром

Боже, чтобы увидеть здесь обе версии;) Понравилось
Себастьян Шюрманн

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

Для объектов 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

а потом просто позвони

gift.to_hash()
purch.to_hash() 

2
забавно, что это не часть структуры Rails. Кажется, это полезно иметь там.
Magne

Метод атрибутов возвращает новый хэш со значениями внутри - поэтому нет необходимости создавать другой в методе to_hash. Примерно так: attribute_names.each_with_object ({}) {| name, attrs | attrs [имя] = атрибут чтения (имя)}. См. Здесь: github.com/rails/rails/blob/master/activerecord/lib/…
Крис Кимптон

вы могли бы сделать это с помощью карты, ваша реализация побочного эффекта задевает мой разум!
Nate Symer


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


4

Рекурсивно преобразуйте ваши объекты в хэш с помощью хешируемого гема ( 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

Я начал использовать структуры, чтобы упростить хэш-преобразования. Вместо использования простой структуры я создаю свой собственный класс, производный от хэша, это позволяет вам создавать свои собственные функции и документировать свойства класса.

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.