Ricerca…


introduzione

Un hash è un insieme simile a un dizionario di chiavi uniche e dei loro valori. Chiamati anche array associativi, sono simili agli array, ma laddove una matrice usa numeri interi come indice, un hash consente di utilizzare qualsiasi tipo di oggetto. Puoi recuperare o creare una nuova voce in un hash facendo riferimento alla sua chiave.

Sintassi

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

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

  • {"Nome" => "Noel", "Secondo nome" => "Edmonds"}

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

Osservazioni

Gli hash in Ruby mappano i valori usando una tabella hash.

Qualsiasi oggetto lavabile può essere usato come chiave. Tuttavia, è molto comune utilizzare un Symbol in quanto è generalmente più efficiente in diverse versioni di Ruby, a causa della ridotta allocazione degli oggetti.

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

Creare un hash

Un hash in Ruby è un oggetto che implementa una tabella hash , mappando le chiavi ai valori. Ruby supporta una specifica sintassi letterale per definire gli hash utilizzando {} :

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

Un hash può anche essere creato usando il new metodo standard:

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

Gli hash possono avere valori di qualsiasi tipo, inclusi tipi complessi come matrici, oggetti e altri hash:

mapping = { 'Mark' => 15, 'Jimmy' => [3,4], 'Nika' => {'a' => 3, 'b' => 5} }
mapping['Mark']   # => 15
mapping['Jimmy']  # => [3, 4]
mapping['Nika']   # => {"a"=>3, "b"=>5}

Anche le chiavi possono essere di qualsiasi tipo, incluse quelle complesse:

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

I simboli sono comunemente usati come chiavi hash e Ruby 1.9 ha introdotto una nuova sintassi specifica per abbreviare questo processo. I seguenti hash sono equivalenti:

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

Il seguente hash (valido in tutte le versioni di Ruby) è diverso , poiché tutte le chiavi sono stringhe:

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

Sebbene entrambe le versioni di sintassi possano essere miste, quanto segue è sconsigliato.

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

Con Ruby 2.2+, esiste una sintassi alternativa per la creazione di un hash con i tasti simbolo (utile soprattutto se il simbolo contiene spazi):

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

Accesso ai valori

I singoli valori di un hash vengono letti e scritti usando i metodi [] e []= :

my_hash = { length: 4, width: 5 }

my_hash[:length] #=> => 4

my_hash[:height] = 9

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

Per impostazione predefinita, l'accesso a una chiave che non è stata aggiunta all'hash restituisce nil , il che significa che è sempre possibile tentare di cercare il valore di una chiave:

my_hash = {}

my_hash[:age] # => nil

Gli hash possono anche contenere chiavi nelle stringhe. Se si tenta di accedere normalmente, verrà restituito un valore nil , ma sarà possibile accedervi tramite le relative chiavi stringa:

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

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

Per le situazioni in cui sono previste o necessarie le chiavi, gli hash hanno un metodo di fetch che genera un'eccezione quando si accede a una chiave che non esiste:

my_hash = {}

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

fetch accetta un valore predefinito come secondo argomento, che viene restituito se la chiave non è stata precedentemente impostata:

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

fetch può anche accettare un blocco che viene restituito se la chiave non è stata precedentemente impostata:

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

my_hash.fetch(:age) do |k|
  puts "Could not find #{k}"
end

#=> Could not find age

Gli hash supportano anche un metodo store come alias per []= :

my_hash = {}

my_hash.store(:age, 45)

my_hash #=> { :age => 45 }

Puoi anche ottenere tutti i valori di un hash utilizzando il metodo dei values :

my_hash = { length: 4, width: 5 }

my_hash.values #=> [4, 5]

Nota: questo è solo per Ruby 2.3+ #dig è utile per gli Hash nidificati. Estrae il valore nidificato specificato dalla sequenza di oggetti idx chiamando dig a ogni passaggio, restituendo nil se qualsiasi passaggio intermedio è nullo.

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

Impostazione dei valori predefiniti

Per impostazione predefinita, il tentativo di cercare il valore per una chiave che non esiste restituirà nil . È possibile specificare facoltativamente un altro valore da restituire (o un'azione da eseguire) quando si accede all'hash con una chiave inesistente. Anche se questo è indicato come "il valore predefinito", non è necessario che sia un singolo valore; potrebbe, ad esempio, essere un valore calcolato come la lunghezza della chiave.

Il valore predefinito di un hash può essere passato al suo costruttore:

h = Hash.new(0)

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

Un valore predefinito può anche essere specificato su un hash già creato:

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

È importante notare che il valore predefinito non viene copiato ogni volta che si accede a una nuova chiave, il che può portare a risultati sorprendenti quando il valore predefinito è un tipo di riferimento:

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

Per ovviare a questo problema, il costruttore Hash accetta un blocco che viene eseguito ogni volta che si accede a una nuova chiave e il valore restituito viene utilizzato come predefinito:

authors = Hash.new { [] }

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

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

Nota che sopra abbiamo dovuto usare + = invece di << perché il valore predefinito non è assegnato automaticamente all'hash; usando << sarebbe stato aggiunto all'array, ma gli autori [: homer] sarebbero rimasti indefiniti:

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

Per poter assegnare valori predefiniti all'accesso, nonché per calcolare valori predefiniti più sofisticati, il blocco predefinito viene passato sia all'hash che alla chiave:

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

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

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

Puoi anche utilizzare un blocco predefinito per eseguire un'azione e / o restituire un valore dipendente dalla chiave (o da altri dati):

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

chars[:test] # => 4

Puoi persino creare hash più complessi:

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

Per impostare l'impostazione predefinita su Proc su un hash già esistente , usa default_proc= :

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

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

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

Creazione automatica di un Deep Hash

Hash ha un valore predefinito per le chiavi che vengono richieste ma non esistono (nil):

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

Quando si crea un nuovo hash, è possibile specificare il valore predefinito:

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

Anche Hash.new accetta un blocco, che consente di creare automaticamente hash annidati, come il comportamento di autovivificazione di Perl o 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 } } }

Modifica di chiavi e valori

Puoi creare un nuovo hash con le chiavi o i valori modificati, infatti puoi anche aggiungere o eliminare i tasti, usando inject (AKA, reduce ). Ad esempio per produrre un hash con chiavi stringificate e valori maiuscoli:

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 è un enumerabile, in sostanza una raccolta di coppie chiave / valore. Pertanto ha metodi come each , map e inject .

Per ogni coppia chiave / valore nell'hash viene valutato il blocco dato, il valore del memo alla prima esecuzione è il valore seme passato per inject , nel nostro caso un hash vuoto, {} . Il valore del memo per le valutazioni successive è il valore restituito della valutazione dei blocchi precedenti, questo è il motivo per cui modifichiamo il memo impostando una chiave con un valore e quindi restituendo il memo alla fine. Il valore di ritorno della valutazione dei blocchi finali è il valore di ritorno inject , nel nostro caso memo .

Per evitare di dover fornire il valore finale, puoi utilizzare invece each_with_object :

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

O anche la mappa :

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

(Vedi questa risposta per maggiori dettagli, incluso come manipolare gli hash sul posto.)

Iterating Over a Hash

Un Hash include il modulo Enumerable , che fornisce diversi metodi di iterazione, come: Enumerable#each , Enumerable#each_pair , Enumerable#each_key e Enumerable#each_value .

.each e .each_pair iterano su ciascuna coppia chiave-valore:

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

# => first_name = John
#    last_name = Doe

.each_key iterazioni solo sulle chiavi:

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

# => first_name
#    last_name

.each_value iterazioni solo sui valori:

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

# => John
#    Doe

.each_with_index iterazioni sugli elementi e fornisce l'indice dell'iterazione:

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

Conversione da e verso le matrici

Gli hash possono essere convertiti liberamente da e verso gli array. La conversione di un hash di coppie chiave / valore in una matrice produrrà un array contenente array annidati per la coppia:

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

Nella direzione opposta, è possibile creare un hash da una matrice dello stesso formato:

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

Allo stesso modo, gli hash possono essere inizializzati usando Hash[] e un elenco di chiavi e valori alternati:

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

O da una matrice di matrici con due valori ciascuno:

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

Gli hash possono essere riconvertiti in una matrice di chiavi e valori alternati usando flatten() :

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

La semplice conversione da e verso un array consente a Hash di funzionare bene con molti metodi Enumerable come collect e 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 }

Ottenere tutte le chiavi o valori di hash

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

Sovrascrivere la funzione di hash

Gli hash di Ruby usano i metodi hash ed eql? per eseguire l'operazione di hash e assegnare gli oggetti memorizzati nell'hash ai contenitori interni di hash. L'implementazione predefinita hash in Ruby è la funzione di hashing del soffio su tutti i campi membri dell'oggetto hash . Per sovrascrivere questo comportamento è possibile sovrascrivere hash ed eql? metodi.

Come con altre implementazioni di hash, due oggetti a e b saranno sottoposti a hashing sullo stesso bucket se a.hash == b.hash e saranno considerati identici se a.eql?(b) . Quindi, quando si reimplementa hash ed eql? si dovrebbe fare attenzione a garantire che a e b siano uguali sotto eql? devono restituire lo stesso valore di hash . Altrimenti ciò potrebbe causare voci duplicate in un hash. Viceversa, una scelta sbagliata nell'implementazione hash potrebbe portare molti oggetti a condividere lo stesso bucket hash, distruggendo efficacemente il tempo di ricerca O (1) e causando O (n) per chiamare eql? su tutti gli oggetti.

Nell'esempio seguente solo l'istanza della classe A è memorizzata come chiave, poiché è stata aggiunta per prima:

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

Filtro degli hash

select restituisce un nuovo hash con coppie chiave-valore per le quali il blocco restituisce true .

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

Quando non avrai bisogno della chiave o del valore in un blocco filtro, la convenzione è di usare _ in quel punto:

{ :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 un nuovo hash con coppie chiave-valore per le quali il blocco restituisce false :

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

Imposta le operazioni sugli hash

  • Intersezione di Hash

    Per ottenere l'intersezione di due hash, restituisci le chiavi condivise i cui valori sono uguali:

    hash1 = { :a => 1, :b => 2 }
    hash2 = { :b => 2, :c => 3 }
    hash1.select { |k, v| (hash2.include?(k) && hash2[k] == v) } # => { :b => 2 }
    
  • Unione (unione) di hash:

    le chiavi in ​​un hash sono univoche, se una chiave si verifica in entrambi gli hash che devono essere uniti, quello dell'hash con cui viene eseguita l' merge viene sovrascritto:

    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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow