Массивы и их методы

Массив — это упорядоченный список значений. Это самая универсальная коллекция Ruby, и у неё десятки готовых методов на любой случай.
Суть: массив (Array) хранит элементы по порядку с доступом по индексу (с нуля); он динамический (растёт и сжимается) и может содержать значения любых типов вперемешку.

Массивы в Ruby невероятно гибкие. Они не требуют фиксированного размера и типа: в одном массиве могут лежать число, строка и даже другой массив. Доступ к элементам — по индексу, начиная с нуля, причём отрицательные индексы считают с конца.

nums = [10, 20, 30, 40]
puts nums[0]        # => 10  (первый)
puts nums[-1]       # => 40  (последний)
puts nums.first(2).inspect  # => [10, 20]
puts nums.length    # => 4

nums << 50           # добавить в конец (push)
nums.unshift(5)     # добавить в начало
puts nums.inspect   # => [5, 10, 20, 30, 40, 50]

Разбор: добавление, удаление, срезы

Оператор << («shovel», лопата) добавляет элемент в конец — это самый частый способ. Для удаления есть pop (с конца), shift (с начала), delete (по значению). Срезы достаются диапазоном индексов.

letters = ["a", "b", "c", "d", "e"]
puts letters[1..3].inspect   # => ["b", "c", "d"]  срез
puts letters[1, 2].inspect   # => ["b", "c"]  старт + длина
last = letters.pop           # удалить и вернуть последний => "e"
letters.delete("a")          # удалить по значению
puts letters.inspect         # => ["b", "c", "d"]

Как работает под капотом

Массив Ruby хранит ссылки на объекты в непрерывном блоке памяти. Доступ по индексу мгновенный — Ruby сразу знает, где лежит N-й элемент. Добавление в конец почти всегда быстрое, а вот вставка в начало (unshift) дороже, потому что приходится сдвигать остальные элементы.

индексы:    0     1     2     3
          +-----+-----+-----+-----+
nums  =   | 10  | 20  | 30  | 40  |
          +-----+-----+-----+-----+
индексы:   -4    -3    -2    -1      <-- отрицательные с конца

  nums << 50   добавляет справа   --> [10,20,30,40,50]
  nums.shift   убирает слева      --> [20,30,40]

Частые ошибки

  • Выход за границы. Обращение к несуществующему индексу nums[99] возвращает nil, а не ошибку — и это коварно, ошибка всплывёт позже.
  • Путать [] и метод. nums[1,2] — это «старт, длина», а nums[1..2] — диапазон. Лёгко перепутать.
  • Изменять массив во время перебора. Удаление элементов прямо в each ломает обход — собирайте результат в новый массив.

Best practices

  • Для добавления в конец предпочитайте << — это идиоматично и читаемо.
  • Создавайте массив фиксированных значений быстро через %w[a b c] (массив слов) вместо кавычек и запятых.
  • Используйте .dig для безопасного доступа к вложенным массивам: data.dig(0, 1) не упадёт на nil.

Глубже: массивы как универсальный инструмент

Массив в Ruby настолько богат методами, что значительная часть повседневных задач решается одной их комбинацией без единого явного цикла. Нужно убрать дубликаты — uniq. Перевернуть — reverse. Перемешать — shuffle. Сгруппировать по N штук — each_slice. Разбить по условию — partition. Сплющить вложенность — flatten. Найти первый подходящий — find. Эта плотность методов не случайна: массив реализует модуль Enumerable, о котором речь пойдёт отдельно, и потому наследует десятки операций перебора и преобразования. Практический вывод для новичка: прежде чем писать цикл вручную, спросите себя — нет ли уже готового метода? В девяти случаях из десяти он есть, и код с ним будет короче, читаемее и быстрее (многие методы реализованы на C). Полезная привычка — держать под рукой документацию класса Array и периодически её листать: каждый новый освоенный метод убирает из вашего кода ещё один ручной цикл и ещё одну возможность ошибиться.

Итог. Массив — упорядоченная динамическая коллекция с доступом по индексу с нуля и отрицательными индексами с конца. Добавление в конец дёшево, в начало — дороже; обращение за границы даёт nil, а не ошибку.

Проверьте себя
1. Что вернёт обращение nums[-1] для массива nums = [10, 20, 30]?
Anil
BОшибку выхода за границы
C30 — последний элемент
D10 — первый элемент
2. Что произойдёт при обращении к несуществующему индексу, например nums[99]?
AПрограмма упадёт с ошибкой
BВернётся nil
CВернётся последний элемент
DМассив автоматически расширится