Ruby Language
Hashes
Suche…
Einführung
Ein Hash ist eine wörterbuchartige Sammlung eindeutiger Schlüssel und ihrer Werte. Sie werden auch assoziative Arrays genannt und ähneln Arrays. Wenn ein Array jedoch Ganzzahlen als Index verwendet, können Sie mit einem Hash einen beliebigen Objekttyp verwenden. Sie rufen einen neuen Eintrag ab oder erstellen einen neuen Eintrag in einem Hash, indem Sie auf seinen Schlüssel verweisen.
Syntax
{first_name: "Noel", zweiter Name: "Edmonds"}
{: first_name => "Noel",: second_name => "Edmonds"}
{"Vorname" => "Noel", "Zweiter Name" => "Edmonds"}
{first_key => first_value, second_key => second_value}
Bemerkungen
Hashes in Ruby ordnen Schlüssel mithilfe einer Hashtabelle zu.
Jedes Hash-Objekt kann als Schlüssel verwendet werden. Es ist jedoch üblich, ein Symbol
da es in der Regel in mehreren Ruby-Versionen effizienter ist, da die Objektzuordnung reduziert ist.
{ key1: "foo", key2: "baz" }
Hash erstellen
Ein Hash in Ruby ist ein Objekt, das eine Hash-Tabelle implementiert und Schlüssel zu Werten abbildet. Ruby unterstützt eine bestimmte Literal-Syntax zum Definieren von Hashes mit {}
:
my_hash = {} # an empty hash
grades = { 'Mark' => 15, 'Jimmy' => 10, 'Jack' => 10 }
Ein Hash kann auch mit der new
Standardmethode erstellt werden:
my_hash = Hash.new # any empty hash
my_hash = {} # any empty hash
Hashes können Werte jeden Typs haben, einschließlich komplexer Typen wie Arrays, Objekte und andere Hashes:
mapping = { 'Mark' => 15, 'Jimmy' => [3,4], 'Nika' => {'a' => 3, 'b' => 5} }
mapping['Mark'] # => 15
mapping['Jimmy'] # => [3, 4]
mapping['Nika'] # => {"a"=>3, "b"=>5}
Schlüssel können auch beliebig sein, auch komplexe:
mapping = { 'Mark' => 15, 5 => 10, [1, 2] => 9 }
mapping['Mark'] # => 15
mapping[[1, 2]] # => 9
Symbole werden häufig als Hash-Schlüssel verwendet, und Ruby 1.9 führte eine neue Syntax ein, um diesen Vorgang zu verkürzen. Die folgenden Hashes sind gleichwertig:
# Valid on all Ruby versions
grades = { :Mark => 15, :Jimmy => 10, :Jack => 10 }
# Valid in Ruby version 1.9+
grades = { Mark: 15, Jimmy: 10, Jack: 10 }
Der folgende Hash (gültig in allen Ruby-Versionen) unterscheidet sich , da alle Schlüssel Strings sind:
grades = { "Mark" => 15, "Jimmy" => 10, "Jack" => 10 }
Während beide Syntaxversionen gemischt werden können, wird davon abgeraten.
mapping = { :length => 45, width: 10 }
Mit Ruby 2.2+ gibt es eine alternative Syntax zum Erstellen eines Hash mit Symbolschlüsseln (am nützlichsten, wenn das Symbol Leerzeichen enthält):
grades = { "Jimmy Choo": 10, :"Jack Sparrow": 10 }
# => { :"Jimmy Choo" => 10, :"Jack Sparrow" => 10}
Auf Werte zugreifen
Einzelne Werte eines Hash werden mit den Methoden []
und []=
gelesen und geschrieben:
my_hash = { length: 4, width: 5 }
my_hash[:length] #=> => 4
my_hash[:height] = 9
my_hash #=> {:length => 4, :width => 5, :height => 9 }
Wenn Sie auf einen Schlüssel zugreifen, der nicht zum Hash hinzugefügt wurde, wird standardmäßig nil
bedeutet, dass Sie immer den Wert eines Schlüssels nachschlagen können:
my_hash = {}
my_hash[:age] # => nil
Hashes können auch Schlüssel in Strings enthalten. Wenn Sie versuchen, auf sie normalerweise zuzugreifen, wird nur eine nil
, stattdessen greifen Sie mit ihren Zeichenfolgeschlüsseln darauf zu:
my_hash = { "name" => "user" }
my_hash[:name] # => nil
my_hash["name"] # => user
In Situationen, in denen Schlüssel erwartet werden oder benötigt werden müssen, verfügen Hashes über eine fetch
, die beim Zugriff auf einen nicht vorhandenen Schlüssel eine Ausnahme auslöst:
my_hash = {}
my_hash.fetch(:age) #=> KeyError: key not found: :age
Als zweites Argument akzeptiert fetch
einen Standardwert, der zurückgegeben wird, wenn der Schlüssel noch nicht festgelegt wurde:
my_hash = {}
my_hash.fetch(:age, 45) #=> => 45
fetch
kann auch einen Block akzeptieren, der zurückgegeben wird, wenn der Schlüssel noch nicht gesetzt wurde:
my_hash = {}
my_hash.fetch(:age) { 21 } #=> 21
my_hash.fetch(:age) do |k|
puts "Could not find #{k}"
end
#=> Could not find age
Hashes unterstützen auch eine store
Methode als Alias für []=
:
my_hash = {}
my_hash.store(:age, 45)
my_hash #=> { :age => 45 }
Sie können auch alle Werte eines Hashes mit der Methode values
abrufen:
my_hash = { length: 4, width: 5 }
my_hash.values #=> [4, 5]
Hinweis: Dies ist nur für Ruby 2.3+ #dig
für verschachtelte praktisch ist Hash
s. Extrahiert den verschachtelten Wert, der in der Sequenz der idx-Objekte angegeben ist, indem bei jedem Schritt dig aufgerufen wird. Wird ein Zwischenschritt gleich null, wird Null zurückgegeben.
h = { foo: {bar: {baz: 1}}}
h.dig(:foo, :bar, :baz) # => 1
h.dig(:foo, :zot, :xyz) # => nil
g = { foo: [10, 11, 12] }
g.dig(:foo, 1) # => 11
Standardwerte festlegen
Wenn Sie versuchen, den Wert eines Schlüssels nachzuschlagen, der nicht vorhanden ist, wird standardmäßig nil
. Sie können optional einen anderen Wert angeben, der zurückgegeben werden soll (oder eine auszuführende Aktion), wenn auf den Hash mit einem nicht vorhandenen Schlüssel zugegriffen wird. Obwohl dies als "Standardwert" bezeichnet wird, muss es kein einzelner Wert sein. es könnte sich zum Beispiel um einen berechneten Wert wie die Länge des Schlüssels handeln.
Der Standardwert eines Hash kann an seinen Konstruktor übergeben werden:
h = Hash.new(0)
h[:hi] = 1
puts h[:hi] # => 1
puts h[:bye] # => 0 returns default value instead of nil
Ein Standard kann auch für einen bereits erstellten Hash angegeben werden:
my_hash = { human: 2, animal: 1 }
my_hash.default = 0
my_hash[:plant] # => 0
Beachten Sie, dass der Standardwert nicht bei jedem Zugriff auf einen neuen Schlüssel kopiert wird. Dies kann zu überraschenden Ergebnissen führen, wenn der Standardwert ein Referenztyp ist:
# Use an empty array as the default value
authors = Hash.new([])
# Append a book title
authors[:homer] << 'The Odyssey'
# All new keys map to a reference to the same array:
authors[:plato] # => ['The Odyssey']
Um dieses Problem zu umgehen, akzeptiert der Hash-Konstruktor einen Block, der bei jedem Zugriff auf einen neuen Schlüssel ausgeführt wird. Der zurückgegebene Wert wird als Standardwert verwendet:
authors = Hash.new { [] }
# Note that we're using += instead of <<, see below
authors[:homer] += ['The Odyssey']
authors[:plato] # => []
authors # => {:homer=>["The Odyssey"]}
Beachten Sie, dass wir oben + anstelle von << verwenden mussten, da der Standardwert nicht automatisch dem Hash zugewiesen wird. mit << hätte das Array hinzugefügt, aber Autoren [: homer] wären undefiniert geblieben:
authors[:homer] << 'The Odyssey' # ['The Odyssey']
authors[:homer] # => []
authors # => {}
Um Standardwerte für den Zugriff zuweisen zu können und komplexere Standardwerte zu berechnen, werden dem Standardblock sowohl der Hash als auch der Schlüssel übergeben:
authors = Hash.new { |hash, key| hash[key] = [] }
authors[:homer] << 'The Odyssey'
authors[:plato] # => []
authors # => {:homer=>["The Odyssey"], :plato=>[]}
Sie können auch einen Standardblock verwenden, um eine Aktion auszuführen und / oder einen vom Schlüssel (oder einigen anderen Daten) abhängigen Wert zurückzugeben:
chars = Hash.new { |hash,key| key.length }
chars[:test] # => 4
Sie können sogar komplexere Hashes erstellen:
page_views = Hash.new { |hash, key| hash[key] = { count: 0, url: key } }
page_views["http://example.com"][:count] += 1
page_views # => {"http://example.com"=>{:count=>1, :url=>"http://example.com"}}
Verwenden Sie default_proc=
, um den Standardwert für einen Proc für einen bereits vorhandenen Hash default_proc=
:
authors = {}
authors.default_proc = proc { [] }
authors[:homer] += ['The Odyssey']
authors[:plato] # => []
authors # {:homer=>["The Odyssey"]}
Deep Hash automatisch erstellen
Hash hat einen Standardwert für Schlüssel, die angefordert werden, aber nicht existieren (Null):
a = {}
p a[ :b ] # => nil
Beim Erstellen eines neuen Hash kann man den Standard angeben:
b = Hash.new 'puppy'
p b[ :b ] # => 'puppy'
Hash.new nimmt auch einen Block, mit dem Sie automatisch verschachtelte Hashes erstellen können, z. B. das automatische Verhalten von Perl oder mkdir -p
# h is the hash you're creating, and k the key.
#
hash = Hash.new { |h, k| h[k] = Hash.new &h.default_proc }
hash[ :a ][ :b ][ :c ] = 3
p hash # => { a: { b: { c: 3 } } }
Schlüssel und Werte ändern
Sie können einen neuen Hash mit den geänderten Schlüsseln oder Werten erstellen. Tatsächlich können Sie auch Schlüssel mithilfe von inj (AKA, verkleinern ) hinzufügen oder löschen. So erzeugen Sie beispielsweise einen Hash mit stringifizierten Schlüsseln und Großbuchstaben:
fruit = { name: 'apple', color: 'green', shape: 'round' }
# => {:name=>"apple", :color=>"green", :shape=>"round"}
new_fruit = fruit.inject({}) { |memo, (k,v)| memo[k.to_s] = v.upcase; memo }
# => new_fruit is {"name"=>"APPLE", "color"=>"GREEN", "shape"=>"ROUND"}
Hash ist eine Zahl, im Wesentlichen eine Sammlung von Schlüssel / Wert-Paaren. Daher hat es Methoden wie each
, map
und inject
.
Für jedes Schlüssel / Wert-Paar im Hash wird der angegebene Block ausgewertet. Der Wert von memo im ersten Durchlauf ist der Startwert, der an inject
, in unserem Fall ein leerer Hash, {}
. Der memo
für nachfolgende Auswertungen ist der zurückgegebene Wert der vorherigen Blockauswertung. Deshalb ändern wir das memo
indem wir einen Schlüssel mit einem Wert setzen und am Ende ein memo
. Der Rückgabewert der Endblockauswertung ist der Rückgabewert von inject
, in unserem Fall memo
.
Um zu vermeiden, dass Sie den endgültigen Wert angeben müssen , können Sie stattdessen each_with_object verwenden:
new_fruit = fruit.each_with_object({}) { |(k,v), memo| memo[k.to_s] = v.upcase }
Oder sogar eine Karte :
new_fruit = Hash[fruit.map{ |k,v| [k.to_s, v.upcase] }]
(In dieser Antwort finden Sie weitere Informationen, einschließlich der Manipulation von Hashes.)
Iteration über einen Hash
Ein Hash
umfasst die Enumerable
- Modul, das mehrere Iterationsverfahren wie bestimmt: Enumerable#each
, Enumerable#each_pair
, Enumerable#each_key
und Enumerable#each_value
.
.each
und .each_pair
jedes Schlüsselwertpaar:
h = { "first_name" => "John", "last_name" => "Doe" }
h.each do |key, value|
puts "#{key} = #{value}"
end
# => first_name = John
# last_name = Doe
.each_key
iteriert nur über die Tasten:
h = { "first_name" => "John", "last_name" => "Doe" }
h.each_key do |key|
puts key
end
# => first_name
# last_name
.each_value
nur die Werte:
h = { "first_name" => "John", "last_name" => "Doe" }
h.each_value do |value|
puts value
end
# => John
# Doe
.each_with_index
die Elemente und stellt den Index der Iteration bereit:
h = { "first_name" => "John", "last_name" => "Doe" }
h.each_with_index do |(key, value), index|
puts "index: #{index} | key: #{key} | value: #{value}"
end
# => index: 0 | key: first_name | value: John
# index: 1 | key: last_name | value: Doe
Konvertierung von und zu Arrays
Hashes können in Arrays und von Arrays frei konvertiert werden. Beim Konvertieren eines Hashes von Schlüssel / Wert-Paaren in ein Array wird ein Array mit verschachtelten Arrays für Paare erzeugt:
{ :a => 1, :b => 2 }.to_a # => [[:a, 1], [:b, 2]]
In umgekehrter Richtung kann ein Hash aus einem Array des gleichen Formats erstellt werden:
[[:x, 3], [:y, 4]].to_h # => { :x => 3, :y => 4 }
In ähnlicher Weise können Hashes mit Hash[]
und einer Liste alternierender Schlüssel und Werte initialisiert werden:
Hash[:a, 1, :b, 2] # => { :a => 1, :b => 2 }
Oder aus einem Array von Arrays mit jeweils zwei Werten:
Hash[ [[:x, 3], [:y, 4]] ] # => { :x => 3, :y => 4 }
Hashes können mit flatten()
wieder in ein Array alternierender Schlüssel und Werte umgewandelt werden:
{ :a => 1, :b => 2 }.flatten # => [:a, 1, :b, 2]
Die einfache Konvertierung in und aus einem Array ermöglicht es Hash
, mit vielen Enumerable
Methoden wie collect
und zip
gut zu Enumerable
:
Hash[('a'..'z').collect{ |c| [c, c.upcase] }] # => { 'a' => 'A', 'b' => 'B', ... }
people = ['Alice', 'Bob', 'Eve']
height = [5.7, 6.0, 4.9]
Hash[people.zip(height)] # => { 'Alice' => 5.7, 'Bob' => '6.0', 'Eve' => 4.9 }
Alle Schlüssel oder Werte von Hash erhalten
{foo: 'bar', biz: 'baz'}.keys # => [:foo, :biz]
{foo: 'bar', biz: 'baz'}.values # => ["bar", "baz"]
{foo: 'bar', biz: 'baz'}.to_a # => [[:foo, "bar"], [:biz, "baz"]]
{foo: 'bar', biz: 'baz'}.each #<Enumerator: {:foo=>"bar", :biz=>"baz"}:each>
Hash-Funktion überschreiben
Ruby-Hashes verwenden die Methoden hash
und eql?
um die Hash-Operation durchzuführen und den im Hash gespeicherten Objekten internen Hash-Bins zuzuordnen. Die Standardimplementierung von hash
in Ruby ist die murmur Hash-Funktion über alle Member-Felder des Hash-Objekts . Um dieses Verhalten zu überschreiben, ist es möglich, hash
und eql?
zu überschreiben eql?
Methoden.
Wie bei anderen Hash-Implementierungen werden zwei Objekte a und b bei a.hash == b.hash
auf den gleichen Bucket a.hash == b.hash
und für a.eql?(b)
als identisch a.eql?(b)
. Wenn also hash
und eql?
reimplementiert werden eql?
sollte man darauf achten, dass a
und b
unter eql?
gleich eql?
Sie müssen denselben hash
zurückgeben. Andernfalls kann dies zu doppelten Einträgen in einem Hash führen. Umgekehrt kann eine schlechte Wahl bei der hash
Implementierung dazu führen, dass viele Objekte denselben Hash-Bucket gemeinsam verwenden, wodurch die O (1) eql?
effektiv zerstört wird und O (n) für den Aufruf von eql?
auf alle Objekte.
Im folgenden Beispiel wird nur die Instanz von Klasse A
als Schlüssel gespeichert, da sie zuerst hinzugefügt wurde:
class A
def initialize(hash_value)
@hash_value = hash_value
end
def hash
@hash_value # Return the value given externally
end
def eql?(b)
self.hash == b.hash
end
end
class B < A
end
a = A.new(1)
b = B.new(1)
h = {}
h[a] = 1
h[b] = 2
raise "error" unless h.size == 1
raise "error" unless h.include? b
raise "error" unless h.include? a
Hashes filtern
select
gibt einen neuen hash
mit Schlüssel-Wert-Paaren zurück, für den der Block den Wert true
ergibt.
{ :a => 1, :b => 2, :c => 3 }.select { |k, v| k != :a && v.even? } # => { :b => 2 }
Wenn Sie den Schlüssel oder Wert in einem Filterblock nicht benötigen, verwenden Sie an dieser Stelle ein _
:
{ :a => 1, :b => 2, :c => 3 }.select { |_, v| v.even? } # => { :b => 2 }
{ :a => 1, :b => 2, :c => 3 }.select { |k, _| k == :c } # => { :c => 3 }
reject
gibt einen neuen hash
mit Schlüssel-Wert-Paaren zurück, für den der Block den Wert false
ergibt:
{ :a => 1, :b => 2, :c => 3 }.reject { |_, v| v.even? } # => { :a => 1, :c => 3 }
{ :a => 1, :b => 2, :c => 3 }.reject { |k, _| k == :b } # => { :a => 1, :c => 3 }
Operationen für Hashes festlegen
Überschneidung von Hashes
Um den Schnittpunkt zweier Hashes zu erhalten, geben Sie die gemeinsam genutzten Schlüssel zurück, deren Werte gleich sind:
hash1 = { :a => 1, :b => 2 } hash2 = { :b => 2, :c => 3 } hash1.select { |k, v| (hash2.include?(k) && hash2[k] == v) } # => { :b => 2 }
Vereinigung (Zusammenführung) von Hashes:
Schlüssel in einem Hash sind eindeutig, wenn ein Schlüssel in beiden Hashes vorkommt, die zusammengeführt werden sollen, wird derjenige aus dem Hash, für den die
merge
aufgerufen wird, überschrieben:hash1 = { :a => 1, :b => 2 } hash2 = { :b => 4, :c => 3 } hash1.merge(hash2) # => { :a => 1, :b => 4, :c => 3 } hash2.merge(hash1) # => { :b => 2, :c => 3, :a => 1 }