Recherche…


Introduction

Un hachage est une collection de clés uniques de type dictionnaire et leurs valeurs. Aussi appelés tableaux associatifs, ils sont similaires aux tableaux, mais lorsqu'un tableau utilise des entiers comme index, un hachage vous permet d'utiliser n'importe quel type d'objet. Vous récupérez ou créez une nouvelle entrée dans un hachage en vous référant à sa clé.

Syntaxe

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

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

  • {"Prénom" => "Noel", "Second Name" => "Edmonds"}

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

Remarques

Hash in Ruby mappe les clés aux valeurs en utilisant une table de hachage.

Tout objet hashable peut être utilisé comme clé. Cependant, il est très courant d'utiliser un Symbol car il est généralement plus efficace dans plusieurs versions de Ruby, en raison de l'allocation d'objet réduite.

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

Créer un hash

Un hachage dans Ruby est un objet qui implémente une table de hachage , mappant les clés aux valeurs. Ruby prend en charge une syntaxe littérale spécifique pour la définition des hachages à l'aide de {} :

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

Un hachage peut également être créé en utilisant la new méthode standard:

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

Les hachages peuvent avoir des valeurs de tout type, y compris des types complexes tels que des tableaux, des objets et d'autres hachages:

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

Les clés peuvent également être de tout type, y compris les plus complexes:

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

Les symboles sont couramment utilisés comme clés de hachage et Ruby 1.9 introduit une nouvelle syntaxe spécifiquement pour raccourcir ce processus. Les hachages suivants sont équivalents:

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

Le hachage suivant (valide dans toutes les versions de Ruby) est différent , car toutes les clés sont des chaînes:

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

Bien que les deux versions de syntaxe puissent être mélangées, les opérations suivantes sont déconseillées.

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

Avec Ruby 2.2+, il existe une syntaxe alternative pour créer un hachage avec des clés de symbole (le plus utile si le symbole contient des espaces):

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

Accès aux valeurs

Les valeurs individuelles d'un hachage sont lues et écrites à l'aide des méthodes [] et []= :

my_hash = { length: 4, width: 5 }

my_hash[:length] #=> => 4

my_hash[:height] = 9

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

Par défaut, l'accès à une clé qui n'a pas été ajoutée au hachage renvoie nil , ce qui signifie qu'il est toujours prudent d'essayer de rechercher la valeur d'une clé:

my_hash = {}

my_hash[:age] # => nil

Les hachages peuvent également contenir des clés dans des chaînes. Si vous essayez d'y accéder normalement, il retournera simplement un nil , au lieu de cela vous y accéderez par leurs clés de chaîne:

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

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

Pour les situations où des clés sont attendues ou requises, les méthodes de hachage ont une méthode d' fetch qui génère une exception lors de l'accès à une clé qui n'existe pas:

my_hash = {}

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

fetch accepte une valeur par défaut comme deuxième argument, qui est renvoyé si la clé n'a pas été définie précédemment:

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

fetch peut également accepter un bloc qui est renvoyé si la clé n'a pas déjà été définie:

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

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

#=> Could not find age

Les hachages prennent également en charge une méthode de store tant qu'alias pour []= :

my_hash = {}

my_hash.store(:age, 45)

my_hash #=> { :age => 45 }

Vous pouvez également obtenir toutes les valeurs d'un hachage en utilisant la méthode values :

my_hash = { length: 4, width: 5 }

my_hash.values #=> [4, 5]

Note: Ceci est uniquement pour Ruby 2.3+ #dig est pratique pour les Hash imbriqués. Extrait la valeur imbriquée spécifiée par la séquence d'objets idx en appelant dig à chaque étape, renvoyant nil si une étape intermédiaire est nulle.

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

Définition des valeurs par défaut

Par défaut, tenter de rechercher la valeur d'une clé qui n'existe pas renverra nil . Vous pouvez éventuellement spécifier une autre valeur à renvoyer (ou une action à effectuer) lorsque le hachage est accessible avec une clé inexistante. Bien que cela soit appelé "la valeur par défaut", il ne doit pas nécessairement s'agir d'une valeur unique. il pourrait s'agir, par exemple, d'une valeur calculée telle que la longueur de la clé.

La valeur par défaut d'un hachage peut être transmise à son constructeur:

h = Hash.new(0)

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

Un défaut peut également être spécifié sur un hachage déjà construit:

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

Il est important de noter que la valeur par défaut n'est pas copiée à chaque fois qu'une nouvelle clé est utilisée, ce qui peut donner des résultats surprenants lorsque la valeur par défaut est un type de référence:

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

Pour contourner ce problème, le constructeur Hash accepte un bloc qui est exécuté à chaque accès à une nouvelle clé, et la valeur renvoyée est utilisée par défaut:

authors = Hash.new { [] }

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

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

Notez que ci-dessus, nous avons dû utiliser + = au lieu de << car la valeur par défaut n'est pas automatiquement attribuée au hachage; using << aurait ajouté au tableau, mais les auteurs [: homer] seraient restés indéfinis:

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

Pour pouvoir attribuer des valeurs par défaut à l’accès, ainsi que pour calculer des valeurs par défaut plus sophistiquées, le bloc par défaut est transmis à la fois au hachage et à la clé:

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

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

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

Vous pouvez également utiliser un bloc par défaut pour effectuer une action et / ou renvoyer une valeur dépendant de la clé (ou d'autres données):

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

chars[:test] # => 4

Vous pouvez même créer des hachages plus complexes:

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

Pour définir la valeur par défaut sur Proc sur un hachage déjà existant , utilisez default_proc= :

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

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

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

Création automatique d'un hachage profond

Hash a une valeur par défaut pour les clés qui sont demandées mais qui n'existent pas (nil):

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

Lors de la création d'un nouveau Hash, vous pouvez spécifier la valeur par défaut:

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

Hash.new prend également un bloc, ce qui vous permet de créer automatiquement des hachages imbriqués, tels que le comportement d'autovivification de Perl ou 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 } } }

Modification des clés et des valeurs

Vous pouvez créer un nouveau hachage avec les clés ou les valeurs modifiées, vous pouvez également ajouter ou supprimer des clés en utilisant inject (AKA, réduire ). Par exemple, pour produire un hachage avec des clés et des valeurs majuscules:

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 est un enumerable, en substance une collection de paires clé / valeur. Par conséquent, il existe des méthodes telles que each , map et inject .

Pour chaque paire clé / valeur du hachage, le bloc donné est évalué, la valeur de mémo lors de la première exécution est la valeur initiale transmise pour inject , dans notre cas, un hachage vide, {} . La valeur de memo pour les évaluations ultérieures est la valeur renvoyée de l'évaluation des blocs précédents, c'est pourquoi nous modifions le memo en définissant une clé avec une valeur, puis renvoyons le memo à la fin. La valeur de retour de l'évaluation des blocs finaux est la valeur de retour de inject , dans notre memo cas.

Pour éviter d'avoir à fournir la valeur finale, vous pouvez utiliser each_with_object à la place:

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

Ou même carte :

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

(Voir cette réponse pour plus de détails, y compris comment manipuler les hachages en place.)

Itérer sur un hachage

Un Hash inclut le module Enumerable , qui fournit plusieurs méthodes d'itération, telles que: Enumerable#each , Enumerable#each_pair , Enumerable#each_key et Enumerable#each_value .

.each et .each_pair chaque paire clé-valeur:

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

# => first_name = John
#    last_name = Doe

.each_key itère .each_key sur les clés:

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

# => first_name
#    last_name

.each_value itère .each_value sur les valeurs:

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

# => John
#    Doe

.each_with_index les éléments et fournit l'index de l'itération:

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

Conversion vers et depuis les tableaux

Les hachages peuvent être librement convertis vers et depuis les tableaux. La conversion d'un hachage de paires clé / valeur en un tableau produira un tableau contenant des tableaux imbriqués pour la paire:

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

Dans le sens inverse, un hachage peut être créé à partir d’un tableau du même format:

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

De même, les Hash peuvent être initialisés en utilisant Hash[] et une liste de clés et de valeurs en alternance:

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

Ou à partir d'un tableau de tableaux avec deux valeurs chacun:

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

Les hachages peuvent être reconvertis en un tableau de clés et de valeurs en alternance en utilisant flatten() :

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

La conversion facile vers et depuis un tableau permet à Hash de bien fonctionner avec de nombreuses méthodes Enumerable telles que collect et 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 }

Obtenir toutes les clés ou valeurs du hachage

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

Fonction de hachage prioritaire

Les hashes Ruby utilisent les méthodes hash et eql? pour effectuer l'opération de hachage et attribuer des objets stockés dans le hachage à des bacs de hachage internes. L'implémentation par défaut du hash dans Ruby est la fonction de hachage de murmure sur tous les champs membres de l'objet haché . Pour contourner ce comportement, il est possible de remplacer le hash et eql? méthodes.

Comme avec les autres implémentations de hachage, deux objets a et b seront hachés dans le même a.hash == b.hash si a.hash == b.hash et seront réputés identiques si a.eql?(b) . Ainsi, lors de la réimplémentation du hash et de l’ eql? il faut veiller à ce que si a et b sont égaux sous eql? ils doivent retourner la même valeur de hash . Sinon, cela pourrait entraîner des doublons dans un hachage. À l'inverse, un mauvais choix dans l'implémentation du hash peut conduire de nombreux objets à partager le même compartiment de hachage, détruisant ainsi le temps de recherche O (1) et provoquant l'appel de eql? O (n) eql? sur tous les objets.

Dans l'exemple ci-dessous, seule l'instance de la classe A est stockée en tant que clé, car elle a été ajoutée en premier:

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

Filtrage des hashs

select renvoie un nouveau hash avec des paires clé-valeur pour lesquelles le bloc est évalué comme true .

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

Lorsque vous n'avez pas besoin de la clé ou de la valeur dans un bloc de filtrage, la convention est d'utiliser un _ à cet endroit:

{ :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 renvoie un nouveau hash avec des paires clé-valeur pour lesquelles le bloc est évalué à 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 }

Définir les opérations sur les hachages

  • Intersection des Hashs

    Pour obtenir l'intersection de deux hachages, retournez les clés partagées dont les valeurs sont égales:

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

    les clés d'un hachage sont uniques, si une clé apparaît dans les deux hachages à fusionner, celle du hachage sur lequel la merge est appelée est écrasée:

    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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow