Przegląd 20 iteratorów w Rubim

Posted by Jacek Galanciak on

W dzisiejszym odcinku przyjrzymy się bliżej iteratorom w języku Ruby.

Do dzieła!

all?

Przekazuje do bloku każdy element kolekcji. Zwraca true, jeśli blok nigdy nie zwróci false (lub nil)

[1, 2, 5].all? { |element| element <= 5 } # => true
[1, 2, 5].all? { |element| element <= 4 } # => false
%w{RAM CPU GPU DDR}.all? { |element| element.length == 3 } # => true

[1, 2, 5].all? do |element|
    puts "Sprawdzam #{element}; #{element} < 5: #{element < 5}"
    element < 5
end # => false

Wyjście:

Sprawdzam 1; 1 < 5: true
Sprawdzam 2; 2 < 5: true
Sprawdzam 5; 5 < 5: false

any?

Zwraca true, jeśli przekazany do bloku element kiedykolwiek zwróci true

[1, 2, 5].any? { |element| element > 5 } # => false
[1, 2, 5].any? { |element| element == 2} # => true

collect (map)

Przekazuje do bloku każdy element kolekcji, następnie tworzy nową - z elementów zwracanych przez blok.

%w{kot tulipan parowka}.collect { |element| element.upcase }
# => ["KOT", "TULIPAN", "PAROWKA"]
[1, 2, 3].collect { |element| element + 1}
# => [2, 3, 4]

collect! (map!)

Działa jak collect, z tą jednak różnicą, że operacji kolekcja dokonuje na sobie, w każdej iteracji zmieniając swoją zawartość.

a = [1, 2, 3] # => [1, 2, 3]
a.collect! { |element| element + 1 } # => [2, 3, 4]
a # => [2, 3, 4]

delete_if

Usuwa z kolekcji elementy, dla których blok zwraca true

[1, 2, 3, 4, 5, 6].delete_if { |i| i%2 == 0 } # => [1, 3, 5]

detect (find)

Zwraca pierwszy element, dla którego blok zwróci true

(36..100).detect { |i| i%7 == 0 } # => 42

downto

Wykonuje blok, podając w kolejności malejącej liczby od siebie samej do podanej jako parametr.

9.downto(0) { |i| print i }

9876543210

each

Przekazuje do bloku każdy z elementów kolekcji

['pies', 'kot', 'ryba'].each { |word| print word + " " } # => ["pies", "kot", "ryba"]
(0..9).each { |i| print i } # => (0..9)
# Wyjście: pies kot ryba 0123456789

each_index

Działa jak each, ale przekazuje sam indeks każdego elementu.

[3, 6, -5].each_index { |i| print i.to_s + " " }
# Wyjście: 0 1 2

each_with_index

Przekazuje jednocześnie element i jego indeks do bloku.

["jeden", 2, "trzy"].each_with_index do |element, index|
    puts "Indeksowi #{index} przyporządkowałem #{element}"
end
Indeksowi 0 przyporzdkowaem jeden
Indeksowi 1 przyporządkowałem 2
Indeksowi 2 przyporządkowałem trzy

find_all

Zwraca wszystkie elementy kolekcji, dla których blok zwróci true

(0..30).find_all { |i| i%9 == 0 } # => [0, 9, 18, 27]

grep

Zwraca elementy spełniające dopasowanie podane jako parametr. Jeśli podano blok, przekazuje do niego tylko te elementy i zwraca tablicę zbudowaną z wartości zwracanych przez blok.

# Zwraca wyrazy zawierające literę 'r'
%w{ruby python perl php}.grep(/r/) do |w| 
    print "#{w.upcase} "
    w.capitalize
end # => ["Ruby", "Perl"]
RUBY PERL

inject

Przekazuje do bloku każdy element kolekcji. Posiada dodatkowo pamięć, która początkowo jest równa pierwszemu elementowi (lub wartości podanej jako parametr). Po zakończeniu każdej iteracji pamięć jest update'owana do wartości zwracanej przez blok.

# Zwraca największą liczbę z tablicy
a = [-5, 2, 10, 17, -50]
a.inject a.first do |mem, element|
    mem > element ? mem : element
end # => 17

# Silnia
(1..5).inject do |mem, element|
    mem *= element
end # => 120

partition

Zwraca dwie tablice: jedną z elementami, dla których blok zwraca true, i drugą z resztą.

(1..6).partition { |i| i%2 == 0 } # => [[2, 4, 6], [1, 3, 5]]

reject

Odrzuca z kolekcji wszystkie elementy, dla których blok zwróci true.

(1..10).reject { |i| i >= 3 and i <= 7 } # => [1, 2, 8, 9, 10]

reject!

Wyrzuca z siebie elementy, dla których blok zwraca true

a = (1..10).to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
a.reject! { |i| i >= 3 and i <= 7 } # => [1, 2, 8, 9, 10]
a # => [1, 2, 8, 9, 10]

reverse_each

Działa jak each tyle, że podaje elementy w odwrotnej kolejności.

(0..9).to_a.reverse_each { |i| print i }
9876543210

step

Przekazuje do bloku wartości od, do - z określonym krokiem.

# (1)
0.step(100, 10) { |i| puts i}
# (2)
(0..100).step(10) { |i| puts i }

W obu przypadkach wyjście będzie wyglądało tak:

0
10
20
30
40
50
60
70
80
90
100

times

Wykonuje dany blok określoną ilość razy.

5.times { puts "Hej!" }
5.times { |i| print "#{i} "}
Hej!
Hej!
Hej!
Hej!
Hej!
0 1 2 3 4

upto

Iteruje blok, przekazując liczby od, do.

1.upto(3) { |i| print i }
123

Podsumowanie

Iteratory są bardzo ważną częścią języka, pozwalają wykonywać mniej lub bardziej skomplikowane czynności w sposób prosty i zwięzły. Warto w pełni poznać ich możliwości i, co ważne, korzystać z nich. Bo własnie to pokazuje, jak dobrzy jesteśmy w Rubim.