Hash değerleri nasıl değiştirilir?


126

valueBir hash içindeki her birini ile değiştirmek istiyorum value.some_method.

Örneğin, basit bir hash için:

{"a" => "b", "c" => "d"}` 

her değer .upcased olmalıdır , bu nedenle şöyle görünür:

{"a" => "B", "c" => "D"}

Denedim #collectve #mapama her zaman sadece geri diziler olsun. Bunu yapmanın zarif bir yolu var mı?

GÜNCELLEME

Kahretsin, unuttum: Hash, değiştirilmemesi gereken bir örnek değişkeninde. Değişen değerlere sahip yeni bir hash'e ihtiyacım var, ancak bu değişkeni açıkça tanımlamamayı ve ardından onu dolduran hash üzerinde döngü yapmayı tercih ederim. Gibi bir şey:

new_hash = hash.magic{ ... }

cevabımdaki ikinci örnek yeni bir hash döndürüyor. #inject'in yapmakta olduğu sihri genişletmemi istiyorsanız üzerine yorum yapın.
kch

Yanıtlar:


218
my_hash.each { |k, v| my_hash[k] = v.upcase } 

veya, tahribatsız bir şekilde yapmayı ve değiştirmek yerine yeni bir karma döndürmeyi tercih ederseniz my_hash:

a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h } 

Bu son sürüm, anahtarları da dönüştürebilmeniz gibi ek bir avantaja sahiptir.


26
İlk öneri uygun değildir; Yineleme sırasında numaralandırılabilir bir öğeyi değiştirmek tanımsız davranışa yol açar - bu diğer dillerde de geçerlidir. İşte Matz'den bir
Marcus

4
@Marcus Genel olarak bu tür değişikliklerden kaçınılması gerektiğini kabul ediyorum. Ancak bu durumda muhtemelen güvenlidir, çünkü değişiklik gelecekteki yinelemeleri değiştirmez. Şimdi başka bir anahtar (bloğa geçmeyen anahtar) atanmışsa, sorun çıkacaktır.
Kelvin

36
@Adam @kch each_with_object, injectbloğu hash ile sonlandırmak istemediğinizde daha kullanışlı bir versiyondur :my_hash.each_with_object({}) {|(k, v), h| h[k] = v.upcase }
Kelvin

6
Ayrıca bir çiftler listesini hash'e dönüştürmek için çok düzgün bir sözdizimi var, böylece yazabilirsiniza_new_hash = Hash[ my_hash.map { |k, v| [k, v.upcase] } ]
yeniden yazıldı

2
Ruby 2 ile yazabilirsiniz.to_h
roman-roman


35

Değerleri toplayabilir ve tekrar Array'den Hash'e dönüştürebilirsiniz.

Bunun gibi:

config = Hash[ config.collect {|k,v| [k, v.upcase] } ]

23

Bunu yapacak:

my_hash.each_with_object({}) { |(key, value), hash| hash[key] = value.upcase }

injectAvantajın aksine , hash'i blok içinde tekrar iade etmenize gerek kalmamasıdır.


Bu yöntemin orijinal hash'i değiştirmemesini seviyorum.
Christian Di Lorenzo

10

Bu işlevi deneyin:

h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.

5
bu iyidir, ancak sadece j'nin kendi başına hareket eden yıkıcı bir yöntemi olduğu zaman işe yaradığına dikkat edilmelidir. Bu nedenle genel value.some_method durumunu işleyemez.
kch

İyi nokta ve onun güncellemesiyle, ikinci çözümünüz (tahribatsız) en iyisidir.
Chris Doggett

10

ActiveSupport v4.2.0'da bunun için bir yöntem var . Bu denir transform_valuesve temelde her anahtar-değer çifti için bir blok yürütür.

Bunu bir ile yaptıkları için, eachdöngüden daha iyi bir yol olmadığını düşünüyorum.

hash = {sample: 'gach'}

result = {}
hash.each do |key, value|
  result[key] = do_stuff(value)
end

Güncelleme:

Ruby 2.4.0'dan beri yerel olarak #transform_valuesve kullanabilirsiniz #transform_values!.


2

Bir adım daha ileri gitmek ve bunu iç içe geçmiş bir hash üzerinde yapmak isteyebilirsiniz. Kesinlikle bu, Rails projelerinde makul bir miktarda olur.

Bir params hash'inin UTF-8'de olmasını sağlamak için bazı kodlar:

  def convert_hash hash
    hash.inject({}) do |h,(k,v)|
      if v.kind_of? String
        h[k] = to_utf8(v) 
      else
        h[k] = convert_hash(v)
      end
      h
    end      
  end    

  # Iconv UTF-8 helper
  # Converts strings into valid UTF-8
  #
  # @param [String] untrusted_string the string to convert to UTF-8
  # @return [String] your string in UTF-8
  def to_utf8 untrusted_string=""
    ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
    ic.iconv(untrusted_string + ' ')[0..-2]
  end  

1

Bunun gibi bir şey yapıyorum:

new_hash = Hash [* original_hash.collect {| anahtar, değer | [anahtar, değer + 1]}. düzleştir]

Bu size anahtarı veya değeri herhangi bir ifade yoluyla dönüştürme olanağı sağlar (ve elbette tahribatsızdır).


1

Ruby, bunun gibi şeyler için çok yararlı olan bir tapyönteme ( 1.8.7 , 1.9.3 ve 2.1.0 ) sahiptir.

original_hash = { :a => 'a', :b => 'b' }
original_hash.clone.tap{ |h| h.each{ |k,v| h[k] = v.upcase } }
# => {:a=>"A", :b=>"B"}
original_hash # => {:a=>"a", :b=>"b"}

1

Raylar özgü

Birinin yalnızca to_sher bir değer için yöntemi çağırması gerektiğinde ve Rails 4.2 ( transform_valuesyöntem bağlantısını içeren ) kullanmıyorsa , aşağıdakileri yapabilirsiniz:

original_hash = { :a => 'a', :b => BigDecimal('23.4') }
#=> {:a=>"a", :b=>#<BigDecimal:5c03a00,'0.234E2',18(18)>}
JSON(original_hash.to_json)
#=> {"a"=>"a", "b"=>"23.4"}

Not: 'json' kitaplığının kullanılması gereklidir.

Not 2: Bu, anahtarları da dizelere dönüştürecektir


1

Değerlerin dizeler olduğunu biliyorsanız, döngüdeyken üzerlerinde değiştirme yöntemini çağırabilirsiniz : bu şekilde değeri değiştirirsiniz.

Bu, değerlerin dizeler olduğu ve dolayısıyla soruyu tam olarak yanıtlamadığı durumla sınırlı olsa da, birileri için yararlı olabileceğini düşündüm.


1
new_hash = old_hash.merge(old_hash) do |_key, value, _value|
             value.upcase
           end

# old_hash = {"a" => "b", "c" => "d"}
# new_hash = {"a" => "B", "c" => "D"}

Bu verimli mi?
ioquatix
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.