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 :

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


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow