Chcę zmienić każdą wartość w skrócie, aby dodać „%” przed i po wartości tak
{ :a=>'a' , :b=>'b' }
należy zmienić na
{ :a=>'%a%' , :b=>'%b%' }
Jak najlepiej to zrobić?
Chcę zmienić każdą wartość w skrócie, aby dodać „%” przed i po wartości tak
{ :a=>'a' , :b=>'b' }
należy zmienić na
{ :a=>'%a%' , :b=>'%b%' }
Jak najlepiej to zrobić?
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):
# 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 }
Jeśli chcesz nowy 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.[]
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 bloku
W Ruby 2.1 i nowszych możesz to zrobić
{ a: 'a', b: 'b' }.map { |k, str| [k, "%#{str}%"] }.to_h
#transform_values!
jak wskazał sschmeck ( stackoverflow.com/a/41508214/6451879 ).
Ruby 2.4 wprowadził metodę Hash#transform_values!
, której możesz użyć.
{ :a=>'a' , :b=>'b' }.transform_values! { |v| "%#{v}%" }
# => {:a=>"%a%", :b=>"%b%"}
Hash#transform_values
(bez huku), który nie modyfikuje słuchawki. W przeciwnym razie świetna odpowiedź, dzięki!
Najlepszym sposobem na zmodyfikowanie wartości skrótu jest
hash.update(hash){ |_,v| "%#{v}%" }
Mniej kodu i wyraźny zamiar. Również szybciej, ponieważ żadne nowe obiekty nie są przydzielane poza wartościami, które należy zmienić.
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 i reduce
to zmerge
the_hash.map{ |key,value| {key => "%#{value}%"} }.reduce(:merge)
Hash[the_hash.map { |key,value| [key, "%#{value}%"] }]
Array
(for map
), a następnie a Hash
. Następnie na każdym etapie operacji redukcji zostanie zduplikowana „notatka” Hash
i dodana do niej nowa para klucz-wartość. Przynajmniej użyj :merge!
w reduce
celu zmodyfikowania ostatecznego Hash
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śli the_hash
jest pusty
Istnieje nowa metoda „Rails way” dla tego zadania :) http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values
Hash#transform_values
. Tak powinno być od teraz.
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.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]
Hash.map
semantyka zmienia się w Ruby 2.x?
my_hash.each do |key, value|
my_hash[key] = "%#{value}%"
end
each_with_object
w Rubim 1.9 (IIRC), który pozwala uniknąć konieczności bezpośredniego dostępu do nazwy i Map#merge
może również działać. Nie jestem pewien, czym różnią się zawiłe szczegóły.
Hash.merge! to najczystsze rozwiązanie
o = { a: 'a', b: 'b' }
o.merge!(o) { |key, value| "%#{ value }%" }
puts o.inspect
> { :a => "%a%", :b => "%b%" }
Po przetestowaniu go z RSpec w ten sposób:
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
Możesz zaimplementować Hash # map_values w następujący sposób:
class Hash
def map_values
Hash[map { |k, v| [k, yield(v)] }]
end
end
Funkcji można następnie użyć w następujący sposób:
{:a=>'a' , :b=>'b'}.map_values { |v| "%#{v}%" }
# {:a=>"%a%", :b=>"%b%"}
Jeśli jesteście ciekawi, który wariant Inplace jest najszybszy, oto:
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