Veuillez clarifier si vous voulez muter les objets de chaîne d'origine, muter simplement l'original a ou ne rien muter.
Phrogz
1
Alors vous avez accepté la mauvaise réponse. (Aucune infraction à l'intention de @pst, car je préconise personnellement également la programmation de style fonctionnel au lieu de faire muter des objets.)
Phrogz
Mais l'approche était toujours agréable soo
theReverseFlick
Réponses:
178
Si vous voulez que les chaînes réelles elles-mêmes muter sur place (affectant éventuellement et de manière souhaitable d'autres références aux mêmes objets chaîne):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{|_,str| str.gsub!/^|$/,'%'}
my_hash.each{|_,str| str.replace "%#{str}%"}
Si vous voulez que le hachage change en place, mais que vous ne voulez pas affecter les chaînes (vous voulez qu'il obtienne de nouvelles chaînes):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{|key,str| my_hash[key]="%#{str}%"}
my_hash.inject(my_hash){|h,(k,str)| h[k]="%#{str}%"; h }
@Andrew Marshall C'est vrai, merci. Dans Ruby 1.8, Hash.[]n'accepte pas un tableau de paires de tableaux, il nécessite un nombre pair d'arguments directs (d'où le splat à l'avant).
Phrogz
2
En fait, Hash. [Key_value_pairs] a été introduit dans la 1.8.7, donc seul Ruby 1.8.6 n'a pas besoin de splat & aplatir.
Marc-André Lafortune
2
@Aupajo renvoie Hash#eachla clé et la valeur au bloc. Dans ce cas, je me fichais de la clé et je ne lui ai donc rien donné d'utile. Les noms de variables peuvent commencer par un trait de soulignement et peuvent en fait être simplement un trait de soulignement. Il n'y a aucun avantage en termes de performances à faire cela, c'est juste une note subtile d'auto-documentation indiquant que je ne fais rien avec cette première valeur de bloc.
Phrogz
1
Je pense que vous voulez dire my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }, devez retourner le hachage du bloc
aceofspades
1
Vous pouvez également utiliser la méthode each_value, qui est un peu plus facile à comprendre que d'utiliser un trait de soulignement pour la valeur de clé inutilisée.
Merci, c'était en fait ce que je cherchais. Je ne sais pas pourquoi vous n'êtes pas si bien voté.
simperreault
7
C'est, cependant, très lent et très gourmand en RAM. Le hachage d'entrée est itéré pour produire un ensemble intermédiaire de tableaux imbriqués qui sont ensuite convertis en un nouveau hachage. En ignorant l'utilisation maximale de la RAM, le temps d'exécution est bien pire - l'analyse comparative de cela par rapport aux solutions de modification sur place dans une autre réponse montre 2,5 s contre 1,5 s sur le même nombre d'itérations. Puisque Ruby est un langage relativement lent, éviter les parties lentes du langage lent a beaucoup de sens :-)
Andrew Hodgkinson
2
@AndrewHodgkinson alors qu'en général, je suis d'accord et je ne préconise pas de ne pas prêter attention aux performances d'exécution, le suivi de tous ces pièges de performance ne commence-t-il pas à devenir un problème et à aller à l'encontre de la philosophie «productivité des développeurs d'abord» de ruby? Je suppose que c'est moins un commentaire pour vous, que plus un commentaire général sur le paradoxe éventuel auquel cela nous amène, en utilisant le rubis.
elsurudo
4
L'énigme étant: eh bien, nous abandonnons déjà la performance dans notre décision d'utiliser même le rubis, alors quelle différence fait "cet autre petit morceau"? C'est une pente glissante, n'est-ce pas? Pour mémoire, je préfère cette solution à la réponse acceptée, du point de vue de la lisibilité.
elsurudo
1
Si vous utilisez Ruby 2.4+, il est encore plus facile à utiliser #transform_values!comme indiqué par sschmeck ( stackoverflow.com/a/41508214/6451879 ).
Pas exactement vrai: de nouvelles chaînes sont allouées. Pourtant, une solution intéressante qui est efficace. +1
Phrogz
@Phrogz bon point; J'ai mis à jour la réponse. L'allocation de valeur ne peut pas être évitée en général car toutes les transformations de valeur ne peuvent pas être exprimées en tant que mutateurs tels que gsub!.
Sim
3
Identique à ma réponse mais avec un autre synonyme, je conviens que cela updateexprime mieux l'intention que merge!. Je pense que c'est la meilleure réponse.
1
Si vous n'utilisez pas k, utilisez à la _place.
sekrett
28
Un peu plus lisible, mapc'est à un tableau de hachages à un seul élément et reducecela avecmerge
Plus clair à utiliserHash[the_hash.map { |key,value| [key, "%#{value}%"] }]
meagar
Il s'agit d'une manière extrêmement inefficace de mettre à jour les valeurs. Pour chaque paire de valeurs, il crée d'abord une paire Array(pour map) puis un Hash. Ensuite, chaque étape de l'opération de réduction dupliquera le "mémo" Hashet y ajoutera la nouvelle paire clé-valeur. Au moins une utilisation :merge!dans reducede modifier la finale Hashen place. Et au final, vous ne modifiez pas les valeurs de l'objet existant mais créez un nouvel objet, ce qui n'est pas ce que la question a posée.
Une méthode qui n'introduit pas d'effets secondaires sur l'original:
h ={:a =>'a',:b =>'b'}
h2 =Hash[h.map {|k,v|[k,'%'+ v +'%']}]
Hash # map peut également être une lecture intéressante car elle explique pourquoi le Hash.mapne renvoie pas de Hash (c'est pourquoi le tableau de [key,value]paires résultant est converti en un nouveau Hash) et fournit des approches alternatives au même modèle général.
Bon codage.
[Clause de non-responsabilité: je ne sais pas si la Hash.mapsémantique change dans Ruby 2.x]
Je n'aime pas les effets secondaires, mais +1 pour l'approche :) Il y en a each_with_objectdans Ruby 1.9 (IIRC) qui évite d'avoir à accéder directement au nom et Map#mergepeut aussi fonctionner. Je ne sais pas en quoi les détails complexes diffèrent.
1
Le hachage initial est modifié - c'est correct si le comportement est anticipé mais peut causer des problèmes subtils s'il est "oublié". Je préfère réduire la mutabilité des objets, mais ce n'est pas toujours pratique. (Ruby est à peine un langage "sans effets secondaires" ;-)
8
Hash.merge! est la solution la plus propre
o ={ a:'a', b:'b'}
o.merge!(o){|key, value|"%#{ value }%"}
puts o.inspect
>{:a =>"%a%",:b =>"%b%"}
Réponses:
Si vous voulez que les chaînes réelles elles-mêmes muter sur place (affectant éventuellement et de manière souhaitable d'autres références aux mêmes objets chaîne):
Si vous voulez que le hachage change en place, mais que vous ne voulez pas affecter les chaînes (vous voulez qu'il obtienne de nouvelles chaînes):
Si vous voulez un nouveau hachage:
la source
Hash.[]
n'accepte pas un tableau de paires de tableaux, il nécessite un nombre pair d'arguments directs (d'où le splat à l'avant).Hash#each
la clé et la valeur au bloc. Dans ce cas, je me fichais de la clé et je ne lui ai donc rien donné d'utile. Les noms de variables peuvent commencer par un trait de soulignement et peuvent en fait être simplement un trait de soulignement. Il n'y a aucun avantage en termes de performances à faire cela, c'est juste une note subtile d'auto-documentation indiquant que je ne fais rien avec cette première valeur de bloc.my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, devez retourner le hachage du blocDans Ruby 2.1 et supérieur, vous pouvez faire
la source
#transform_values!
comme indiqué par sschmeck ( stackoverflow.com/a/41508214/6451879 ).Ruby 2.4 a introduit la méthode
Hash#transform_values!
que vous pouvez utiliser.la source
Hash#transform_values
(sans le bang), qui ne modifie pas le récepteur. Sinon une excellente réponse, merci!reduce
:-pLa meilleure façon de modifier les valeurs d'un Hash en place est
Moins de code et une intention claire. Aussi plus rapide car aucun nouvel objet n'est alloué au-delà des valeurs qui doivent être modifiées.
la source
gsub!
.update
exprime mieux l'intention quemerge!
. Je pense que c'est la meilleure réponse.k
, utilisez à la_
place.Un peu plus lisible,
map
c'est à un tableau de hachages à un seul élément etreduce
cela avecmerge
la source
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(pourmap
) puis unHash
. Ensuite, chaque étape de l'opération de réduction dupliquera le "mémo"Hash
et y ajoutera la nouvelle paire clé-valeur. Au moins une utilisation:merge!
dansreduce
de modifier la finaleHash
en place. Et au final, vous ne modifiez pas les valeurs de l'objet existant mais créez un nouvel objet, ce qui n'est pas ce que la question a posée.nil
sithe_hash
est videIl existe une nouvelle méthode 'Rails way' pour cette tâche :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
la source
Hash#transform_values
. Cela devrait être la voie à suivre à partir de maintenant.Une méthode qui n'introduit pas d'effets secondaires sur l'original:
Hash # map peut également être une lecture intéressante car elle explique pourquoi le
Hash.map
ne renvoie pas de Hash (c'est pourquoi le tableau de[key,value]
paires résultant est converti en un nouveau Hash) et fournit des approches alternatives au même modèle général.Bon codage.
[Clause de non-responsabilité: je ne sais pas si la
Hash.map
sémantique change dans Ruby 2.x]la source
Hash.map
sémantique change dans Ruby 2.x?la source
each_with_object
dans Ruby 1.9 (IIRC) qui évite d'avoir à accéder directement au nom etMap#merge
peut aussi fonctionner. Je ne sais pas en quoi les détails complexes diffèrent.Hash.merge! est la solution la plus propre
la source
Après l'avoir testé avec RSpec comme ceci:
Vous pouvez implémenter Hash # map_values comme suit:
La fonction peut alors être utilisée comme ceci:
la source
Si vous êtes curieux de savoir quelle variante en place est la plus rapide, voici:
la source