Quiero cambiar cada valor en un hash para agregar '%' antes y después del valor para
{ :a=>'a' , :b=>'b' }
debe cambiarse a
{ :a=>'%a%' , :b=>'%b%' }
¿Cuál es la mejor manera de hacer esto?
Quiero cambiar cada valor en un hash para agregar '%' antes y después del valor para
{ :a=>'a' , :b=>'b' }
debe cambiarse a
{ :a=>'%a%' , :b=>'%b%' }
¿Cuál es la mejor manera de hacer esto?
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):
# 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 }
Si quieres un nuevo hash:
# Ruby 1.8.6+
new_hash = Hash[*my_hash.map{|k,str| [k,"%#{str}%"] }.flatten]
# Ruby 1.8.7+
new_hash = Hash[my_hash.map{|k,str| [k,"%#{str}%"] } ]
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 bloque
En Ruby 2.1 y superior puedes hacer
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
#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.
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
Hash#transform_values
(sin la explosión), que no modifica el receptor. De lo contrario, una gran respuesta, gracias!
La mejor manera de modificar los valores de un Hash en su lugar es
hash.update(hash){ |_,v| "%#{v}%" }
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.
gsub!
.
update
transmite la intención mejor que merge!
. 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 y reduce
eso conmerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(para map
) y luego a Hash
. 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!
en reduce
modificar la final Hash
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
si the_hash
está vacío
Hay un nuevo método 'Rails way' para esta tarea :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
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:
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.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]
Hash.map
semántica cambia en Ruby 2.x?
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
each_with_object
en Ruby 1.9 (IIRC) que evita tener que acceder al nombre directamente y Map#merge
también puede funcionar. No estoy seguro de cómo difieren los detalles intrincados.
Después de probarlo con RSpec así:
describe Hash do
describe :map_values do
it 'should map the values' do
expect({:a => 2, :b => 3}.map_values { |x| x ** 2 }).to eq({:a => 4, :b => 9})
end
end
end
Puede implementar Hash # map_values de la siguiente manera:
class Hash
def map_values
Hash[map { |k, v| [k, yield(v)] }]
end
end
La función se puede usar así:
{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
Si tiene curiosidad por saber qué variante in situ es la más rápida, aquí está:
Calculating -------------------------------------
inplace transform_values! 1.265k (± 0.7%) i/s - 6.426k in 5.080305s
inplace update 1.300k (± 2.7%) i/s - 6.579k in 5.065925s
inplace map reduce 281.367 (± 1.1%) i/s - 1.431k in 5.086477s
inplace merge! 1.305k (± 0.4%) i/s - 6.630k in 5.080751s
inplace each 1.073k (± 0.7%) i/s - 5.457k in 5.084044s
inplace inject 697.178 (± 0.9%) i/s - 3.519k in 5.047857s