Najlepszy sposób na konwersję ciągów znaków na symbole w skrócie


250

Jaki jest (najszybszy / najczystszy / prosty) sposób konwersji wszystkich kluczy w haszu z ciągów znaków na symbole w Rubim?

Przydałoby się to podczas analizowania YAML.

my_hash = YAML.load_file('yml')

Chciałbym móc użyć:

my_hash[:key] 

Zamiast:

my_hash['key']


80
hash.symbolize_keysi hash.deep_symbolize_keyswykonaj pracę, jeśli używasz Railsów.
Zaz

Josh, gdybyś umieścił swój komentarz w odpowiedzi, głosowałbym za tobą. wymagają „szyn”; hash.deep_symbolize_keys działa całkiem dobrze w IRB lub PRY. : D
Douglas G. Allen,

Odpowiedzi:


239

W Ruby> = 2.5 ( dokumenty ) możesz użyć:

my_hash.transform_keys(&:to_sym)

Używasz starszej wersji Ruby? Oto linijka, która skopiuje skrót do nowego z symbolami kluczy:

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

Z Railsami możesz używać:

my_hash.symbolize_keys
my_hash.deep_symbolize_keys 

5
Ach, przepraszam, że jestem niejasny - zastrzyk nie modyfikuje dzwoniącego. Musisz zrobić my_hash = my_hash.inject ({}) {| memo, (k, v) | memo [k.to_sym] = v; memo}
Sarah Mei

4
Właśnie tego szukałem. Trochę go zmodyfikowałem i dodałem kilka linii, aby nawet tworzyć symbole w zagnieżdżonych skrótach. Spójrz tutaj, jeśli jesteś zainteresowany: any-where.de/blog/ruby-hash-convert-string-keys-to-symbols
Matt

37
W Ruby 1.9 możesz używać each_with_objecttak:my_hash.each_with_object({}){|(k,v), h| h[k.to_sym] = v}
sgtFloyd

8
to nie obsługuje rekurencyjnych skrótów ... Znajdź jednorazowy, ale nie DRY.
baash05

8
@BryanM. Przyszedłem do tej dyskusji bardzo późno :-), ale możesz również użyć tej .tapmetody, aby usunąć potrzebę zaliczenia memona końcu. Stworzyłem oczyszczoną wersję wszystkich rozwiązań (także rekurencyjnych) gist.github.com/Integralist/9503099
Integralist

307

Oto lepsza metoda, jeśli używasz Railsów:

parametry. symbolize_keys

Koniec.

Jeśli nie, po prostu oderwij ich kod (znajduje się również w linku):

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


45
Nie będzie symbolizować zagnieżdżonych skrótów.
oma

3
Zamieniłem link symbolize_keysna nowy i działający (Rails 3) adres URL. Pierwotnie właśnie poprawiłem adres URL to_options, ale pod tym linkiem nie ma żadnej dokumentacji. symbolize_keysfaktycznie ma opis, więc użyłem go zamiast tego.
Craig Walker

23
deep_symbolize_keys! . Działa na szynach 2+
mastaBlasta

14
Dla tych, którzy są ciekawi, jak zrobić odwrotnie, hash.stringify_keysdziała.
Nick

112

W konkretnym przypadku YAML w Ruby, jeśli klawisze zaczynają się od „ :”, zostaną automatycznie internowane jako symbole.

wymagają „yaml”
wymagają „pp”
yaml_str = "
znajomości:
  - host: host1.przyklad.com
    port: 10000
  - host: host2.przyklad.com
    port: 20000
„
yaml_sym = "
:znajomości:
  -: host: host1.przyklad.com
    : port: 10000
  -: host: host2.przyklad.com
    : port: 20000
„
pp yaml_str = YAML.load (yaml_str)
stawia yaml_str.keys.first.class
pp yaml_sym = YAML.load (yaml_sym)
stawia yaml_sym.keys.first.class

Wynik:

# /opt/ruby-1.8.6-p287/bin/ruby ~ / test.rb
{„połączenia” =>
  [{„port” => 10000, „host” => „host1.przyklad.com”},
   {„port” => 20000, „host” => „host2.example.com”}]}
Strunowy
{: połączenia =>
  [{: port => 10000,: host => „host1.przyklad.com”},
   {: port => 20000,: host => „host2.example.com”}]}
Symbol

15
Słodkie! Czy istnieje sposób na ustawienie YAML#load_filedomyślnych wszystkich klawiszy symboli zamiast ciągów bez konieczności rozpoczynania każdego klawisza dwukropkiem?
ma11hew28

63

Jeszcze bardziej zwięzłe:

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

6
To wydaje się oczywistym wyborem.
Casey Watson,

12
Nie symbolizuje zagnieżdżonych skrótów.
rgtk

15
Bardziej czytelna wersja:my_hash.map { |k, v| [k.to_sym, v] }.to_h
Dennis

60

jeśli używasz Railsów, jest to o wiele prostsze - możesz użyć HashWithIndifferentAccess i uzyskać dostęp do kluczy zarówno jako String, jak i Symbole:

my_hash.with_indifferent_access 

Zobacz też:

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


Możesz też użyć niesamowitego klejnotu „Facets of Ruby”, który zawiera wiele rozszerzeń klas Ruby Core i bibliotek standardowych.

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

patrz także: http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class-Hash


2
W rzeczywistości robi to odwrotnie. Konwertuje z symbolu na ciąg. Aby przekonwertować na symbol, użyj my_hash.symbolize_keys
Espen

#symbolize_keys działa tylko w Railsach - nie w zwykłym Ruby / irb. Zauważ też, że #symbolize_keys nie działa na głęboko zagnieżdżonych skrótach.
Tilo

54

Ponieważ Ruby 2.5.0możesz użyć Hash#transform_keyslub Hash#transform_keys!.

{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}

Współpracuje również z transform_values apidock.com/rails/Hash/transform_values . To naprawdę miłe, ponieważ wydaje się, że nie ma odpowiednika do modyfikowania wartości, jak w przypadku stringify_keyslub symbolize_keys.
Mike Vallano,

jest sposób na głęboką symbolizację kluczy
Abhay Kumar

To powinno być wybrane rozwiązanie po 2018 r.
Ivan Wang,


26

Oto sposób na głęboką symbolizację obiektu

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

fajnie, pójdę z tym, nawet jeśli zmienię jego nazwę deep_symbolize :)
PierrOz


19

Jeśli używasz jsona i chcesz go używać jako skrótu, w rdzeniu Ruby możesz to zrobić:

json_obj = JSON.parse(json_str, symbolize_names: true)

symbolize_names : Jeśli ustawione na true, zwraca symbole dla nazw (kluczy) w obiekcie JSON. W przeciwnym razie zwracane są łańcuchy. Ciągi są domyślne.

Doc: Json # parsuj symbolize_names


symbol_hash = JSON.parse(JSON.generate(YAML.safe_load(FILENAME)), symbolize_names: true)jest dość SUCHYM (ale nieefektywnym) sposobem szybkiego uzyskania skrótu z zagnieżdżonymi kluczami jako symbolami, jeśli pochodzi z pliku YAML.
Cameron Gagnon

12

params.symbolize_keysbędzie również działać. Ta metoda zamienia klucze skrótu w symbole i zwraca nowy skrót.


26
Ta metoda nie jest rdzeniem Ruby. Jest to metoda Railsowa.
Ricardo Acras,

10

Modyfikacja odpowiedzi @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

Byłoby pomocne, gdybyś podał powód modyfikacji obiektu dla osób skanujących odpowiedzi.
Dbz

9

W Railsach możesz użyć:

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

Konwertuje na:

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

3
deep_symbolize_keysjest dodany w rozszerzeniu Hash Rails , ale nie jest częścią rdzenia Ruby.
Ryan Crispin Heneise


7

To moja jedyna wkładka do zagnieżdżonych skrótów

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

Do twojej wiadomości, działa tylko dla Railsów. W takim przypadku HashWithIndifferentAccess może być lepszą alternatywą.
Adam Grant

6

W przypadku, gdy powodem trzeba to zrobić, ponieważ dane pochodzi z JSON, można pominąć żadnej z tego parsowania po prostu przechodząc w :symbolize_namesopcji po spożyciu JSON.

Nie wymaga Railsów i działa z Ruby> 1.9

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

4

Możesz być leniwy i owinąć to lambda:

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

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

Ale działałoby to tylko do czytania z hasza - nie do pisania.

Aby to zrobić, możesz użyć Hash#merge

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

Blok inicjujący dokona konwersji kluczy jeden raz na żądanie, ale jeśli zaktualizujesz wartość wersji ciągu klucza po uzyskaniu dostępu do wersji symbolu, wersja symbolu nie zostanie zaktualizowana.

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}

Możliwe, że blok inicjujący nie zaktualizuje skrótu, co uchroni Cię przed tego rodzaju błędem, ale nadal będziesz narażony na działanie przeciwne - aktualizacja wersji symbolu nie zaktualizuje wersji łańcuchowej:

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}

Dlatego należy zachować ostrożność przy przełączaniu między tymi dwoma kluczowymi formami. Trzymaj się jednego.


3

Czy coś podobnego do poniższej pracy?

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

Skopiuje skrót, ale przez większość czasu nie będziesz się tym przejmować. Prawdopodobnie istnieje sposób, aby to zrobić bez kopiowania wszystkich danych.


3

krótszy jednowarstwowy fwiw:

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

2

Co powiesz na to:

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

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

2

To jest dla osób, które używają mrubyi nie mają symbolize_keyszdefiniowanej żadnej metody:

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

Metoda:

  • symbolizuje tylko klucze, które są String
  • jeśli symbolizowanie łańcucha oznacza utratę niektórych informacji (zastąpienie części skrótu) RuntimeError
  • symbolizują również rekurencyjnie zawarte skróty
  • zwraca symbolizowany skrót
  • działa na miejscu!

1
Jest literówka w swojej metodzie zapomniałeś !in symbolize_keys. W przeciwnym razie działa dobrze.
Ka Mok

1

Tablica, którą chcemy zmienić.

strings = [„HTML”, „CSS”, „JavaScript”, „Python”, „Ruby”]

Utwórz nową zmienną jako pustą tablicę, abyśmy mogli „.pchować” symbole.

symbole = []

Tutaj definiujemy metodę z blokiem.

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

Koniec kodu.

Jest to więc prawdopodobnie najprostszy sposób na konwersję ciągów znaków na symbole w tablicy (ach) w Rubim. Utwórz tablicę ciągów, a następnie utwórz nową zmienną i ustaw zmienną na pustą tablicę. Następnie wybierz każdy element w pierwszej tablicy utworzonej za pomocą metody „.each”. Następnie użyj kodu blokowego do „.push” wszystkich elementów w nowej tablicy i użyj „.intern lub .to_sym”, aby przekonwertować wszystkie elementy na symbole.

Symbole są szybsze, ponieważ oszczędzają więcej pamięci w kodzie i można ich użyć tylko raz. Symbole są najczęściej używane dla kluczy w skrócie, co jest świetne. Nie jestem najlepszym programistą ruby, ale ta forma kodu bardzo mi pomogła. Jeśli ktoś zna lepszy sposób, proszę udostępnić i możesz użyć tej metody do mieszania!


2
Pytanie dotyczyło skrótów, a nie tablic.
Richard_G,

1

Jeśli chcesz rozwiązanie waniliowo-rubinowe, a ponieważ nie mam dostępu do tego ActiveSupportmiejsca, głęboko symbolizuj rozwiązanie (bardzo podobne do poprzednich)

    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

1

Począwszy od wersji Psych 3.0 możesz dodać opcję symbolize_names:

Psych.load("---\n foo: bar") # => {"foo"=>"bar"}

Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}

Uwaga: jeśli masz wersję Psych niższą niż 3.0, symbolize_names:zostanie po cichu zignorowana.

Moje Ubuntu 18.04 zawiera go po wyjęciu z pudełka z ruby ​​2.5.1p57


0
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}

Możesz owinąć anawiasy kwadratowe, aby rozłożyć argument blokowy i uczynić go jeszcze bardziej zwięzłym. Zobacz na przykład moją odpowiedź.
Michael Barton

0

To nie jest dokładnie jedna linijka, ale zamienia wszystkie klucze ciągów w symbole, także te zagnieżdżone:

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

0

Podoba mi się ten liner, gdy nie używam Railsów, ponieważ wtedy nie muszę tworzyć drugiego skrótu i ​​przechowywać dwóch zestawów danych podczas jego przetwarzania:

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 zwraca wartość usuniętego klucza


0

Dobra funkcja Facets 'Hash # deep_rekey jest również dobrą opcją, szczególnie:

  • jeśli w swoim projekcie znajdziesz zastosowanie innego cukru pochodzącego z różnych aspektów,
  • jeśli wolisz czytelność kodu od kryptycznych jedno-liniowych.

Próba:

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

0

W ruby ​​uważam, że jest to najprostszy i najłatwiejszy do zrozumienia sposób na przekształcenie skrótów klawiszowych w skróty na symbole:

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

Dla każdego klucza w skrócie wywołujemy na nim delete, który usuwa go z skrótu (również delete zwraca wartość powiązaną z kluczem, który został usunięty) i natychmiast ustawiamy to na wartość klucza symbolizowanego.


0

Podobne do poprzednich rozwiązań, ale napisane nieco inaczej.

  • Pozwala to na zagnieżdżenie i / lub tablice.
  • Uzyskaj konwersję kluczy na ciąg jako bonus.
  • Kod nie mutuje hasha, który został przekazany.

    module HashUtils
      def symbolize_keys(hash)
        transformer_function = ->(key) { key.to_sym }
        transform_keys(hash, transformer_function)
      end
    
      def stringify_keys(hash)
        transformer_function = ->(key) { key.to_s }
        transform_keys(hash, transformer_function)
      end
    
      def transform_keys(obj, transformer_function)
        case obj
        when Array
          obj.map{|value| transform_keys(value, transformer_function)}
        when Hash
          obj.each_with_object({}) do |(key, value), hash|
            hash[transformer_function.call(key)] = transform_keys(value, transformer_function)
          end
        else
          obj
        end
      end
    end
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.