Buscar..


Introducción

A Hash es una colección similar a un diccionario de claves únicas y sus valores. También llamados arrays asociativos, son similares a Arrays, pero cuando un Array usa enteros como su índice, un Hash le permite usar cualquier tipo de objeto. Usted recupera o crea una nueva entrada en un Hash refiriéndose a su clave.

Sintaxis

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

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

  • {"Nombre" => "Noel", "Segundo nombre" => "Edmonds"}

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

Observaciones

Los hash en Ruby asignan claves a valores utilizando una tabla hash.

Cualquier objeto hashable se puede utilizar como claves. Sin embargo, es muy común usar un Symbol ya que generalmente es más eficiente en varias versiones de Ruby, debido a la reducción de la asignación de objetos.

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

Creando un hash

Un hash en Ruby es un objeto que implementa una tabla hash , asignando claves a valores. Ruby admite una sintaxis literal específica para definir hashes utilizando {} :

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

También se puede crear un hash usando el new método estándar:

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

Los hash pueden tener valores de cualquier tipo, incluidos tipos complejos como matrices, objetos y otros 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}

También las claves pueden ser de cualquier tipo, incluidas las complejas:

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

Los símbolos se usan comúnmente como claves hash, y Ruby 1.9 introdujo una nueva sintaxis específicamente para acortar este proceso. Los siguientes hashes son equivalentes:

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

El siguiente hash (válido en todas las versiones de Ruby) es diferente , porque todas las claves son cadenas:

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

Si bien se pueden mezclar ambas versiones de sintaxis, se desaconseja lo siguiente.

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

Con Ruby 2.2+, hay una sintaxis alternativa para crear un hash con teclas de símbolo (más útil si el símbolo contiene espacios):

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

Valores de acceso

Los valores individuales de un hash se leen y escriben utilizando los métodos [] y []= :

my_hash = { length: 4, width: 5 }

my_hash[:length] #=> => 4

my_hash[:height] = 9

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

De forma predeterminada, el acceso a una clave que no se ha agregado al hash devuelve nil , lo que significa que siempre es seguro intentar buscar el valor de una clave:

my_hash = {}

my_hash[:age] # => nil

Los hashes también pueden contener claves en cadenas. Si intenta acceder a ellos normalmente, solo devolverá un valor nil , en lugar de eso, acceda a ellos mediante sus claves de cadena:

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

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

Para situaciones en las que se espera o se requiere que existan claves, los hash tienen un método de fetch que generará una excepción al acceder a una clave que no existe:

my_hash = {}

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

fetch acepta un valor predeterminado como su segundo argumento, que se devuelve si la clave no se ha establecido previamente:

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

fetch también puede aceptar un bloque que se devuelve si la clave no se ha establecido previamente:

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

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

#=> Could not find age

Los elementos hash también admiten un método de store como un alias para []= :

my_hash = {}

my_hash.store(:age, 45)

my_hash #=> { :age => 45 }

También puede obtener todos los valores de un hash utilizando el método de values :

my_hash = { length: 4, width: 5 }

my_hash.values #=> [4, 5]

Nota: esto es solo para Ruby #dig es útil para Hash anidados. Extrae el valor anidado especificado por la secuencia de objetos idx llamando a dig en cada paso, devolviendo nil si algún paso intermedio es nulo.

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

Configuración de valores predeterminados

De forma predeterminada, al intentar buscar el valor de una clave que no existe, se devolverá nil . Opcionalmente, puede especificar algún otro valor para devolver (o una acción a realizar) cuando se accede al hash con una clave que no existe. Si bien esto se conoce como "el valor predeterminado", no es necesario que sea un valor único; podría, por ejemplo, ser un valor computado como la longitud de la clave.

El valor predeterminado de un hash se puede pasar a su constructor:

h = Hash.new(0)

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

Un valor predeterminado también se puede especificar en un hash ya construido:

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

Es importante tener en cuenta que el valor predeterminado no se copia cada vez que se accede a una nueva clave, lo que puede generar resultados sorprendentes cuando el valor predeterminado es un tipo de referencia:

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

Para evitar este problema, el constructor de hash acepta un bloque que se ejecuta cada vez que se accede a una nueva clave, y el valor devuelto se utiliza como predeterminado:

authors = Hash.new { [] }

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

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

Tenga en cuenta que anteriormente tuvimos que usar + = en lugar de << porque el valor predeterminado no se asigna automáticamente al hash; el uso de << se habría agregado a la matriz, pero los autores [: homer] habrían permanecido indefinidos:

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

Para poder asignar valores predeterminados en el acceso, así como para calcular valores predeterminados más sofisticados, el bloque predeterminado se pasa el hash y la clave:

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

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

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

También puede usar un bloque predeterminado para realizar una acción y / o devolver un valor dependiente de la clave (o algún otro dato):

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

chars[:test] # => 4

Incluso puedes crear hashes más complejos:

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

Para establecer el valor predeterminado en un Proc en un hash ya existente , use default_proc= :

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

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

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

Creando automáticamente un Hash profundo

Hash tiene un valor predeterminado para las claves que se solicitan pero que no existen (nil):

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

Al crear un nuevo hash, se puede especificar el valor predeterminado:

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

Hash.new también toma un bloque, que le permite crear hashes anidados, como el comportamiento de autovivificación de 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 } } }

Modificación de claves y valores.

Puede crear un nuevo hash con las claves o los valores modificados, de hecho, también puede agregar o eliminar claves, usando inyectar (AKA, reducir ). Por ejemplo, para producir un hash con claves de cadena y valores en mayúsculas:

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 es un enumerable, en esencia, una colección de pares clave / valor. Por eso tiene métodos como each , map e inject .

Para cada par de clave / valor en el hash se evalúa el bloque dado, el valor de memo en la primera ejecución es el valor semilla que se pasa para inject , en nuestro caso un hash vacío, {} . El valor de memo para evaluaciones posteriores es el valor devuelto de la evaluación de bloques anterior, es por eso que modificamos memo estableciendo una clave con un valor y luego devolvemos memo al final. El valor de retorno de la evaluación final bloques es el valor de retorno de inject , en nuestro caso memo .

Para evitar tener que proporcionar el valor final, puede utilizar each_with_object en su lugar:

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

O incluso mapa :

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

(Consulte esta respuesta para obtener más detalles, incluida la forma de manipular los hashes en su lugar).

Iterando sobre un hash

A Hash incluye el módulo Enumerable , que proporciona varios métodos de iteración, como: Enumerable#each , Enumerable#each_pair , Enumerable#each_key , y Enumerable#each_value .

.each y .each_pair sobre cada par clave-valor:

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

# => first_name = John
#    last_name = Doe

.each_key itera sobre las teclas solamente:

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

# => first_name
#    last_name

.each_value itera sobre los valores solamente:

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

# => John
#    Doe

.each_with_index itera sobre los elementos y proporciona el índice de la iteración:

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

Conversión ay desde matrices

Los hashes se pueden convertir libremente desde y hacia matrices. Convertir un hash de pares clave / valor en una matriz producirá una matriz que contiene matrices anidadas para la pareja:

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

En la dirección opuesta se puede crear un Hash a partir de una matriz del mismo formato:

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

De manera similar, Hashes puede inicializarse usando Hash[] y una lista de claves y valores alternos:

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

O de una matriz de matrices con dos valores cada una:

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

Los hash se pueden convertir de nuevo a una matriz de claves y valores alternativos utilizando flatten() :

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

La fácil conversión hacia y desde una matriz permite que Hash funcione bien con muchos métodos Enumerable , como collect y 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 }

Obteniendo todas las claves o valores de 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>

Anulando la función hash

Rubí hashes utiliza los métodos hash y eql? para realizar la operación de hash y asignar objetos almacenados en el hash a los hash bins internos. La implementación predeterminada de hash en Ruby es la función hash murmur sobre todos los campos miembros del objeto hash . Para anular este comportamiento, es posible anular el hash y el eql? metodos

Al igual que con otras implementaciones de hash, dos objetos a y b se procesarán en el mismo grupo si a.hash == b.hash y se considerarán idénticos si a.eql?(b) . ¿Así, al reimplementar hash y eql? uno debe tener cuidado de asegurarse de que si a y b son iguales bajo eql? Deben devolver el mismo valor hash . De lo contrario, esto podría resultar en entradas duplicadas en un hash. Por el contrario, una mala elección en la implementación de hash podría llevar a muchos objetos a compartir el mismo hash bucket, destruyendo efectivamente el tiempo de búsqueda O (1) y causando O (n) para llamar a eql? en todos los objetos.

En el siguiente ejemplo, solo la instancia de la clase A se almacena como una clave, ya que se agregó primero:

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

Filtrado de hashes

select devuelve un nuevo hash con pares clave-valor para los cuales el bloque se evalúa como true .

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

Cuando no necesite la clave o el valor en un bloque de filtro, la convención es usar un _ en ese lugar:

{ :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 devuelve un nuevo hash con pares clave-valor para los cuales el bloque se evalúa como 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 }

Establecer operaciones en Hashes

  • Intersección de Hashes

    Para obtener la intersección de dos hashes, devuelva las claves compartidas cuyos valores son iguales:

    hash1 = { :a => 1, :b => 2 }
    hash2 = { :b => 2, :c => 3 }
    hash1.select { |k, v| (hash2.include?(k) && hash2[k] == v) } # => { :b => 2 }
    
  • Unión (fusión) de hashes:

    las claves en un hash son únicas, si una clave se produce en ambos hashes que se fusionarán, se sobrescribe la del hash que se llama a merge :

    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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow