2010-06-07 32 views
12

que con frecuencia escribir algo como esto:hashes Inicialización

a_hash['x'] ? a_hash['x'] += ' some more text' : a_hash['x'] = 'first text' 

Tiene que haber una mejor manera de hacer esto, pero no puedo encontrarlo.

Respuesta

27

Hay dos maneras de crear valores iniciales con un Hash.

Una es pasar un solo objeto a Hash.new. Esto funciona bien en muchas situaciones, especialmente si el objeto es un valor congelado, pero si el objeto tiene un estado interno, esto puede tener efectos secundarios inesperados. Dado que el mismo objeto se comparte entre todas las claves sin un valor asignado, la modificación del estado interno para uno se mostrará en total.

método
a_hash = Hash.new "initial value" 
a_hash['a'] #=> "initial value" 
# op= methods don't modify internal state (usually), since they assign a new 
# value for the key. 
a_hash['b'] += ' owned by b' #=> "initial value owned by b" 
# other methods, like #<< and #gsub modify the state of the string 
a_hash['c'].gsub!(/initial/, "c's") 
a_hash['d'] << " modified by d" 
a_hash['e'] #=> "c's value modified by d" 

Otra inicialización es pasar Hash.new un bloque, que se invoca cada vez que se solicita un valor para una llave que no tiene ningún valor. Esto le permite usar un valor distinto para cada tecla.

another_hash = Hash.new { "new initial value" } 
another_hash['a'] #=> "new initial value" 
# op= methods still work as expected 
another_hash['b'] += ' owned by b' 
# however, if you don't assign the modified value, it's lost, 
# since the hash rechecks the block every time an unassigned key's value is asked for 
another_hash['c'] << " owned by c" #=> "new initial value owned by c" 
another_hash['c'] #=> "new initial value" 

Al bloque se le pasan dos argumentos: se le pide al hash un valor y la clave utilizada. Esto le da la opción de asignar un valor para esa clave, de modo que se presente el mismo objeto cada vez que se da una clave en particular.

yet_another_hash = Hash.new { |hash, key| hash[key] = "#{key}'s initial value" } 
yet_another_hash['a'] #=> "a's initial value" 
yet_another_hash['b'] #=> "b's initial value" 
yet_another_hash['c'].gsub!('initial', 'awesome') 
yet_another_hash['c'] #=> "c's awesome value" 
yet_another_hash #=> { "a" => "a's initial value", "b" => "b's initial value", "c" => "c's awesome value" } 

Este último método es el que más utilizo a menudo. También es útil para almacenar en caché el resultado de un cálculo caro.

+0

inicializar con un bloque es uno que comenzaré a usar. ¡Bien! – Paul

+1

sería bueno mencionar que el último caso que usa un bloque, también se puede usar para establecer un valor predeterminado al recuperar un valor con el método Hash # fetch, ya que muchas veces puede recibir un Hash que se creó en otro lugar. – SystematicFrank

+0

No sabía nada de ['Hash # fetch'] (http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-fetch), ¡gracias!Vale la pena señalar que el bloque pasado a '# fetch' es un poco diferente, no obtiene la tabla hash como un parámetro durante una falla. – rampion

1

El constructor de Hash, en su primer argumento, tiene un valor predeterminado para las claves. De esta manera

>> a_hash = Hash.new "first text" 
=> {} 
>> a_hash['a'] 
=> "first text" 
>> a_hash['b'] += ", edit" 
=> "first text, edit" 
+1

El peligro de esto es que el objeto * same * string se usa para todas las teclas. De modo que las modicaciones de esa cadena se verán en todos ellos. Eso es si usamos '<<': 'a_hash ['c'] << " by c" #=>" primer texto por c "', luego 'a_hash ['d'] # =>" primer texto por c "' – rampion

+0

Esto se pone en marcha Quiero hacer si inicializo a_hash con una cadena vacía: a_hash = Hash.new "", entonces puedo agregar texto arbitrario a las teclas arbitrarias usando + = – Paul

4

puede especificar el valor inicial cuando se crea el picadillo:

a_hash = { 'x' => 'first text' } 
// ... 
a_hash['x'] << ' some more text' 
0

dado que está utilizando el hash para recoger las cuerdas, supongo que simplemente quiere asegurarse de que usted don' Obtener un error al agregar. Por lo tanto, haría que el hash predeterminado sea la cadena vacía. Entonces puede agregar sin error, sin importar la clave hash.

a_hash = Hash.new {|h,k| h[k]=""} 

texts = ['first text', ' some more text'] 

texts.each do |text| 
    a_hash['x'] << text 
end 

puts a_hash['x'] #=> 'first text some more text' 
+0

@rampion, estás en lo correcto. La forma de bloque a Hash.new se me olvidó. Corregido –

0

En caso de que no quiere meterse con el default= valor almohadillas (existente), se puede acortar su código mediante el uso de fetch(key [, default]) como una consulta con un valor por defecto:

a_hash['x'] = a_hash.fetch('x', 'first_text') + ' some more text'