Frage

Was ist der (schnellst / sauberste / einfach) Weg, um alle Schlüssel in einem Hash von Strings Symbolen in Ruby zu konvertieren?

Dies wäre praktisch, wenn YAML Parsen.

my_hash = YAML.load_file('yml')

Ich möchte verwenden können:

my_hash[:key] 

Statt:

my_hash['key']
War es hilfreich?

Lösung

Wenn Sie einen Einzeiler,

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

wird der Hash in eine neue mit den Tasten kopieren symbolisiert.

Andere Tipps

Hier ist eine bessere Methode, wenn Sie Rails verwenden:

params. symbolize_keys

Das Ende.

Wenn Sie nicht nur ihren Code Abzocke (es ist auch in Link):

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

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

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

Ausgabe:

#  /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

Noch kurz und bündig:

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

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

my_hash.with_indifferent_access 

siehe auch:

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


Sie können auch die awesome „Facetten von Ruby“ Gem verwenden, die eine Menge Erweiterungen Ruby-Entwicklung und Standard Library-Klassen 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

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

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

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

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

Hier ist ein Weg, um tief ein Objekt symbolisiert

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

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

Eine Änderung @igorsales beantworten

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
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

Wandelt an:

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

So viele Antworten hier, aber die eine Methode Schienen Funktion ist hash.symbolize_keys

Das ist mein Motto für verschachteltes 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

Bei der Grund müssen Sie dies zu tun ist, weil Ihre Daten ursprünglich von JSON kam, Sie gerade vorbei in der :symbolize_names Option jede dieser Parsing überspringen könnte JSON auf die Einnahme.

Keine Rails erforderlich und arbeiten mit Rubin> 1.9

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

Sie könnten faul sein, und wickeln Sie es in einem lambda:

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 aber nur funktionieren, für aus dem Hash Lesen -. Nicht schreiben

Um das zu tun, könnten Sie Hash#merge verwenden

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

Der init-Block wird die Schlüssel einmal auf Nachfrage konvertieren, obwohl, wenn Sie den Wert für die String-Version des Schlüssels aktualisieren, nachdem das Symbol Version zugreifen, wird das Symbol Version nicht aktualisiert werden.

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}

Sie könnte auch die init-Block den Hash nicht aktualisieren, die Sie von dieser Art von Fehler schützen würde, aber Sie würden immer noch auf der gegenüberliegenden anfällig sein - das Symbol Version zu aktualisieren würde die String-Version nicht aktualisiert werden:

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}

So ist die Sache vorsichtig zu sein, mit diesen zwischen den beiden Schlüsselformen wechseln. Stick mit einem.

wäre so etwas wie die folgende Arbeit?

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

Es wird der Hash kopieren, aber Sie werden nicht kümmern uns um, dass die meiste Zeit. Wahrscheinlich gibt es eine Möglichkeit, es zu tun, ohne alle Daten zu kopieren.

ein kürzerer Einzeiler FWIW:

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

Wie wäre es damit:

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

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

Dies ist für Leute, die mruby verwendet und haben keine symbolize_keys Methode definiert:

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 symbolisieren bedeutet, einige Informationen zu verlieren (Teil des Hash überschreiben) erhöhen, um eine RuntimeError
  • symbolisieren auch rekursiv Hashes enthalten
  • Rückkehr der symbolisierten Hash
  • arbeitet an Ort und Stelle!

Das Array wollen wir ändern.

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

Erstellen Sie eine neue Variable als leeres Array so können wir ".push" die Symbole in.

Symbole = []

Hier ist, wo wir ein Verfahren mit einem Block definieren.

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

Ende des Codes.

Das ist also wahrscheinlich die einfachste Art und Weise Strings Symbole in Ihrem Array (s) in Ruby zu konvertieren. Machen Sie ein Array von Strings dann eine neue Variable machen und die Variable auf ein leeres Array gesetzt. Dann wählen die jedes Element in dem ersten Array mit dem Sie „.each“ -Methode erstellt. Dann mit einem Blockcode auf „.push“ alle Elemente in Ihrem neuen Array und verwenden „.intern oder .to_sym“ alle Elemente Symbole zu konvertieren.

Die Symbole sind schneller, weil sie mehr Speicher in Ihrem Code speichern und Sie können sie nur einmal verwenden. Symbole werden am häufigsten für Schlüssel in Hash verwendet, die groß ist. Ich bin das nicht die beste Rubin Programmierer, aber diese Form des Code hat mir geholfen, ein lot.If jemand weiß, einen besseren Weg, bitte teilen und Sie können diese Methode für Hash verwenden!

Wenn Sie Vanille Rubin Lösung gefällt und wie mir den Zugang nicht hier ActiveSupport haben, ist tief symbolize Lösung (sehr ähnlich wie die 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

Ab Psych 3.0 können Sie fügen Sie den symbolize_names: Option

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

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

. Hinweis: wenn Sie eine niedrigere Psych Version haben als 3,0 symbolize_names: wird stillschweigend ignoriert

Meine Ubuntu 18.04 enthält es aus der Box mit Ruby 2.5.1p57

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}

Dies ist nicht gerade ein Einzeiler, aber es stellt sich alle String-Schlüssel in Symbole, auch die verschachtelten Einsen:

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

Ich mag diese Einzeiler, wenn ich nicht Rails verwenden, denn dann muss ich nicht einen zweiten Hash machen und zwei Sätze von Daten halten, während ich es bin Verarbeitung:

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 # gibt den Wert des gelöschten Schlüssel löschen

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

  • Wenn Sie für anderen Zucker von Facetten in Ihrem Projekt verwenden finden,
  • , wenn Sie die Lesbarkeit des Codes über kryptische Einzeiler bevorzugen.

Beispiel:

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

In Ruby finde ich das die einfache und leicht verständliche Art und Weise sein String-Schlüssel in Hashes Symbole zu drehen:

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

Für jeden Schlüssel in der Hash wir sie löschen nennen, die es aus dem Hash entfernt (auch löschen gibt den Wert im Zusammenhang mit der Taste , die gelöscht wurde), und wir setzen sofort diese gleich der symbolisierten Taste.

ähnlich wie bei bisherigen Lösungen, aber ein bisschen anders geschrieben.

  • Dies ermöglicht einen Hash, die verschachtelten und / oder hat Arrays.
  • Get Umwandlung von Schlüsseln in einen String als Bonus.
  • -Code wird der Hash nicht mutiert worden übergeben.

    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
    
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top