Cambiar cada valor en un hash en Ruby


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?

 136
Author: Jakub Hampl, 2011-03-04

10 answers

Si desea que las propias cadenas muten en su lugar (posiblemente y deseablemente afectando 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}%"] } ]
 159
Author: Phrogz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-10 00:06:32

En Ruby 2.1 y superior puedes hacer

{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
 212
Author: shock_one,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-20 14:59:28

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 nuevos objetos más allá de los valores que deben cambiarse.

 76
Author: Sim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-12 01:02:58

Ruby 2.4 introdujo el método Hash#transform_values!, que podrías usar.

{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"} 
 56
Author: sschmeck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-06 14:52:37

Un poco más legible, map a una matriz de hashes de un solo elemento y reduce que con merge

the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
 28
Author: edgerunner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-01 18:49:38

Un método que no introduce efectos secundarios al original:

h = {:a => 'a', :b => 'b'}
h2 = Hash[h.map {|k,v| [k, '%' + v + '%']}]

Hash # map también puede ser una lectura interesante, ya que explica por qué el Hash.map no devuelve un Hash (por lo que la matriz resultante de pares [key,value] se convierte en un nuevo Hash) y proporciona enfoques alternativos al mismo patrón general.

Feliz codificación.

[Descargo de responsabilidad: No estoy seguro si Hash.map la semántica cambia en Ruby 2.x]

 16
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-04 03:11:05

Hay un nuevo método 'Rails way' para esta tarea :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values

 15
Author: woto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-08 00:15:45
my_hash.each do |key, value|
  my_hash[key] = "%#{value}%"
end
 14
Author: Andrew Marshall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-04 03:12:30

Hachís.¡fusionaos! 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%" }
 7
Author: Dorian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-03 22:16:36

Después de probarlo con RSpec de esta manera:

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

Puedes implementar el Hash # map_values de la siguiente manera:

class Hash
  def map_values
    Hash[map { |k, v| [k, yield(v)] }]
  end
end

La función entonces se puede usar así:

{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
 5
Author: wedesoft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-25 15:38:02