it-swarm.com.de

Am besten konvertieren Sie Strings in Symbole in Hash

Was ist der (schnellste/sauberste/einfachste) Weg, um alle Schlüssel in einem Hash von Strings in Symbole in Ruby zu konvertieren?

Dies wäre praktisch, wenn Sie YAML analysieren.

my_hash = YAML.load_file('yml')

Ich möchte in der Lage sein zu verwenden:

my_hash[:key] 

Eher, als:

my_hash['key']
224
Bryan M.

Wenn Sie einen One-Liner wollen,

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

kopiert den Hash in einen neuen mit den symbolisierten Schlüsseln.

205
Sarah Mei

Hier ist eine bessere Methode, wenn Sie Rails verwenden:

params. symbolize_keys

Das Ende.

Wenn Sie nicht sind, reißen Sie einfach ihren Code ab (auch im Link):

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

Für den speziellen Fall von YAML in Ruby, wenn die Schlüssel mit ':' beginnen, werden sie automatisch als Symbole interniert.

 erfordern 'yaml' 
 erfordern 'pp' 
 yaml_str = "
 Verbindungen: 
 - Host: Host1.example.com 
 Port: 10000 
 - Host: Host2. example.com 
 Port: 20000 
 "
 yaml_sym =" 
: Verbindungen: 
 -: Host: Host1.example.com 
: Port: 10000 
 -: Host : Host2.example.com 
: Port: 20000 
 "
 Pp yaml_str = YAML.load (yaml_str) 
 Setzt yaml_str.keys.first.class 
 Pp yaml_sym = yaml_sym) 
 setzt yaml_sym.keys.first.class 

Ausgabe:

 # /opt/Ruby-1.8.6-p287/bin/Ruby ~/test.rb 
 {"verbindungen" => 
 [{"port" => 10000, "Host" => "Host1.example.com"}, 
 {"port" => 20000, "Host" => "Host2.example.com"}]} 
 String 
 {: verbindungen => 
 [{: port => 10000,: Host => "Host1.example.com"}, 
 {: port => 20000,: Host => "Host2.example.com"}]} 
 Symbol 
112
jrgm

Noch knapper:

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

wenn Sie Rails verwenden, ist es viel einfacher - Sie können einen HashWithIndifferentAccess verwenden und auf die Schlüssel sowohl als String als auch als Symbole zugreifen:

my_hash.with_indifferent_access 

siehe auch:

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


Oder Sie können den fantastischen "Facets of Ruby" Gem verwenden, der viele Erweiterungen der Klassen Ruby Core und Standard Library enthält.

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

siehe auch: http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class -Hash

54
Tilo

http://api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys

hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }
31
Ery

Seit Ruby 2.5.0 können Sie Hash#transform_keys oder Hash#transform_keys! verwenden.

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

Hier ist ein Weg, um ein Objekt tief zu symbolisieren

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
26
igorsales

Ich mag den Mash Edelstein wirklich.

sie können mash['key'] oder mash[:key] oder mash.key tun

20
ykaganovich

params.symbolize_keys wird auch funktionieren. Diese Methode wandelt Hash-Schlüssel in Symbole und gibt einen neuen Hash zurück.

11
Jae Cho

Eine Änderung an der Antwort von @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
10
Tony

Wenn Sie json verwenden und es als Hash verwenden möchten, können Sie dies in Core Ruby tun:

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

symbolize_names: Wenn auf true gesetzt, werden Symbole für die Namen (Schlüssel) in einem JSON-Objekt zurückgegeben. Andernfalls werden Zeichenfolgen zurückgegeben. Zeichenfolgen sind die Standardeinstellung.

Doc: Json # parse symbolize_names

10
Mark

Dies ist mein einziger Liner für verschachtelte Hashes

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
Nick Dobson

So viele Antworten hier, aber die eine Methode der Rails-Funktion ist hash.symbolize_keys

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

Konvertiert in:

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

Für den Fall, dass reason erforderlich ist, weil Ihre Daten ursprünglich von JSON stammen, können Sie diese Analyse überspringen, indem Sie beim Einlesen von JSON die Option :symbolize_names übergeben.

Keine Rails erforderlich und funktioniert mit Ruby> 1.9

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

Sie könnten faul sein und es in eine lambda einwickeln:

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

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

Dies würde jedoch nur zum Lesen aus dem Hash funktionieren - nicht zum Schreiben.

Dazu können Sie Hash#merge verwenden.

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

Der Init-Block konvertiert die Schlüssel einmalig auf Anforderung. Wenn Sie jedoch nach dem Zugriff auf die Symbolversion den Wert für die String-Version des Schlüssels aktualisieren, wird die Symbolversion nicht aktualisiert.

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}

Der Init-Block könnte den Hash auch nicht aktualisieren, was Sie vor solchen Fehlern schützen würde, aber Sie wären immer noch anfällig für das Gegenteil. Wenn Sie die Symbolversion aktualisieren, wird die String-Version nicht aktualisiert:

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}

Das Wichtigste dabei ist das Umschalten zwischen den beiden Schlüsselformen. Bleib bei einem.

4
rampion

ein kürzerer Einliner fwiw: 

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

Dies ist für Personen, die mruby verwenden und keine symbolize_keys-Methode definiert haben:

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

Die Methode:

  • symbolisiert nur Schlüssel, die String sind
  • wenn eine Zeichenfolge symbolisiert werden soll, um einige Informationen zu verlieren (überschreiben Sie einen Teil des Hashes), erhöhen Sie RuntimeError.
  • symbolisieren auch rekursiv enthaltene Hashes
  • gib den symbolisierten Hash zurück
  • arbeitet an Ort und Stelle!
2
Matteo Ragni

Wie wäre es damit:

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

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

Würde so etwas wie das folgende funktionieren?

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

Es wird den Hash kopieren, aber das interessiert Sie meistens nicht. Es gibt wahrscheinlich eine Möglichkeit, dies zu tun, ohne alle Daten zu kopieren.

2
ChrisInEdmonton

Das Array, das wir ändern möchten.

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

Erstellen Sie eine neue Variable als leeres Array, damit wir die Symbole in ".Push" können.

symbole = []

Hier definieren wir eine Methode mit einem Block.

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

Ende des Codes.

Dies ist wahrscheinlich der einfachste Weg, Zeichenfolgen in Symbole in Ihren Arrays in Ruby zu konvertieren. Erstellen Sie ein String-Array, erstellen Sie eine neue Variable und setzen Sie die Variable auf ein leeres Array. Wählen Sie dann jedes Element im ersten Array aus, das Sie mit der Methode ".each" erstellt haben. Verwenden Sie dann einen Blockcode, um alle Elemente in Ihrem neuen Array zu ".Push" und verwenden Sie ".intern" oder ".to_sym", um alle Elemente in Symbole zu konvertieren. 

Symbole sind schneller, da sie mehr Speicherplatz in Ihrem Code sparen und Sie nur einmal verwenden können. Symbole werden am häufigsten für Schlüssel in Hash verwendet, was großartig ist. Ich bin nicht der beste Ruby-Programmierer, aber diese Form des Codes hat mir sehr geholfen. Wenn jemand einen besseren Weg kennt, teilen Sie ihn bitte und Sie können diese Methode auch für Hash verwenden! 

1
rubyguest123

Wenn Sie eine Vanilla Ruby-Lösung möchten und da ich keinen Zugriff auf ActiveSupport habe, wird hier eine tiefe Lösung symbolisiert (sehr ähnlich der vorherigen)

    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
Haris Krajina

In Ruby finde ich, dass dies die einfachste und einfachste Methode ist, Zeichenfolgenschlüssel in Hashes in Symbole umzuwandeln: 

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

Für jeden Schlüssel im Hash rufen wir delete auf, der ihn aus dem Hash entfernt (auch delete gibt den mit dem Schlüssel verknüpften Wert zurück der gelöscht wurde) und setzen diesen Wert sofort dem symbolisierten Schlüssel.

0
user4488109

Ich mag diesen One-Liner, wenn ich Rails nicht verwende, weil ich dann keinen zweiten Hash machen und zwei Datensätze halten muss, während ich ihn verarbeite:

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 gibt den Wert des gelöschten Schlüssels zurück

0
nvts8a

Dies ist nicht gerade ein Einzeiler, aber es werden alle Zeichenkettenschlüssel in Symbole umgewandelt, auch die verschachtelten:

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

Ähnlich wie bei früheren Lösungen, jedoch etwas anders geschrieben.

  • Dies ermöglicht einen Hash, der verschachtelt ist und/oder Arrays enthält.
  • Bekommen Sie die Umwandlung von Schlüsseln in einen String als Bonus.
  • Code verändert den übergebenen Hash nicht.

    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
    
0
jham
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}
0
Vlad Khomich

Ab Psych 3.0 können Sie die Option symbolize_names: hinzufügen

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

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

Hinweis: Wenn Sie eine niedrigere Psych-Version als 3.0 haben, wird symbolize_names: still ignoriert.

Mein Ubuntu 18.04 enthält es mit Ruby 2.5.1p57

0

Facets 'Hash # deep_rekey ist auch eine gute Option, vor allem:

  • wenn Sie in Ihrem Projekt für anderen Zucker aus Facetten Verwendung finden,
  • wenn Sie die Lesbarkeit des Codes gegenüber kryptischen Einzeilen bevorzugen.

Probe:

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