Proszę wyjaśnić, czy chcesz zmutować oryginalne obiekty typu string, po prostu zmutować oryginał ma, lub nic nie mutować.
Phrogz
1
Zatem zaakceptowałeś złą odpowiedź. (Bez obrazy @pst zamierzone, ponieważ osobiście opowiadam się również za programowaniem w stylu funkcjonalnym zamiast mutowania obiektów.)
Phrogz
Ale i tak podejście było fajne
theReverseFlick
Odpowiedzi:
178
Jeśli chcesz, aby same ciągi mutowały w miejscu (prawdopodobnie i korzystnie wpływając na inne odwołania do tych samych obiektów łańcuchowych):
# Two ways to achieve the same result (any Ruby version)
my_hash.each{|_,str| str.gsub!/^|$/,'%'}
my_hash.each{|_,str| str.replace "%#{str}%"}
Jeśli chcesz, aby hash zmienił się w miejscu, ale nie chcesz wpływać na ciągi (chcesz, aby otrzymywał nowe ciągi):
# 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 Racja, dzięki. W Rubim 1.8 Hash.[]nie akceptuje tablicy par tablic, wymaga parzystej liczby bezpośrednich argumentów (stąd ikona z przodu).
Phrogz
2
Właściwie Hash. [Key_value_pairs] został wprowadzony w 1.8.7, więc tylko Ruby 1.8.6 nie potrzebuje splat & flatten.
Marc-André Lafortune
2
@Aupajo Hash#eachzwraca zarówno klucz, jak i wartość do bloku. W tym przypadku nie przejmowałem się kluczem, więc nie nazwałem go niczym przydatnym. Nazwy zmiennych mogą zaczynać się od podkreślenia, a w rzeczywistości mogą być tylko podkreśleniem. Nie ma z tego żadnej korzyści dla wydajności, jest to po prostu subtelna samodokumentująca uwaga, że nie robię nic z tą pierwszą wartością bloku.
Phrogz
1
Myślę, że masz na myśli my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }, musisz zwrócić haszysz z bloku
aceofspades
1
Alternatywnie możesz użyć metody each_value, która jest nieco łatwiejsza do zrozumienia niż użycie podkreślenia dla nieużywanej wartości klucza.
Dzięki, właśnie tego szukałem. Nie wiem, dlaczego nie cieszysz się tak dużym poparciem.
simperreault
7
Jest to jednak bardzo powolne i bardzo wymagające pamięci RAM. Hash wejściowy jest powtarzany w celu utworzenia pośredniego zestawu zagnieżdżonych tablic, które są następnie konwertowane na nowy skrót. Ignorując szczytowe użycie pamięci RAM, czas działania jest znacznie gorszy - porównanie tego z rozwiązaniami modyfikowanymi na miejscu w innej odpowiedzi pokazuje 2,5 s w porównaniu z 1,5 s przy tej samej liczbie iteracji. Ponieważ Ruby jest stosunkowo wolnym językiem, unikanie powolnych fragmentów języka wolnego ma wiele sensu :-)
Andrew Hodgkinson
2
@AndrewHodgkinson, podczas gdy generalnie się zgadzam i nie zalecam nie zwracania uwagi na wydajność środowiska wykonawczego, czy śledzenie wszystkich tych pułapek wydajnościowych nie staje się uciążliwe i nie jest sprzeczne z filozofią rubinową „najpierw produktywność programisty”? Wydaje mi się, że jest to mniejszy komentarz do ciebie, a bardziej ogólny komentarz dotyczący ewentualnego paradoksu, do którego nas to doprowadziło, używając rubinu.
elsurudo
4
Problem polega na tym, że: cóż, już rezygnujemy z wydajności, decydując się w ogóle na użycie rubinu, więc jaką różnicę robi „ten drugi kawałek”? To śliskie zbocze, prawda? Dla porządku, z punktu widzenia czytelności wolę to rozwiązanie od zaakceptowanej odpowiedzi.
Nie do końca prawda: przydzielane są nowe ciągi. Mimo to ciekawe rozwiązanie, które jest skuteczne. +1
Phrogz,
@Phrogz dobra uwaga; Zaktualizowałem odpowiedź. Generalnie nie można uniknąć alokacji wartości, ponieważ nie wszystkie transformacje wartości można wyrazić jako mutatory, takie jak gsub!.
Sim
3
Tak samo jak moja odpowiedź, ale z innym synonimem, zgadzam się, że updatelepiej przekazuje intencję niż merge!. Myślę, że to najlepsza odpowiedź.
1
Jeśli nie używasz k, użyj _zamiast tego.
sekrett
28
Nieco bardziej czytelny, mapdo tablicy jednoelementowych skrótów i reduceto zmerge
Jaśniejsze w użyciuHash[the_hash.map { |key,value| [key, "%#{value}%"] }]
meagar
Jest to wyjątkowo nieefektywny sposób aktualizowania wartości. Dla każdej pary wartości najpierw tworzy parę Array(for map), a następnie a Hash. Następnie na każdym etapie operacji redukcji zostanie zduplikowana „notatka” Hashi dodana do niej nowa para klucz-wartość. Przynajmniej użyj :merge!w reducecelu zmodyfikowania ostatecznego Hashna miejscu. Ostatecznie nie modyfikujesz wartości istniejącego obiektu, ale tworzysz nowy obiekt, co nie jest tym, o co chodziło w pytaniu.
Jedna metoda, która nie wprowadza skutków ubocznych do oryginału:
h ={:a =>'a',:b =>'b'}
h2 =Hash[h.map {|k,v|[k,'%'+ v +'%']}]
Hash # map może być również interesującą lekturą, ponieważ wyjaśnia, dlaczego Hash.mapnie zwraca Hash (dlatego wynikowa tablica [key,value]par jest konwertowana na nowy Hash) i zapewnia alternatywne podejście do tego samego ogólnego wzorca.
Miłego kodowania.
[Uwaga: nie jestem pewien, czy Hash.mapsemantyka zmieni się w Ruby 2.x]
Nie lubię efektów ubocznych, ale +1 za podejście :) Jest each_with_objectw Rubim 1.9 (IIRC), który pozwala uniknąć konieczności bezpośredniego dostępu do nazwy i Map#mergemoże również działać. Nie jestem pewien, czym różnią się zawiłe szczegóły.
1
Początkowy skrót jest modyfikowany - jest to w porządku, jeśli zachowanie jest przewidywane, ale może powodować subtelne problemy, jeśli zostanie „zapomniane”. Wolę ograniczać zmienność obiektów, ale nie zawsze jest to praktyczne. (Ruby nie jest językiem pozbawionym efektów ubocznych ;-)
8
Hash.merge! to najczystsze rozwiązanie
o ={ a:'a', b:'b'}
o.merge!(o){|key, value|"%#{ value }%"}
puts o.inspect
>{:a =>"%a%",:b =>"%b%"}
Odpowiedzi:
Jeśli chcesz, aby same ciągi mutowały w miejscu (prawdopodobnie i korzystnie wpływając na inne odwołania do tych samych obiektów łańcuchowych):
Jeśli chcesz, aby hash zmienił się w miejscu, ale nie chcesz wpływać na ciągi (chcesz, aby otrzymywał nowe ciągi):
Jeśli chcesz nowy hash:
źródło
Hash.[]
nie akceptuje tablicy par tablic, wymaga parzystej liczby bezpośrednich argumentów (stąd ikona z przodu).Hash#each
zwraca zarówno klucz, jak i wartość do bloku. W tym przypadku nie przejmowałem się kluczem, więc nie nazwałem go niczym przydatnym. Nazwy zmiennych mogą zaczynać się od podkreślenia, a w rzeczywistości mogą być tylko podkreśleniem. Nie ma z tego żadnej korzyści dla wydajności, jest to po prostu subtelna samodokumentująca uwaga, że nie robię nic z tą pierwszą wartością bloku.my_hash.inject(my_hash){ |h,(k,str)| h[k]="%#{str}%"; h }
, musisz zwrócić haszysz z blokuW Ruby 2.1 i nowszych możesz to zrobić
źródło
#transform_values!
jak wskazał sschmeck ( stackoverflow.com/a/41508214/6451879 ).Ruby 2.4 wprowadził metodę
Hash#transform_values!
, której możesz użyć.źródło
Hash#transform_values
(bez huku), który nie modyfikuje słuchawki. W przeciwnym razie świetna odpowiedź, dzięki!reduce
:-pNajlepszym sposobem na zmodyfikowanie wartości skrótu jest
Mniej kodu i wyraźny zamiar. Również szybciej, ponieważ żadne nowe obiekty nie są przydzielane poza wartościami, które należy zmienić.
źródło
gsub!
.update
lepiej przekazuje intencję niżmerge!
. Myślę, że to najlepsza odpowiedź.k
, użyj_
zamiast tego.Nieco bardziej czytelny,
map
do tablicy jednoelementowych skrótów ireduce
to zmerge
źródło
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(formap
), a następnie aHash
. Następnie na każdym etapie operacji redukcji zostanie zduplikowana „notatka”Hash
i dodana do niej nowa para klucz-wartość. Przynajmniej użyj:merge!
wreduce
celu zmodyfikowania ostatecznegoHash
na miejscu. Ostatecznie nie modyfikujesz wartości istniejącego obiektu, ale tworzysz nowy obiekt, co nie jest tym, o co chodziło w pytaniu.nil
jeślithe_hash
jest pustyIstnieje nowa metoda „Rails way” dla tego zadania :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
źródło
Hash#transform_values
. Tak powinno być od teraz.Jedna metoda, która nie wprowadza skutków ubocznych do oryginału:
Hash # map może być również interesującą lekturą, ponieważ wyjaśnia, dlaczego
Hash.map
nie zwraca Hash (dlatego wynikowa tablica[key,value]
par jest konwertowana na nowy Hash) i zapewnia alternatywne podejście do tego samego ogólnego wzorca.Miłego kodowania.
[Uwaga: nie jestem pewien, czy
Hash.map
semantyka zmieni się w Ruby 2.x]źródło
Hash.map
semantyka zmienia się w Ruby 2.x?źródło
each_with_object
w Rubim 1.9 (IIRC), który pozwala uniknąć konieczności bezpośredniego dostępu do nazwy iMap#merge
może również działać. Nie jestem pewien, czym różnią się zawiłe szczegóły.Hash.merge! to najczystsze rozwiązanie
źródło
Po przetestowaniu go z RSpec w ten sposób:
Możesz zaimplementować Hash # map_values w następujący sposób:
Funkcji można następnie użyć w następujący sposób:
źródło
Jeśli jesteście ciekawi, który wariant Inplace jest najszybszy, oto:
źródło