2009-05-01 14 views
96

Chciałbym zastąpić każdy value w haszy z value.some_method.Jak zmienić wartości skrótu?

Na przykład, biorąc pod uwagę prosty hash:

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

każda wartość powinna być .upcase d, tak to wygląda:

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

Próbowałem #collect i #map ale zawsze po prostu tablice z powrotem . Czy istnieje elegancki sposób na zrobienie tego?

UPDATE

Cholera, zapomniałem: Hasz jest w zmiennej instancji, które nie powinny być zmieniane. Potrzebuję nowego skrótu ze zmienionymi wartościami, ale wolałbym nie definiować tej zmiennej jawnie, a następnie pętli nad hashem, który ją wypełnia. Coś jak:

new_hash = hash.magic{ ... } 
+0

Drugi przykład w mojej odpowiedzi zwraca nowy skrót. skomentuj to, jeśli chcesz, żebym rozszerzył się na magię, którą robi #inject. – kch

Odpowiedz

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

albo, jeśli wolisz, aby to zrobić nieniszczący i powrót nowego skrótu zamiast modyfikowania my_hash:

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

Ta ostatnia wersja ma dodana korzyści, które można również przekształcić klucze.

+22

Pierwsza sugestia nie jest właściwa; modyfikowanie elementu przeliczalnego podczas iterowania prowadzi do niezdefiniowanego zachowania - jest to również ważne w innych językach. Oto odpowiedź od Matza (odpowiada na przykład za pomocą Tablicy, ale odpowiedź jest ogólna): http://j.mp/tPOh2U – Marcus

+4

@Marcus Zgadzam się, że w ogóle takiej modyfikacji należy unikać. Ale w tym przypadku jest to prawdopodobnie bezpieczne, ponieważ modyfikacja nie zmienia przyszłych iteracji. Teraz, jeśli został przypisany inny klucz (który nie został przekazany do bloku), pojawią się kłopoty. – Kelvin

+28

@Adam @kch 'each_with_object' to wygodniejsza wersja' inject ', gdy nie chcesz kończyć bloku hashiem: 'my_hash.each_with_object ({}) {| (k, v), h | h [k] = v.upcase} ' – Kelvin

9

Spróbuj funkcję:

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

, co jest dobre, ale należy zauważyć, że działa tylko wtedy, gdy j ma destrukcyjną metodę, która działa sama. Więc nie może obsłużyć ogólnego przypadku value.some_method. – kch

+0

Dobra rada, a dzięki jego aktualizacji twoje drugie rozwiązanie (nieniszczące) jest najlepsze. –

1

zrobić coś takiego:

new_hash = Hash [* original_hash.collect {| klucz, wartość | [key, value + 1]}. flatten]

Zapewnia to możliwość przekształcenia klucza lub wartości również za pomocą dowolnego wyrażenia (i oczywiście nieniszczącego).

+0

To jest całkiem sprytne. Dzięki!:) –

1

Możesz pójść o krok dalej i zrobić to na zagnieżdżonym haszowniku. Z pewnością dzieje się tak z projektami Rails.

Oto niektóre kodu, aby zapewnić hash params jest w UTF-8:

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 
30

Można zbierać wartości, i przekształcić ją z Array ponownie Hash.

Jak to:

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

To jest bardziej zwięzłe, a IMHO lepiej syntaktycznie - jak @rewritten skomentował zaakceptowaną odpowiedź – MatzFan

21

to zrobi:

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

W przeciwieństwie do inject Zaletą jest to, że nie są w potrzebie powrotu ponownie hash wewnątrz bloku.

+0

Podoba mi się, że ta metoda nie modyfikuje oryginalnego skrótu. –

1

Ruby ma metodę tap (1.8.7, 1.9.3 i 2.1.0), która jest bardzo przydatna dla rzeczy jak ta.

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

Szyny specyficzne

W przypadku gdy ktoś potrzebuje tylko zadzwonić to_s metodę do każdej wartości i nie używa Rails 4.2 (która obejmuje transform_values metodę link), można wykonać następujące czynności:

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"} 

Uwaga: korzystanie z biblioteki 'json' jest requir ed.

Uwaga 2: W ten sposób zwrócić klucze do strun jak również

7

Jest to metoda, która w ActiveSupport v4.2.0. Nazywa się to transform_values iw zasadzie po prostu wykonuje blok dla każdej pary klucz-wartość.

Ponieważ robią to z each myślę, że nie ma lepszego sposobu, niż przechodzić.

hash = {sample: 'gach'} 

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

Jeśli wiesz, że wartości są ciągi, można wywołać metodę replace na nich, podczas gdy w cyklu: w ten sposób można zmienić wartość.

Altohugh jest to ograniczone do przypadku, w którym wartości są ciągi, a więc nie odpowiada na pytanie w pełni, myślałem, że może być przydatne dla kogoś.

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"} 
+0

Czy to jest wydajne? – ioquatix

28

Od ruby 2.4.0 można użyć natywną Hash#transform_values metody:

hash = {"a" => "b", "c" => "d"} 
new_hash = hash.transform_values(&:upcase) 
# => {"a" => "B", "c" => "D"} 

Istnieje także destrukcyjny Hash#transform_values! wersja.

+3

świetnie, jest również dostarczany z szynami 4.2.1 https://apidock.com/rails/v4.2.1/Hash/transform_values ​​ –

+1

To jest najlepsze rozwiązanie. Kod jest krótszy i bardziej czytelny. –

+0

@ZackXu To jest lepsze, ale nie zapominajmy, że pytanie to zadano w 2009 r., A większość odpowiedzi napisano w tym czasie. Szybko do przodu do roku 2017, rzeczy są znacznie łatwiejsze dla klucza Hasha i transformacji wartości, ponieważ nowsze wersje Ruby zaimplementowały te metody. – rubyprince

Powiązane problemy