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.