Por favor, aclare si desea mutar los objetos de cadena originales, simplemente mute el original o mute nada.
Phrogz
1
Entonces has aceptado la respuesta incorrecta. (Sin ofender a @pst, ya que personalmente también defiendo la programación de estilo funcional en lugar de objetos mutantes)
Phrogz
Pero aún así, el enfoque fue agradable
revés Flick
Respuestas:
178
Si desea que las cadenas en sí mismas muten en su lugar (posiblemente y deseablemente afecten otras referencias a los mismos objetos de cadena):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{|_,str| str.gsub!/^|$/,'%'}
my_hash.each{|_,str| str.replace "%#{str}%"}
Si desea que el hash cambie en su lugar, pero no desea afectar las cadenas (desea que obtenga nuevas cadenas):
# 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 Tienes razón, gracias. En Ruby 1.8, Hash.[]no acepta una matriz de pares de matrices, requiere un número par de argumentos directos (de ahí la splat por adelantado).
Phrogz
2
En realidad, Hash. [Key_value_pairs] se introdujo en 1.8.7, por lo que solo Ruby 1.8.6 no necesita splat & flatten.
Marc-André Lafortune
2
@Aupajo Hash#eachproduce tanto la clave como el valor para el bloque. En este caso, no me importaba la clave, por lo que no le puse nada útil. Los nombres de las variables pueden comenzar con un guión bajo y, de hecho, pueden ser solo un guión bajo. No hay ningún beneficio de rendimiento al hacer esto, es solo una sutil nota de autodocumentación de que no estoy haciendo nada con ese primer valor de bloque.
Phrogz
1
Creo que quieres decir my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }, tengo que devolver el hash del bloque
aceofspades
1
Alternativamente, puede usar el método each_value, que es un poco más fácil de entender que usar un guión bajo para el valor clave no utilizado.
Gracias, en realidad era lo que estaba buscando. No sé por qué no te votan tanto.
simperreault
77
Sin embargo, esto es muy lento y tiene mucha RAM. El hash de entrada se repite para producir un conjunto intermedio de matrices anidadas que luego se convierten en un nuevo hash. Ignorando el uso máximo de RAM, el tiempo de ejecución es mucho peor: comparar esto con las soluciones de modificación en el lugar en otra respuesta muestra 2.5s versus 1.5s en el mismo número de iteraciones. Dado que Ruby es un lenguaje relativamente lento, evitar los fragmentos lentos del lenguaje lento tiene mucho sentido :-)
Andrew Hodgkinson
2
@AndrewHodgkinson, si bien, en general, estoy de acuerdo y no estoy abogando por no prestar atención al rendimiento en tiempo de ejecución, ¿no hace un seguimiento de todos estos escollos de rendimiento que comienza a convertirse en una molestia y va en contra de la filosofía de ruby "productividad del desarrollador primero"? Supongo que esto es menos un comentario para ti, y más un comentario general sobre la eventual paradoja a la que nos lleva, usando ruby.
elsurudo
44
El enigma es: bueno, ya estamos renunciando al rendimiento en nuestra decisión de incluso usar rubí, entonces, ¿qué diferencia hace "este otro poquito"? Es una pendiente resbaladiza, ¿no? Para el registro, prefiero esta solución a la respuesta aceptada, desde una perspectiva de legibilidad.
No es exactamente cierto: se asignan nuevas cadenas. Aún así, una solución interesante que es efectiva. +1
Phrogz
@Phrogz buen punto; Actualicé la respuesta. La asignación de valores no se puede evitar en general porque no todas las transformaciones de valores se pueden expresar como mutadores como gsub!.
Sim
3
Igual que mi respuesta pero con otro sinónimo, estoy de acuerdo en que updatetransmite la intención mejor que merge!. Creo que esta es la mejor respuesta.
1
Si no usa k, use _en su lugar.
sekrett
28
Un poco más legible, mapa una matriz de hashes de un solo elemento y reduceeso conmerge
Más Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
fácil de
Esta es una forma extremadamente ineficiente de actualizar valores. Para cada par de valores, primero crea un par Array(para map) y luego a Hash. Luego, cada paso de la operación de reducción duplicará el "memo" Hashy le agregará el nuevo par clave-valor. Al menos uso :merge!en reducemodificar la final Hashen su lugar. Y al final, no está modificando los valores del objeto existente, sino que está creando un nuevo objeto, que no es lo que hizo la pregunta.
Un método que no introduce efectos secundarios al original:
h ={:a =>'a',:b =>'b'}
h2 =Hash[h.map {|k,v|[k,'%'+ v +'%']}]
El mapa Hash # también puede ser una lectura interesante, ya que explica por qué el Hash.mapno devuelve un Hash (razón por la cual la matriz resultante de[key,value] pares se convierte en un nuevo Hash) y proporciona enfoques alternativos para el mismo patrón general.
Feliz codificación
[Descargo de responsabilidad: no estoy seguro si la Hash.mapsemántica cambia en Ruby 2.x]
No me gustan los efectos secundarios, pero +1 para el enfoque :) Existe each_with_objecten Ruby 1.9 (IIRC) que evita tener que acceder al nombre directamente y Map#mergetambién puede funcionar. No estoy seguro de cómo difieren los detalles intrincados.
1
El hash inicial se modifica; esto está bien si se anticipa el comportamiento, pero puede causar problemas sutiles si se "olvida". Prefiero reducir la mutabilidad del objeto, pero puede no ser siempre práctico. (Ruby no es un lenguaje "sin efectos secundarios" ;-)
8
Hash.merge! es la solución más limpia
o ={ a:'a', b:'b'}
o.merge!(o){|key, value|"%#{ value }%"}
puts o.inspect
>{:a =>"%a%",:b =>"%b%"}
Respuestas:
Si desea que las cadenas en sí mismas muten en su lugar (posiblemente y deseablemente afecten otras referencias a los mismos objetos de cadena):
Si desea que el hash cambie en su lugar, pero no desea afectar las cadenas (desea que obtenga nuevas cadenas):
Si quieres un nuevo hash:
fuente
Hash.[]
no acepta una matriz de pares de matrices, requiere un número par de argumentos directos (de ahí la splat por adelantado).Hash#each
produce tanto la clave como el valor para el bloque. En este caso, no me importaba la clave, por lo que no le puse nada útil. Los nombres de las variables pueden comenzar con un guión bajo y, de hecho, pueden ser solo un guión bajo. No hay ningún beneficio de rendimiento al hacer esto, es solo una sutil nota de autodocumentación de que no estoy haciendo nada con ese primer valor de bloque.my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, tengo que devolver el hash del bloqueEn Ruby 2.1 y superior puedes hacer
fuente
#transform_values!
según lo indicado por sschmeck ( stackoverflow.com/a/41508214/6451879 ).Ruby 2.4 introdujo el método
Hash#transform_values!
, que puedes usar.fuente
Hash#transform_values
(sin la explosión), que no modifica el receptor. De lo contrario, una gran respuesta, gracias!reduce
:-pLa mejor manera de modificar los valores de un Hash en su lugar es
Menos código e intención clara. También más rápido porque no se asignan objetos nuevos más allá de los valores que se deben cambiar.
fuente
gsub!
.update
transmite la intención mejor quemerge!
. Creo que esta es la mejor respuesta.k
, use_
en su lugar.Un poco más legible,
map
a una matriz de hashes de un solo elemento yreduce
eso conmerge
fuente
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(paramap
) y luego aHash
. Luego, cada paso de la operación de reducción duplicará el "memo"Hash
y le agregará el nuevo par clave-valor. Al menos uso:merge!
enreduce
modificar la finalHash
en su lugar. Y al final, no está modificando los valores del objeto existente, sino que está creando un nuevo objeto, que no es lo que hizo la pregunta.nil
sithe_hash
está vacíoHay un nuevo método 'Rails way' para esta tarea :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
fuente
Hash#transform_values
. Este debería ser el camino a seguir a partir de ahora.Un método que no introduce efectos secundarios al original:
El mapa Hash # también puede ser una lectura interesante, ya que explica por qué el
Hash.map
no devuelve un Hash (razón por la cual la matriz resultante de[key,value]
pares se convierte en un nuevo Hash) y proporciona enfoques alternativos para el mismo patrón general.Feliz codificación
[Descargo de responsabilidad: no estoy seguro si la
Hash.map
semántica cambia en Ruby 2.x]fuente
Hash.map
semántica cambia en Ruby 2.x?fuente
each_with_object
en Ruby 1.9 (IIRC) que evita tener que acceder al nombre directamente yMap#merge
también puede funcionar. No estoy seguro de cómo difieren los detalles intrincados.Hash.merge! es la solución más limpia
fuente
Después de probarlo con RSpec así:
Puede implementar Hash # map_values de la siguiente manera:
La función se puede usar así:
fuente
Si tiene curiosidad por saber qué variante in situ es la más rápida, aquí está:
fuente