Лучший способ преобразования строк в символы в хеше

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

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

my_hash = YAML.load_file('yml')

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

my_hash[:key] 

Вместо

my_hash['key']

Ответ 1

Если вам нужен однострочный,

my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}

скопирует хэш в новый с символами, обозначенными символами.

Ответ 2

Здесь лучший метод, если вы используете Rails:

params. symbolize_keys

Конец.

Если вы этого не сделаете, просто отделите их код (он также находится в ссылке):

myhash.keys.each do |key|
  myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end

Ответ 3

Для конкретного случая YAML в Ruby, если ключи начинаются с ':', они будут автоматически интернированы как символы.

require 'yaml'
require 'pp'
yaml_str = "
connections:
  - host: host1.example.com
    port: 10000
  - host: host2.example.com
    port: 20000
"
yaml_sym = "
:connections:
  - :host: host1.example.com
    :port: 10000
  - :host: host2.example.com
    :port: 20000
"
pp yaml_str = YAML.load(yaml_str)
puts yaml_str.keys.first.class
pp yaml_sym = YAML.load(yaml_sym)
puts yaml_sym.keys.first.class

Вывод:

#  /opt/ruby-1.8.6-p287/bin/ruby ~/test.rb
{"connections"=>
  [{"port"=>10000, "host"=>"host1.example.com"},
   {"port"=>20000, "host"=>"host2.example.com"}]}
String
{:connections=>
  [{:port=>10000, :host=>"host1.example.com"},
   {:port=>20000, :host=>"host2.example.com"}]}
Symbol

Ответ 4

Еще более кратким:

Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]

Ответ 5

если вы используете Rails, это намного проще - вы можете использовать HashWithIndifferentAccess и обращаться к клавишам как как String, так и как Symbols:

my_hash.with_indifferent_access 

см. также:

http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html


Или вы можете использовать потрясающий "Facets of Ruby" Gem, который содержит много расширений для классов Ruby Core и Standard Library.

  require 'facets'
  > {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys
    =>  {:some=>"thing", :foo=>"bar}

см. также: http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class-Hash

Ответ 7

Здесь способ глубокой символизации объекта

def symbolize(obj)
    return obj.inject({}){|memo,(k,v)| memo[k.to_sym] =  symbolize(v); memo} if obj.is_a? Hash
    return obj.inject([]){|memo,v    | memo           << symbolize(v); memo} if obj.is_a? Array
    return obj
end

Ответ 8

Мне очень нравится Mash gem.

вы можете сделать mash['key'], или mash[:key], или mash.key

Ответ 9

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

Ответ 10

Модификация ответа @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

Ответ 11

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

def symbolize_keys(hash)
  hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end

Ответ 12

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

Ответ 13

Вы можете быть ленивым и обернуть его в 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}

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

Ответ 14

более короткий однострочный fwiw:

my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }

Ответ 15

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

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

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

Ответ 16

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

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

# my_hash['key'] => "val"
# my_hash[:key]  => "val"

Ответ 17

Массив, который мы хотим изменить.

strings = [ "HTML", "CSS", "JavaScript", "Python", "Ruby" ]

Создайте новую переменную как пустой массив, чтобы мы могли ".push" символы в.

symbols = []

Здесь мы определяем метод с блоком.

strings.each {| x | symbols.push(x.intern)}

Конец кода.

Итак, это, пожалуй, самый простой способ преобразования строк в символы в вашем массиве (-ах) в Ruby. Создайте массив строк, затем создайте новую переменную и установите переменную в пустой массив. Затем выберите каждый элемент в первом массиве, созданный с помощью метода ".each". Затем используйте блок-код для ".push" всех элементов в вашем новом массиве и используйте ".intern или .to_sym" для преобразования всех элементов в символы.

Символы быстрее, потому что они сохраняют больше памяти в вашем коде, и вы можете использовать их только один раз. Символы чаще всего используются для ключей в хеше, что отлично. Я не самый лучший программист на рубине, но эта форма кода мне очень помогла. Если кто-нибудь знает лучший способ, поделитесь ею, и вы тоже можете использовать этот метод для хэша!

Ответ 18

Это для людей, которые используют mruby и не имеют метода symbolize_keys:

class Hash
  def symbolize_keys!
    self.keys.each do |k|
      if self[k].is_a? Hash
        self[k].symbolize_keys!
      end
      if k.is_a? String
        raise RuntimeError, "Symbolizing key '#{k}' means overwrite some data (key :#{k} exists)" if self[k.to_sym]
        self[k.to_sym] = self[k]
        self.delete(k)
      end
    end
    return self
  end
end

Метод:

  • символизирует только клавиши String
  • если символизировать строку, означает потерять некоторую информацию (переписать часть хэша), поднять RuntimeError
  • символизирует также рекурсивно содержащиеся хеши
  • вернуть символический хэш
  • работает на месте!

Ответ 19

Если вы хотите использовать ванильное рубиновое решение, и поскольку у меня нет доступа к ActiveSupport, здесь представлено глубокое символическое решение (очень похожее на предыдущие)

    def deep_convert(element)
      return element.collect { |e| deep_convert(e) } if element.is_a?(Array)
      return element.inject({}) { |sh,(k,v)| sh[k.to_sym] = deep_convert(v); sh } if element.is_a?(Hash)
      element
    end

Ответ 20

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

Нет необходимости в Rails и работает с Ruby > 1.9

JSON.parse(my_json, :symbolize_names => true)

Ответ 21

ruby-1.9.2-p180 :001 > h = {'aaa' => 1, 'bbb' => 2}
 => {"aaa"=>1, "bbb"=>2} 
ruby-1.9.2-p180 :002 > Hash[h.map{|a| [a.first.to_sym, a.last]}]
 => {:aaa=>1, :bbb=>2}

Ответ 22

Это не совсем однострочный, но он превращает все строковые ключи в символы, а также вложенные:

def recursive_symbolize_keys(my_hash)
  case my_hash
  when Hash
    Hash[
      my_hash.map do |key, value|
        [ key.respond_to?(:to_sym) ? key.to_sym : key, recursive_symbolize_keys(value) ]
      end
    ]
  when Enumerable
    my_hash.map { |value| recursive_symbolize_keys(value) }
  else
    my_hash
  end
end

Ответ 23

Мне нравится этот однострочный шрифт, когда я не использую Rails, потому что тогда мне не нужно делать второй хэш и хранить два набора данных, пока я его обрабатываю:

my_hash = { "a" => 1, "b" => "string", "c" => true }

my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key) }

my_hash
=> {:a=>1, :b=>"string", :c=>true}

Hash # delete возвращает значение удаленной клавиши

Ответ 24

Графический хэш # deep_rekey также является хорошим вариантом, особенно:

  • если вы нашли применение для другого сахара из фасетов в вашем проекте,
  • если вы предпочитаете читаемость кода над криптовальными одностроками.

Пример:

require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey

Ответ 25

В ruby ​​я считаю, что это самый простой и понятный способ превратить строковые ключи в хэши в символы:

my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}

Для каждого ключа в хэше мы вызываем delete на нем, который удаляет его из хеша (также delete возвращает значение, связанное с ключом, которое было удалено), и мы сразу же установили его равным символическому ключ.

Ответ 26

{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

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

{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}

Ответ 27

symbolize_keys рекурсивно для любого хэша:

class Hash
  def symbolize_keys
    self.is_a?(Hash) ? Hash[ self.map { |k,v| [k.respond_to?(:to_sym) ? k.to_sym : k, v.is_a?(Hash) ? v.symbolize_keys : v] } ] : self
  end
end