2010-06-07 11 views
12

Je vous écris souvent quelque chose comme ceci:Initialiser hash

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

Il devrait être une meilleure façon de le faire, mais je ne peux pas le trouver.

Répondre

27

Il existe deux façons de créer des valeurs initiales avec un Hash.

On doit passer un seul objet à Hash.new. Cela fonctionne bien dans de nombreuses situations, en particulier si l'objet est une valeur gelée, mais si l'objet a un état interne, cela peut avoir des effets secondaires inattendus. Comme le même objet est partagé entre toutes les clés sans valeur assignée, la modification de l'état interne de l'une s'affichera dans toutes les clés.

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" 

Une autre méthode d'initialisation consiste à faire passer Hash.new un bloc, qui est appelée à chaque fois qu'une valeur est demandé pour une clé qui n'a pas de valeur. Cela vous permet d'utiliser une valeur distincte pour chaque clé.

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" 

Le bloc reçoit deux arguments: le hachage est demandé pour une valeur et la clé utilisée. Cela vous donne l'option d'assigner une valeur pour cette clé, de sorte que le même objet sera présenté chaque fois qu'une clé particulière est donnée.

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

Cette dernière méthode est celle que j'utilise le plus souvent. Il est également utile pour mettre en cache le résultat d'un calcul coûteux.

+0

initialiser avec un bloc est celui que je vais commencer à utiliser. Un bon! – Paul

+1

il serait bon de mentionner que le dernier cas utilisant un bloc, peut également être utilisé pour définir une valeur par défaut lors de la récupération d'une valeur avec la méthode Hash # fetch, car plusieurs fois vous pourriez recevoir un Hash créé ailleurs. – SystematicFrank

+0

Je ne connaissais pas ['Hash # fetch'] (http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-fetch), merci!A noter que le bloc passé à # # fetch est un peu différent, il n'obtient pas la table de hachage en tant que paramètre lors d'un échec. – rampion

1

Le constructeur de Hash, dans son premier argument, a une valeur par défaut pour les clés. De cette façon

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

Le danger est que le * même * objet chaîne est utilisé pour toutes les clés. Donc, des modifications à cette chaîne seront visibles à travers chacun d'eux. C'est à dire si nous utilisons '' ':' a_hash ['c'] << " by c" #=> "premier texte par c" ', puis' a_hash ['d'] # => "premier texte par c" ' – rampion

+0

Je veux faire si j'initialise a_hash w/une chaîne vide: a_hash = Hash.new "", alors je peux ajouter du texte arbitraire à des touches arbitraires en utilisant + = – Paul

4

Vous pouvez spécifier la valeur initiale lorsque vous créez votre hachage:

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

Puisque vous utilisez le hachage pour collecter des chaînes, je suppose que vous voulez simplement vous assurer que vous n » t obtenir une erreur lors de l'ajout. Par conséquent, je ferais que le hash par défaut soit la chaîne vide. Ensuite, vous pouvez ajouter sans erreur, peu importe la clé de hachage.

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, vous avez raison. La forme de bloc à Hash.new a glissé dans mon esprit. Corrigée. –

0

Si vous ne voulez pas salir avec la valeur default= (hachage existante), vous pouvez réduire votre code en utilisant fetch(key [, default]) comme look avec une valeur par défaut:

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