Ostatnio pracowałam z metodą each_with_object
. Jak zazwyczaj w takich sytuacjach zawsze staram się przed użyciem jakiejś metody sprawdzić jej dokumentację. Weszłam więc na APIdock przeglądam zastosowanie oraz przykłady. Okazało się że nie było tam jednego z przypadków zastosowania. Chciałam go dodać, lecz bez powodzenia. Postanowiłam więc, że skoro czekam na rozwiązanie problemu ze strony APIdock mogę napisać krótką notatkę na temat metody each_with_object
tutaj.
Najbardziej pożytecznym i wydaje mi się również popularnym użyciem tej metody jest podanie jako argumentu tablicy lub hasha (tablicy słownikowej). Można to zrobić przykładowo:
[:foo, :bar, :jazz].each_with_object({}) do |item, hash|
hash[item] = item.to_s.upcase
end
=> {:foo=>"FOO", :bar=>"BAR", :jazz=>"JAZZ"}
lub
(1..10).each_with_object([]) do |item, array|
array << item ** 2
end
=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Wiem te przykłady są trywialne, ale chodzi o to by zrozumieć samą konstrukcję użycia. Dzięki tej metodzie nie musimy deklarować tablicy przed pętlą:
array = []
(1..10).each do |item|
array << item ** 2
end
array
=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Oczywiście dla tego przykładu można by to zrobić z użyciem metody map
:
(1..10).map { |item| item ** 2 }
=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Innym ciekawym zastosowaniem each_with_object
jest zliczanie częstotliwości występowania:
['one', 'two', 'one', 'one'].each_with_object(Hash.new(0)) do |item, hash|
hash[item] += 1
end
=> {"one"=>3, "two"=>1}
W tym przypadku ustawiamy domyślną wartość dla tablicy słownikowej na 0
. Dzięki temu zliczanie ilości wystąpień elementów w tablicy jest szybkie i proste. Nie potrzebujemy warunku if
by zabezpieczyć się przed nieoczekiwaną wartością nil
:
if hash[item]
hash[item] += 1
else
hash[item] = 0
end
Uwaga
W metodzie each_with_object
nie można użyć niemutowalnych (inmutable) obiektów jak liczby. Przykład poniżej nie zwróci Wam 55 tylko 0.
(1..10).each_with_object(0) do |item, sum|
sum += item
end
=> 0
Możemy to zrobić też inaczej:
(1..10).reduce(:+)
=> 55
lub
(1..10).inject(:+)
=> 55
lub w Ruby on Rails:
(1..10).sum
=> 55
Przy okazji czy wiecie jaka jest różnica między metodą reduce
a inject
? Nie ma różnicy. Te dwie metody to tak naprawdę jedna i ta sama metoda, ale mająca dwie nazwy tzw. alias.
W tym momencie powinniśmy jeszcze wspomnieć o jednej rzeczy. Metodę inject
możemy stosować też bardzo podobnie do each_with_object
. Różnica jest w kolejności argumentów w bloku i tym że w ostatniej linii bloku dla metody inject
zawsze musimy zwrócić naszą wartość agregującą (przykładowo sumującą). Popatrzcie tutaj:
(1..10).inject([]) do |array, item|
array << item ** 2
end
=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
lub
[:foo, :bar, :jazz].inject({}) do |hash, item|
hash[item] = item.to_s.upcase
hash
end
=> {:foo=>"FOO", :bar=>"BAR", :jazz=>"JAZZ"}
Uwaga
Kiedy używamy array << item ** 2
zawsze zostaje tu zwrócona cała tablica, ale dla przykładu drugiego hash[item] = item.to_s.upcase
zwraca item.to_s.upcase
a nie hash
więc musimy pamiętać by w ostatniej linii bloku zawsze zwrócić hash
.
A teraz brakujący przypadek. Metodę each_with_object
można nie tylko używać na tablicy czy enumeratorze ale również na hashu (tablicy słownikowej). Taki przypadek wygląda troszkę inaczej. Zobaczcie sami:
{ foo: 1, bar: 2, jazz: 3 }.each_with_object({}) do |(key, value), hash|
hash[key] = value**2
end
=> {:foo=>1, :bar=>4, :jazz=>9}
lub
{ foo: 1, bar: 2, jazz: 3 }.each_with_object([]) do |(key, value), array|
array << { id: value, name: key }
end
=> [{:id=>1, :name=>:foo}, {:id=>2, :name=>:bar}, {:id=>3, :name=>:jazz}]
To był krótki przegląd tego co można zrobić z metodą each_with_object
. Mam nadzieje, że zastosowanie tej metody podoba się Wam tak samo jak mnie. Jeżeli macie jakieś pytania zostawcie je poniżej w komentarzach. Do zobaczenia następnym razem!
Potrzebujesz pomocy?
Jeśli szukasz doświadczonej programistki Ruby z ponad dziesięcioletnim stażem, śmiało skontaktuj się ze mną.
Mam doświadczenie w różnych domenach, a szczególną wagę przykładam do szybkiej reakcji na opinie użytkowników i pracy zespołowej. Pomogę Ci stworzyć świetny produkt.