ЗмістНатисність на посилання, щоб перейти до потрібного місця
Масиви - одна з найзручніших структур даних у Ruby. Вони гнучкі, динамічні й мають величезну кількість вбудованих методів, які дозволяють обробляти дані максимально елегантно. Розберімо основні з них на прикладах.
each - простий обхід елементів
Метод each ітерує масив і виконує блок коду для кожного елемента. Зазвичай його використовують, коли потрібно щось зробити з елементами, але не створювати новий масив.
[1, 2, 3, 4, 5].each do |n|
puts "Number: #{n}"
end
Результат:
Number: 1 Number: 2 Number: 3 Number: 4 Number: 5 => [1, 2, 3, 4, 5]
each завжди повертає оригінальний масив, а не результат виконання блоку. Що це значить? Блок виконує puts (бачите надруковане "Number 1" і так далі). А повертає ітератор масив => [1, 2, 3, 4, 5]
Тобто якщо передати значення ітератора змінній - ми отримаємо масив.
myvar = [1, 2, 3, 4, 5].each do |n|
puts "Number: #{n}"
end
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
=> [1, 2, 3, 4, 5]
myvar
=> [1, 2, 3, 4, 5]
map - трансформація елементів
Метод map (або collect) створює новий масив, у якому кожен елемент - це результат виконання блоку.
numbers = [1, 2, 3, 4, 5]
squares = numbers.map { |n| n ** 2 }
p squares # => [1, 4, 9, 16, 25]
Використовуйте map, коли потрібно отримати новий набір даних із наявного масиву.
Чим відрізняється map від collect?
У Ruby map просто є більш звичною назвою для програмістів, які прийшли з інших мов (JavaScript, Python тощо). А collect - це історична назва, яка залишилася з ранніх версій Ruby (під впливом Smalltalk).
Якщо подивитись у код Ruby (Enumerable модуль):
alias collect map
тобто це точний синонім, не обгортка, не делегат - просто інше ім’я того ж методу.
select - фільтрація елементів
Метод select повертає масив елементів, для яких блок повертає true.
numbers = [1, 2, 3, 4, 5]
even_numbers = numbers.select { |n| n.even? }
p even_numbers # => [2, 4]
Існує зворотний метод - reject, який повертає елементи, для яких блок повертає false.
odd_numbers = numbers.reject { |n| n.even? }
p odd_numbers # => [1, 3, 5]
inject / reduce - акумуляція значення
inject (синонім reduce) - це потужний інструмент для "згортання" масиву в одне значення. Можна підрахувати суму, побудувати хеш тощо.
numbers = [1, 2, 3, 4, 5]
sum = numbers.inject(0) { |acc, n| acc + n }
p sum # => 15
Він бере початкове значення (у нашому випадку 0) і передає його разом з кожним елементом масиву у блок.
Синтаксис:
array.inject(initial_value) { |accumulator, element| ... }
- accumulator - змінна, у якій зберігається поточний результат.
- element - поточний елемент масиву.
- Блок повертає нове значення для accumulator.
Ще приклад - об’єднати елементи в рядок (string):
words = ["Ruby", "is", "fun"]
sentence = words.reduce("") { |acc, word| acc + word + " " }
p sentence.strip # => "Ruby is fun"
strip використали щоб прибрати зайвий пробіл вкінці.
Побудова хешу буде виглядати так:
letters = %w[a b c]
indexed = letters.inject({}) { |acc, l| acc[l] = l.upcase; acc }
# => {"a"=>"A", "b"=>"B", "c"=>"C"}
Але тут трохи складніше. Метод inject (або reduce) проходить по колекції і поступово накопичує результат у акумуляторі (acc).
Тут ми передаємо {} - тобто початкове значення акумулятора - порожній хеш.
{ |acc, l| acc[l] = l.upcase; acc }
Це блок, який викликається для кожного елемента l у масиві.
- acc - це поточний накопичувач (спочатку {})
- l - поточна літера ("a", потім "b", потім "c")
Всередині блоку:
acc[l] = l.upcase
додає в хеш пару:
- ключ: l (наприклад, "a")
- значення: l.upcase (тобто "A")
Після цього ми повертаємо сам acc, щоб передати його в наступну ітерацію
Звісно це можна зробити й іншим шляхом, але тут ми розглядаємо можливості саме цих методів.
filter_map - Ruby 2.7+ магія
Метод filter_map об’єднує map і select в один прохід масиву. Він перетворює і відфільтровує одночасно.
numbers = [1, 2, 3, 4, 5, 6]
even_squares = numbers.filter_map { |n| n**2 if n.even? }
p even_squares # => [4, 16, 36]
Це зручніше та ефективніше, ніж numbers.select { ... }.map { ... }.
Цей допис поки що не має жодних доповнень від автора/ки.