Лучший способ конвертировать строки в символы в хэше - PullRequest
231 голосов
/ 29 апреля 2009

Какой (самый быстрый / чистый / простой) способ преобразовать все ключи в хэше из строк в символы в Ruby?

Это было бы удобно при разборе YAML.

my_hash = YAML.load_file('yml')

Я бы хотел использовать:

my_hash[:key] 

Вместо:

my_hash['key']

Ответы [ 31 ]

11 голосов
/ 31 декабря 2010

params.symbolize_keys также будет работать. Этот метод превращает ключи хеша в символы и возвращает новый хеш.

10 голосов
/ 04 апреля 2013

Модификация ответа @igorsales

class Object
  def deep_symbolize_keys
    return self.inject({}){|memo,(k,v)| memo[k.to_sym] = v.deep_symbolize_keys; memo} if self.is_a? Hash
    return self.inject([]){|memo,v    | memo           << v.deep_symbolize_keys; memo} if self.is_a? Array
    return self
  end
end
8 голосов
/ 31 октября 2017
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

Преобразует в:

{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
7 голосов
/ 19 августа 2016

Это мой единственный вкладыш для вложенных хэшей

def symbolize_keys(hash)
  hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end
7 голосов
/ 20 мая 2016

Здесь так много ответов, но функция rails с одним методом: hash.symbolize_keys

5 голосов
/ 17 июля 2017

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

Rails не требуется и работает с Ruby> 1.9

JSON.parse(my_json, :symbolize_names => true)
4 голосов
/ 29 апреля 2009

Вы можете быть ленивым, и оберните это в lambda:

my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }

my_lamb[:a] == my_hash['a'] #=> true

Но это будет работать только для чтения из хэша, а не для записи.

Для этого вы можете использовать Hash#merge

my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))

Блок init будет преобразовывать ключи один раз по требованию, хотя если вы обновите значение строковой версии ключа после доступа к версии символа, версия символа не будет обновлена.

irb> x = { 'a' => 1, 'b' => 2 }
#=> {"a"=>1, "b"=>2}
irb> y = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(x)
#=> {"a"=>1, "b"=>2}
irb> y[:a]  # the key :a doesn't exist for y, so the init block is called
#=> 1
irb> y
#=> {"a"=>1, :a=>1, "b"=>2}
irb> y[:a]  # the key :a now exists for y, so the init block is isn't called
#=> 1
irb> y['a'] = 3
#=> 3
irb> y
#=> {"a"=>3, :a=>1, "b"=>2}

Вы также можете сделать так, чтобы блок init не обновлял хеш, что защитило бы вас от ошибок такого рода, но вы все равно были бы уязвимы к противоположному - обновление версии символа не обновило бы строковую версию: *

irb> q = { 'c' => 4, 'd' => 5 }
#=> {"c"=>4, "d"=>5}
irb> r = Hash.new { |h,k| h[k.to_s] }.merge(q)
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called
#=> 4
irb> r
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called again, since this key still isn't in r
#=> 4
irb> r[:c] = 7
#=> 7
irb> r
#=> {:c=>7, "c"=>4, "d"=>5}

Так что с этим следует быть внимательным, переключаясь между двумя ключевыми формами. Палка с одним.

3 голосов
/ 29 апреля 2009

Хотели бы что-нибудь вроде следующей работы?

new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }

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

3 голосов
/ 16 января 2014

более короткий однострочный выпуск:

my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
2 голосов
/ 17 декабря 2010

Как насчет этого:

my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))

# my_hash['key'] => "val"
# my_hash[:key]  => "val"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...