Метод
map є одним із найуживаніших методів у Ruby, що використовується для обробки колекцій. Він дозволяє застосувати блок коду до кожного елементу колекції і повернути нову колекцію з результатами виконання блоку. Насправді це і все пояснення. Але будь-яка інформація засвоюється краще не на абстракціях, а на простих і реальних прикладах. Тож розглянемо метод map трохи детальніше.
Як працює map
Метод map приймає блок, який виконується для кожного елементу колекції. Результати виконання цього блоку збираються в новий масив, який і повертається методом map.
Виконаємо збільшення кожного елементу масиву на 1 (інкремент)
numbers = [1, 2, 3, 4, 5]
incremented_numbers = numbers.map { |n| n + 1 }
incremented_numbers
Результат
=> [2, 3, 4, 5, 6]
Якщо спрощено:
Ми маємо змінну numbers, яка містить в собі масив елементів (колекція елементів) [1, 2, 3, 4, 5].
Далі ми створюємо змінну incremented_numbers та записуємо в неї результат виконання методу map.
Розглянемо конструкцію map:
numbers.map { |n| n + 1 }
# numbers - наша змінна (колекція)
# .map - виклик методу на змінній
# { |n| n + 1 } - блок, який буде виконано для кожного елементу колекції
Тут треба розглянути сам блок:
{ |n| n + 1 }
-
{} - фігурні дужки: Це тіло блоку, де міститься код, який буде виконано для кожного елементу масиву.
-
|n| - змінна: Це параметр блоку. Кожен елемент масиву передається в цю змінну один за одним.
-
n + 1: Це вираз, що виконується для кожного елементу. Він додає 1 до значення змінної n.
Коли ми виконуємо numbers.map { |n| n + 1 }, ось що відбувається:
Для першого елементу 1:
- n стає 1
- Виконується 1 + 1
- Результат 2 додається до нового масиву
Для другого елементу 2:
- n стає 2
- Виконується 2 + 1
- Результат 3 додається до нового масиву
І так далі для кожного елементу масиву. Новий масив записуємо у змінну incremented_numbers та дивимось її зміст
numbers = [1, 2, 3, 4, 5]
incremented_numbers = numbers.map { |n| n + 1 }
incremented_numbers
Результат
=> [2, 3, 4, 5, 6]
А що станеться зі змінною numbers? Нічого, вона
залишається незмінною.
Ще один приклад:
words = ["hello", "world", "ruby"]
new_words = words.map { |word| word.upcase }
new_words
=> ["HELLO", "WORLD", "RUBY"]
words
=> ["hello", "world", "ruby"]
Метод upcase модифікує літери (робить великими). Тож змінна
new_words має модифіковані слова, а
words залишається незмінною.
Доречі, в цих прикладах ми використовуємо конструкцію блоку у фігурних дужка. Блок також можна передавати через
do ... end (у випадках, коли код задовгий і непогано б було його розділити на декілька рядків). Тобто:
numbers.map { |n| n + 1 }
теж саме що й
numbers.map do |n|
n + 1
end
Ну й приклад з передачею результату у змінну:
incremented_numbers_braces = numbers.map { |n| n + 1 }
incremented_numbers_do_end = numbers.map do |n|
n + 1
end
Ще один приклад, але будемо модифікувати hash
hash = { a: 1, b: 2, c: 3 }
incremented_hash = hash.map { |key, value| [key, value + 1] }
incremented_hash
=> [[:a, 2], [:b, 3], [:c, 4]]
Але є нюанс. Виконання map повертає нам Array (масив). Тож нам треба конвертнути Array у Hash (за допомогою методу .to_h).
incremented_hash = incremented_hash.to_h
incremented_hash
=> {:a=>2, :b=>3, :c=>4}
Я думаю, що в конструкції
hash.map { |key, value| [key, value + 1] } доволі зрозуміло, що ми беремо key та value з кожного елементу хешу та повертаємо таку конструкцію
[key, value + 1].
Тобто результатом виконня map для елементу буде повернення масиву, де перший елемент -
key, а другий - модифіковане value (
value + 1).
Результат виконання на всій колекції такий:
incremented_hash
=> [[:a, 2], [:b, 3], [:c, 4]]
Масив масивів. Які вже потім ми конвертуємо у hash (виклик to_h).
map! - модифікує оригінальний масив
Знак оклику доданий до методу map означає, що оригінальна колекція (змінна) буде модифікована. Такий тип виклику методу треба використовувати обережно.
numbers = [1, 2, 3, 4, 5]
numbers.map! { |n| n + 1 }
numbers
=> [2, 3, 4, 5, 6]
- map: Створює новий масив, залишаючи оригінальний масив незмінним.
- map!: Модифікує оригінальний масив, змінюючи його елементи на місці.
Чому така назва методу (map)?
Назва map походить від математичної операції "відображення" (mapping), де кожен елемент одного набору "відображається" у відповідний елемент іншого набору. В програмуванні це означає застосування функції або блоку коду до кожного елементу колекції, щоб створити нову колекцію з результатами цих застосувань.
Метод map у Ruby є потужним інструментом для обробки колекцій, що дозволяє легко створювати нові колекції, перетворюючи кожен елемент за допомогою заданого блоку. Він має додаткові можливості (наприклад використання індексів) альтернативи (наприклад each) і навіть синоніми (collect). Але метою цього допису було пояснити принцип роботи методу map без сильних ускладнень. Інші штуки розглянемо в наступних дописах про Ruby.