На мой взгляд работа с массивами - одна из самых сильных сторон руби. С ними можно cделать вообще все что пожелаешь. Тут я хочу привести пример тех полезных методов и трюков, которые я использую чуть ли не каждый день. Я опущу совсем уж банальщину типа <<. Прошу опытный рубистов не кидать в меня помидорами, тема больше для неопытных
Почти все методы не изменяют начальный массив, но у многих есть бенг вариант (c ! на конце), который это делает. Это называется side effect (неприятная штука, если ей злоупотреблять).
Операции над массивами, они очень быстрые и главное мощные:
Про сложение знают наверное все
[1, 2] + [3, 4] #=> [1, 2, 3, 4]
Вычитание тоже очень полезно (например для валидации с запрещенными элементами).
[3, 4, 5] - [4, 5] #=> [3] Удаляет из первого массива все элементы второго массива. (Не изменяет изначальный массив, возвращает копию)
Пересечение массивов - когда есть массив с разрешенными элементами, и массив, который нужно очистить. Очень часто использую.
[1, 2, 3] & [2, 3] # => [2, 3] Вернет новый массив с элементами которые встречаются в обоих массивах.
Объединение. Работает примерно так - складывает два массива, потом идет по новому массиву и выкидывает все элементы, которые встречал раньше.
[1, 2, 3, 3] | [2, 4, 5] # => [1, 2, 3, 4, 5]
C умножением будьте осторожны, на самом деле элементы не множатся а множится ссылка на них.
[ Object.new ] * 4 #Создаст массив с 4 ссылками на один объект
Array.new(4) { Object.new } # Чаще всего когда умножают массив - хотят именно такой результат.
# 4 раза выполни блок, результат сохрани в новый массив.
Работа с индексами не такое уж и тривиальное занятие.
Получить подмасив:
[1, 2, 3, 4, 5, 6][2 .. 4] #=> [3, 4, 5] Две точки включают последний элемент
[1, 2, 3, 4, 5, 6][2 ... 3] #=> [3, 4] Три точки - не включают, кстати 2 .. 3 - это Range, а не магия :)
[1, 2, 3, 4, 5][3 .. 1] #А это не сработает, когда хотите перевернутую часть массива делайте так:
[1, 2, 3, 4, 5][1 .. 3].reverse
То что совсем не очевидно на первый взгляд, но очень полезно - замена при помощи индексов
arr = [1, 2, 3, 4, 5]
arr[2 .. 3] = 'x' #Возьми элементы со второго по третий и замени их на x
arr # => [1, 2, 'x', 5]
arr[0 .. 1] = ['y', 'y', 'y'] #Возьми элементы с первого по второй и замени их на новый массив ( при этом, как ни странно, новый массив не будет одним элементом, он вставится и развернется)
arr # => ['y', 'y', 'y', 'x', 5]
arr[1 ... 2] = ['z', 'z'] #Возьми элементы со второго по третий (не включая, т.е. только второй) И замени их на новый массив.
arr # => ['y', 'z', 'z', 'y', 'x', 5]
arr[0 .. 1] = [] #Удали элементы 0 и 1
arr # => ['z', 'y', 'x', 5]
arr[1 ... 1] = ['i', 'i'] #Вставь после первого элемента новый массив.
arr # => ['z', 'i', 'i', 'y', 'x', 5]
Array#map - рабочая лошадка, когда из одного массива нужно получить другой массив, связанный с первым.
numbers = [18, 13, 14, 15]
numbers.map { |i| "i + 3 = #{i + 3}" } #map выполняет блок для каждого элемента массива, результат становится элементом нового масива
Array#select, Array#reject - исполняют блок для каждого элемента массива, если блок возвращает истину то первый оставляет элемент в новом массиве, второй - наоборот выкидывает. Оба имеют bang аналоги, используйте с умом
[1, 2, 3, 4].select { |i| i > 2 } #=> [3, 4]
[1, 2, 3, 4].reject { |i| i > 2 } #=> [1, 2]
Array#inject - очень мощная вещь, очень часто использую. Позволяет пройтись по массиву, с каждым элементом выполнить блок и сохранить результат по всему стеку.
arr = [1, 2, 3, 4, 5, 6, 7]
#Метод принимает один аргумент, который будет начальным значением
arr.inject 0 do |sum, element| #В блок передается результат по стеку и текущий элемент
if element % 2 == 0
sum + element #Вы можете не изменять значение sum, все равно в следующий блок
else #передастся результат исполнения предыдущего
sum #(последнее выражение в предыдущем блоке)
end
end # => В итоге получим сумму всех четных элементов.
Если вы завели переменную-массив, и заполняете ее во время перебора, то скорее всего вам нужен inject.
Array#compact - приятный метод, который просто удаляет все nil из массива.
[1, nil, nil, 2, 3, nil].compact # => [1, 2, 3]
# недавно игрался с мейкером -
$game_party.line_set.slots.map(&:buttler).compact # тут с map используется краткий синтаксис блоков, могу и про это рассказать как-нибудь.
Array#flatten - мой любимец
Разворачивает массив любой вложенности в простой массив:
[1, [2, [3]], 4].flatten #[1, 2, 3, 4]
Я сам удивляюсь на сколько часто я использую flatten. Он более полезный, чем может показаться.
Array#empty? - возвращает true когда в массиве ничего нет
Array#any? - возвращает true когда в массиве что нибудь есть.
[1, 2, 3, 4, 5, 6].any? { |i| i > 2 } #Такой синтаксис any? применяет к каждому элементу блок,
#пока блок не вернет истину (не nil или false).
#Тогда и any? вернет true
Array#find - применяет блок к каждому элементу пока он не вернет истину. Когда истина получена - возвращает текущий элемент
[1, 2, 3, 4, 5, 6].find { |i| i > 3 } # => 4. До 5 и 6 он не доберется.
#with_index - позволяет к любому итератору подмешать индекс текущего элемента:
#Это можно использовать к примеру в параллельном переборе
arr1 = [2, 3]
arr2 = [4, 5]
arr1.each.with_index do |element, index|
puts element
puts arr2[index]
end
Array#sample возвращает случайный элемент массива, или несколько уникальных случайных элементов, если передали параметр
arr = [1, 2, 3, 4, 5]
arr.sample # => 3
arr.sample(3) # => [1,3,4]
Array#uniq - если хотите получить массив уникальных значений
Array#join приводит каждый элемент в строку, потом соединяет полученные строки через строку-аргумент.
arr = [1, 2, 3]
"#{arr.join(' + ')} = #{arr.inject(0) { |s, i| s + i }}" #=> "1 + 2 + 3 = 6"
И последний который я сегодня упомяну - sort. Без блока он просто сортирует массив. С блоком его используют, когда нужно как то по сложному отсортировать массив. В блок передаются по два элемента. Метод ожидает от блока один из трех вариантов = -1 когда левый элемент меньше правого, 0 - когда элементы равны, +1 - когда левый элемент больше правого.
arr = [1, 2, 3, 4, 5, 6, 7, 8] # отсортируем его так - сначала четные, потом нечетные
arr.sort { left, right| left % 2 - right % 2 } #[8, 2, 6, 4, 5, 3, 7, 1]
Получилось многовато, надеюсь эта стена текста была полезна кому нибудь. Удачи!