Zoeken…


Invoering

Een hash is een woordenboekachtige verzameling unieke sleutels en hun waarden. Ze worden ook associatieve arrays genoemd en zijn vergelijkbaar met arrays, maar waar een array gehele getallen als index gebruikt, kunt u met een hash elk objecttype gebruiken. U haalt een nieuw item op in een hash of maakt er een aan door naar de sleutel te verwijzen.

Syntaxis

  • {first_name: "Noel", second_name: "Edmonds"}

  • {: first_name => "Noel",: second_name => "Edmonds"}

  • {"First Name" => "Noel", "Second Name" => "Edmonds"}

  • {first_key => first_value, second_key => second_value}

Opmerkingen

Hasht in Ruby-kaartsleutels naar waarden met behulp van een hashtabel.

Elk hash-object kan als sleutels worden gebruikt. Het is echter heel gebruikelijk om een Symbol te gebruiken, omdat het over het algemeen efficiënter is in verschillende Ruby-versies vanwege de verminderde objecttoewijzing.

{ key1: "foo", key2: "baz"  }

Een hash maken

Een hash in Ruby is een object dat een hashtabel implementeert en sleutels toewijst aan waarden. Ruby ondersteunt een specifieke letterlijke syntaxis voor het definiëren van hashes met {} :

my_hash = {}  # an empty hash
grades = { 'Mark' => 15, 'Jimmy' => 10, 'Jack' => 10 }

Een hash kan ook worden gemaakt met de standaard new methode:

my_hash = Hash.new  # any empty hash
my_hash = {}        # any empty hash

Hashes kunnen waarden van elk type hebben, inclusief complexe typen zoals arrays, objecten en 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}

Sleutels kunnen ook van elk type zijn, inclusief complexe:

mapping = { 'Mark' => 15, 5 => 10, [1, 2] => 9 }
mapping['Mark']  # => 15
mapping[[1, 2]]  # => 9

Symbolen worden vaak gebruikt als hashsleutels en Ruby 1.9 introduceerde een nieuwe syntaxis specifiek om dit proces te verkorten. De volgende hashes zijn gelijkwaardig:

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

De volgende hash (geldig in alle Ruby-versies) is anders , omdat alle sleutels tekenreeksen zijn:

grades = { "Mark" => 15, "Jimmy" => 10, "Jack" => 10 }

Hoewel beide syntaxisversies kunnen worden gemengd, wordt het volgende afgeraden.

mapping = { :length => 45, width: 10 }

Met Ruby 2.2+ is er een alternatieve syntaxis voor het maken van een hash met symbooltoetsen (handig als het symbool spaties bevat):

grades = { "Jimmy Choo": 10, :"Jack Sparrow": 10 }
# => { :"Jimmy Choo" => 10, :"Jack Sparrow" => 10}

Toegang tot waarden

Individuele waarden van een hash worden gelezen en geschreven met de methoden [] en []= :

my_hash = { length: 4, width: 5 }

my_hash[:length] #=> => 4

my_hash[:height] = 9

my_hash #=> {:length => 4, :width => 5, :height => 9 }

Toegang tot een sleutel die niet is toegevoegd aan de hash retourneert standaard nil , wat betekent dat het altijd veilig is om te proberen de waarde van een sleutel op te zoeken:

my_hash = {}

my_hash[:age] # => nil

Hashes kunnen ook sleutels in tekenreeksen bevatten. Als je probeert ze normaal te openen, zal het gewoon nil opleveren, in plaats daarvan krijg je toegang door hun string-toetsen:

my_hash = { "name" => "user" }

my_hash[:name]    # => nil
my_hash["name"]   # => user

Voor situaties waarin sleutels worden verwacht of vereist zijn, hebben hashes een fetch die een uitzondering genereert bij toegang tot een sleutel die niet bestaat:

my_hash = {}

my_hash.fetch(:age) #=> KeyError: key not found: :age

fetch accepteert een standaardwaarde als tweede argument, die wordt geretourneerd als de sleutel niet eerder is ingesteld:

my_hash =  {}
my_hash.fetch(:age, 45) #=> => 45

fetch kan ook een blok accepteren dat wordt geretourneerd als de sleutel niet eerder is ingesteld:

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 ondersteunt ook een store als alias voor []= :

my_hash = {}

my_hash.store(:age, 45)

my_hash #=> { :age => 45 }

Je kunt ook alle waarden van een hash ophalen met behulp van de values :

my_hash = { length: 4, width: 5 }

my_hash.values #=> [4, 5]

Opmerking: dit is alleen voor Ruby #dig is handig voor geneste Hash 's. Extraheert de geneste waarde die is opgegeven door de reeks idx-objecten door bij elke stap dig aan te roepen, waarbij nul wordt geretourneerd als een tussenstap nul is.

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

Standaardwaarden instellen

Als u de waarde probeert op te zoeken voor een sleutel die niet bestaat, wordt standaard nil geretourneerd. U kunt optioneel een andere waarde opgeven die moet worden geretourneerd (of een actie die moet worden ondernomen) wanneer de hash wordt geopend met een niet-bestaande sleutel. Hoewel dit "de standaardwaarde" wordt genoemd, hoeft het geen enkele waarde te zijn; het kan bijvoorbeeld een berekende waarde zijn, zoals de lengte van de sleutel.

De standaardwaarde van een hash kan worden doorgegeven aan de constructor:

h = Hash.new(0)

h[:hi] = 1 
puts h[:hi]  # => 1 
puts h[:bye] # => 0 returns default value instead of nil

Een standaard kan ook worden opgegeven voor een reeds gebouwde Hash:

my_hash = { human: 2, animal: 1 }
my_hash.default = 0
my_hash[:plant] # => 0

Het is belangrijk op te merken dat de standaardwaarde niet wordt gekopieerd telkens wanneer een nieuwe sleutel wordt gebruikt, wat kan leiden tot verrassende resultaten wanneer de standaardwaarde een referentietype is:

# 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']

Om dit probleem te omzeilen, accepteert de Hash-constructor een blok dat wordt uitgevoerd telkens wanneer een nieuwe sleutel wordt gebruikt en de geretourneerde waarde als standaard wordt gebruikt:

authors = Hash.new { [] }

# Note that we're using += instead of <<, see below
authors[:homer] += ['The Odyssey']
authors[:plato] # => []

authors # => {:homer=>["The Odyssey"]}

Merk op dat we hierboven + = moesten gebruiken in plaats van << omdat de standaardwaarde niet automatisch wordt toegewezen aan de hash; met behulp van << zou aan de array zijn toegevoegd, maar auteurs [: homer] zouden ongedefinieerd zijn gebleven:

authors[:homer] << 'The Odyssey' # ['The Odyssey']
authors[:homer] # => []
authors # => {}

Om standaardwaarden bij toegang toe te wijzen en om meer geavanceerde standaardwaarden te berekenen, wordt het standaardblok zowel de hash als de sleutel doorgegeven:

authors = Hash.new { |hash, key| hash[key] = [] }

authors[:homer] << 'The Odyssey'
authors[:plato] # => []

authors # => {:homer=>["The Odyssey"], :plato=>[]}

U kunt ook een standaardblok gebruiken om een actie uit te voeren en / of een waarde terug te geven die afhankelijk is van de sleutel (of andere gegevens):

chars = Hash.new { |hash,key| key.length }

chars[:test] # => 4

Je kunt zelfs complexere hashes maken:

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

Gebruik default_proc= om de standaardwaarde in te stellen op een Proc op een reeds bestaande hash:

authors = {}
authors.default_proc = proc { [] }

authors[:homer] += ['The Odyssey']
authors[:plato] # => []

authors # {:homer=>["The Odyssey"]}

Automatisch een Deep Hash maken

Hash heeft een standaardwaarde voor sleutels die worden aangevraagd maar niet bestaan (nul):

a = {}
p a[ :b ] # => nil 

Bij het maken van een nieuwe hash kan men de standaard opgeven:

b = Hash.new 'puppy'
p b[ :b ]            # => 'puppy'

Hash.new neemt ook een blok, waarmee je automatisch geneste hashes kunt maken, zoals Perl's autovivification-gedrag of 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 } } }

Sleutels en waarden wijzigen

Je kunt een nieuwe hash maken met de sleutels of waarden gewijzigd, je kunt inderdaad ook sleutels toevoegen of verwijderen met inject (AKA, verkleinen ). Om bijvoorbeeld een hash te produceren met stringed toetsen en hoofdletters:

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 is een opsomming, in wezen een verzameling sleutel / waarde-paren. Daarom is het heeft methoden zoals each , map en inject .

Voor elk sleutel / waarde-paar in de hash wordt het gegeven blok geëvalueerd, de waarde van memo bij de eerste run is de seed-waarde die wordt doorgegeven om te inject , in ons geval een lege hash, {} . De waarde van memo voor volgende evaluaties is de geretourneerde waarde van de vorige blokkenevaluatie, dit is de reden waarom we memo wijzigen door een sleutel met een waarde in te stellen en vervolgens memo aan het einde retourneren. De retourwaarde van de evaluatie van de laatste blokken is de retourwaarde van inject , in ons geval memo .

Om te voorkomen dat u de uiteindelijke waarde moet opgeven , kunt u in plaats daarvan elk_met_object gebruiken:

new_fruit = fruit.each_with_object({}) { |(k,v), memo| memo[k.to_s] = v.upcase }

Of zelfs kaart :

1.8
new_fruit = Hash[fruit.map{ |k,v| [k.to_s, v.upcase] }]

(Zie dit antwoord voor meer informatie, inclusief hoe je hashes op zijn plaats kunt manipuleren.)

Iets afsnijden over een hash

Een Hash omvat de Enumerable module, die verschillende iteratie werkwijzen, zoals bepaald: Enumerable#each , Enumerable#each_pair , Enumerable#each_key en Enumerable#each_value .

.each en .each_pair itereren over elk sleutel / waarde-paar:

h = { "first_name" => "John", "last_name" => "Doe" }
h.each do |key, value|
    puts "#{key} = #{value}"
end

# => first_name = John
#    last_name = Doe

.each_key herhaalt alleen de sleutels:

h = { "first_name" => "John", "last_name" => "Doe" }
h.each_key do |key|
  puts key
end

# => first_name
#    last_name

.each_value herhaalt alleen de waarden:

h = { "first_name" => "John", "last_name" => "Doe" }
h.each_value do |value|
    puts value
end

# => John
#    Doe

.each_with_index itereert over de elementen en geeft de index van de iteratie:

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

Conversie van en naar arrays

Hashes kunnen vrij worden omgezet van en naar arrays. Het omzetten van een hash van sleutel / waarde-paren in een array levert een array met geneste arrays voor paar:

{ :a => 1, :b => 2 }.to_a # => [[:a, 1], [:b, 2]]

In de tegenovergestelde richting kan een hash worden gemaakt op basis van een array met hetzelfde formaat:

[[:x, 3], [:y, 4]].to_h # => { :x => 3, :y => 4 }

Op dezelfde manier kunnen Hashes worden geïnitialiseerd met Hash[] en een lijst met afwisselende sleutels en waarden:

Hash[:a, 1, :b, 2] # => { :a => 1, :b => 2 }

Of uit een reeks arrays met elk twee waarden:

Hash[ [[:x, 3], [:y, 4]] ] # => { :x => 3, :y => 4 }

Hashes kunnen worden omgezet in een array van afwisselende sleutels en waarden met flatten() :

{ :a => 1, :b => 2 }.flatten # => [:a, 1, :b, 2]

Dankzij de eenvoudige conversie van en naar een array kan Hash goed werken met vele Enumerable methoden, zoals collect en zip :

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 sleutels of waarden van hash ophalen

{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-functie overschrijven

Ruby-hashes gebruiken de methoden hash en eql? om de hash-bewerking uit te voeren en objecten die zijn opgeslagen in de hash toe te wijzen aan interne hashbins. De standaardimplementatie van hash in Ruby is de murmur-hashfunctie over alle ledenvelden van het hashed-object . Om dit gedrag te onderdrukken is het mogelijk om hash en eql? te negeren eql? methoden.

Net als bij andere hash-implementaties, worden twee objecten a en b naar dezelfde bucket a.hash == b.hash als a.hash == b.hash en worden ze als identiek beschouwd als a.eql?(b) . Dus bij het opnieuw implementeren van hash en eql? moet men ervoor zorgen dat als a en b gelijk zijn onder eql? ze moeten dezelfde hash teruggeven. Anders kan dit resulteren in dubbele vermeldingen in een hash. Omgekeerd kan een slechte keuze in hash implementatie ertoe leiden dat veel objecten dezelfde hash-bucket delen, waardoor de O (1) eql? effectief wordt vernietigd en O (n) wordt veroorzaakt voor het aanroepen van eql? op alle objecten.

In het onderstaande voorbeeld wordt alleen het exemplaar van klasse A opgeslagen als een sleutel, omdat deze eerst is toegevoegd:

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 filteren

select retourneert een nieuwe hash met sleutel / waarde-paren waarvoor het blok de waarde true .

{ :a => 1, :b => 2, :c => 3 }.select { |k, v| k != :a && v.even? } # => { :b => 2 }

Wanneer u de sleutel of waarde in een filterblok niet nodig hebt, is de conventie om op die plaats een _ te gebruiken:

{ :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 retourneert een nieuwe hash met sleutel / waarde-paren waarvoor het blok als false evalueert:

{ :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 }

Bewerkingen op hash instellen

  • Kruising van hashes

    Om het snijpunt van twee hashes te krijgen, retourneert u de gedeelde sleutels waarvan de waarden gelijk zijn:

    hash1 = { :a => 1, :b => 2 }
    hash2 = { :b => 2, :c => 3 }
    hash1.select { |k, v| (hash2.include?(k) && hash2[k] == v) } # => { :b => 2 }
    
  • Union (samenvoegen) van hashes:

    sleutels in een hash zijn uniek, als een sleutel voorkomt in beide hashes die moeten worden samengevoegd, wordt die van de hash die merge wordt aangeroepen overschreven:

    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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow