Как использовать методы массива в Ruby

Вступление

Arrays позволяет вам представлять списки данных в ваших программах. Как только у вас есть данные в массиве, вы можете сортировать их, удалять дубликаты, менять их порядок, извлекать разделы массива или искать в массивах определенные данные. Вы также можете преобразовать массив в string, преобразовать один массив данных в другой и свернуть массив в одно значение.

В этом руководстве вы познакомитесь с некоторыми из наиболее практичных методов, которые Ruby предоставляет для работы с данными, хранящимися в массивах.

Работая с этим учебником, вы увидите несколько методов, которые заканчиваются восклицательным знаком (+! +). Эти методы часто имеют побочные эффекты, такие как изменение исходного значения или повышение исключений. Многие методы, которые вы будете использовать в этом руководстве, имеют связанный метод с этим суффиксом.

Вы также встретите методы, которые заканчиваются знаком вопроса (+? +). Эти методы возвращают логическое значение.

Это соглашение об именах, используемое в Ruby. Это не то, что обеспечивается на программном уровне; это просто еще один способ определить, что вы можете ожидать от метода.

Давайте начнем наше исследование методов массива, рассмотрев несколько способов доступа к элементам

Доступ к элементам

Если вы уже ознакомились с учебным пособием Как работать с массивами в Ruby, вы знаете, что можете получить доступ отдельный элемент, использующий его индекс, который начинается с нуля, например так:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks[0]    # "Tiger"
sharks[1]    # "Great White"
sharks[-1]   # "Angel"

Вы также можете вспомнить, что вы можете использовать методы + first + и + last +, чтобы получить первый и последний элементы массива:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks.first   # "Tiger"
sharks.last    # "Angel"

Наконец, когда вы получаете доступ к элементу, который не существует, вы получите + nil +. Но если вы хотите получить ошибку, используйте метод + fetch +:

sharks.fetch(42)
OutputIndexError: index 42 outside of array bounds: -4...4

Если вы предпочитаете указывать собственное значение по умолчанию вместо сообщения об ошибке, вы также можете сделать это:

sharks.fetch(42, "Nope")     # "Nope"

Теперь давайте посмотрим, как получить более одного элемента из массива.

Получение нескольких элементов

Есть моменты, когда вы можете захотеть получить подмножество значений из вашего массива вместо одного элемента.

Если вы укажете начальный индекс, а затем количество элементов, которое вы хотите, вы получите новый массив, содержащий эти значения. Например, вы можете получить две средние записи из массива + sharks + следующим образом:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks[1,2]   # ["Great White", "Hammerhead"]

Мы начинаем с индекса + 1 +, который является " Great White ", и мы указываем, что мы хотим, чтобы элементы + 2 +, таким образом, мы получаем новый массив, содержащий " Great White " и ` + "Hammerhead" + `.

Вы можете использовать метод + slice +, чтобы сделать то же самое:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks.slice(1,2)   # ["Great White", "Hammerhead"]

Метод + slice + также возвращает новый массив, оставляя исходный массив без изменений. Однако, если вы используете метод + slice! +, Исходный массив также будет изменен.

Метод + take + позволяет вам получить указанное количество записей в начале массива:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sharks.take(2)  # ["Tiger", "Great White"]

Иногда вы хотите получить случайное значение из массива вместо определенного. Давайте рассмотрим, как.

Получение случайной записи из массива

Возможно, вы работаете над азартной игрой или пишете программу, которая выбирает победителя конкурса. Такие вещи требуют какого-то случайного значения. Распространенное решение состоит в том, чтобы поместить возможные варианты в массив и выбрать случайный индекс.

Чтобы получить случайный элемент из массива, вы можете сгенерировать случайный индекс между + 0 + и последним индексом массива и использовать его в качестве индекса для получения значения, но есть более простой способ: + sample + Метод получает случайную запись из массива.

Давайте использовать его, чтобы получить случайный ответ из множества стандартных ответов, создав примитивную версию игры Magic 8-Ball:

8ball.rb

answers = ["Yes", "No", "Maybe", "Ask again later"]
print answers.sample
OutputMaybe

Метод + sample + также принимает аргумент, который возвращает массив случайных записей, поэтому, если вам требуется более одной случайной записи, просто укажите нужное число:

random_sharks.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
sample = sharks.sample(2)
print sample
Output["Whale", "Great White"]

Давайте посмотрим, как найти конкретные элементы в массиве дальше.

Поиск и фильтрация элементов

Когда вы ищете конкретные элементы в массиве, вы обычно перебираете его элементы, пока не найдете то, что ищете. Но в массивах Ruby есть несколько методов, специально разработанных для упрощения процесса поиска в массивах.

Если вы просто хотите увидеть, существует ли элемент, вы можете использовать метод + include? +, Который возвращает + true +, если указанные данные являются элементом массива:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
sharks.include? "Tiger"      # true

["a", "b", "c"].include? 2   # false

Однако + include? + Требует точного соответствия, поэтому вы не можете искать частичное слово.

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
sharks.include? "Tiger"      # true
sharks.include? "tiger"      # false
sharks.include? "ti"         # false

Метод + find + находит и возвращает первый элемент в массиве, который соответствует указанному вами условию.

Например, чтобы идентифицировать первую запись в массиве + sharks +, которая содержит букву + a +, вы можете использовать метод + each +, чтобы сравнить каждую запись и прекратить итерацию, когда найдете первую, например:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = nil
sharks.each do |shark|
 if sharks.include? "a"
   result = shark
   break
 end
end

Или вы можете использовать метод + find +, чтобы сделать то же самое:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.find {|item| item.include?("a")}
print result
OutputHammerhead

+ find + выполняет блок, который вы предоставляете для каждого элемента в массиве. Если последнее выражение в блоке оценивается как + true +, метод + find + возвращает значение и прекращает итерацию. Если он ничего не находит после перебора всех элементов, он возвращает + nil +.

Метод + select + работает аналогичным образом, но он создает новый массив, содержащий все элементы, соответствующие условию, а не просто возвращает одно значение и останавливается.

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
results = sharks.select {|item| item.include?("a")}
print results
Output["Hammerhead", "Great White", "Whale"]

Метод + reject + возвращает новый массив, содержащий элементы, которые don’t соответствуют условию. Вы можете думать об этом как о фильтре, который удаляет ненужные элементы. Вот пример, который отклоняет все записи, содержащие букву + a +:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
results = sharks.reject {|item| item.include?("a")}
print results
Output["Tiger"]

+ select + и + reject + оба возвращают новый массив, оставляя исходный массив без изменений. Однако если вы используете методы + select! + И + reject! +, Исходный массив будет изменен.

Метод + find_all + является псевдонимом для + select +, но метода + find_all! + Нет.

Далее, давайте посмотрим, как сортировать значения массива.

Сортировка массива

Сортировка данных является обычной практикой. Возможно, вам придется алфавитизировать список имен или сортировать числа от наименьшего к наибольшему.

Массивы Ruby имеют метод + reverse +, который может инвертировать порядок элементов в массиве. Если у вас есть список данных, которые уже организованы, + reverse + - это быстрый способ перевернуть элементы:

sharks = ["Angel", "Great White", "Hammerhead", "Tiger"]
reversed_sharks = sharks.reverse
print reversed_sharks
Output["Tiger", "Hammerhead", "Great White", "Angel"]

Метод + reverse + возвращает новый массив и не изменяет оригинал. Используйте метод + reverse! +, Если вы хотите изменить исходный массив.

Однако обращение массива не всегда является наиболее эффективным или практичным способом сортировки данных. Используйте метод + sort + для сортировки элементов в массиве так, как вам нравится.

Для простых массивов строк или чисел метод + sort + эффективен и даст вам результаты, которые вы ищете:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sorted_sharks = sharks.sort
print sorted_sharks
Output["Angel", "Great White", "Hammerhead", "Tiger"]

Однако, если вы хотите отсортировать вещи по-другому, вы должны рассказать методу + sort +, как это сделать. Метод + sort + принимает блок Ruby, который дает вам доступ к элементам в массиве, чтобы вы могли их сравнить.

Для сравнения вы используете оператор сравнения _ (+ <⇒ +), часто называемый оператором spaceship. Этот оператор сравнивает два объекта Ruby и возвращает + -1 +, если объект слева меньше, + 0 +, если объекты одинаковы, и + 1 +, если объект слева больше.

1 <=> 2    # -1
2 <=> 2    #  0
2 <=> 1    #  1

Метод + sort + в Ruby принимает блок, который должен возвращать + -1 +, + 0 + или + 1 +, который затем используется для сортировки значений в массиве.

Вот пример, который явно сравнивает записи в массиве для сортировки в порядке возрастания:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sorted_sharks = sharks.sort{|a,b| a <=> b }
print sorted_sharks

Переменные + a + и + b + представляют отдельные элементы в массиве, которые сравниваются. Результат выглядит так:

Output["Angel", "Great White", "Hammerhead", "Tiger"]

Чтобы отсортировать акул в обратном порядке, переверните объекты в сравнении:

sharks = ["Tiger", "Great White", "Hammerhead", "Angel"]
sorted_sharks = sharks.sort{|a,b| b <=> a }
print sorted_sharks
Output["Tiger", "Hammerhead", "Great White", "Angel"]

Метод + sort + отлично подходит для массивов, содержащих простые типы данных, такие как целые числа, числа с плавающей запятой и строки. Но когда массивы содержат более сложные объекты, вам придется сделать немного больше работы.

Вот массив хэшей, каждый из которых представляет акулу:

sharks = [
 {name: "Hammerhead"},
 {name: "Great white"},
 {name: "Angel"}
]

Сортировать это с помощью + sort не так просто. Вызов + sort + в массиве завершается неудачно:

sharks.sort
OutputArgumentError: comparison of Hash with Hash failed

Чтобы выполнить сравнение, мы должны сказать + sort +, что мы хотим сравнить. Поэтому мы сравним значения ключа +: name + в хэше:

sorted_sharks.sort{|a, b| a[:name] <=> b[:name]}
print sorted_sharks
Output[{:name=>"Angel"}, {:name=>"Great white"}, {:name=>"Hammerhead"}]

Когда вы работаете с более сложными структурами, вы можете вместо этого взглянуть на метод + sort_by +, который использует более эффективный алгоритм сортировки. + sort_by + принимает блок, который требует только один аргумент, ссылку на текущий элемент в массиве:

sharks = [
 {name: "Hammerhead"},
 {name: "Great white"},
 {name: "Angel"}
]

sorted_sharks = sharks.sort_by{|shark| shark[:name] }
print sorted_sharks
Output[{:name=>"Angel"}, {:name=>"Great white"}, {:name=>"Hammerhead"}]

Метод + sort_by + реализует Schwartzian transform, алгоритм сортировки, наилучшим образом подходящий для сравнения объектов на основе значения определенного ключа. Следовательно, вы будете использовать + sort_by + при сравнении коллекций объектов, так как это более эффективно.

И + sort + и + sort_by + возвращают новые массивы, оставляя исходный массив без изменений. Если вы хотите изменить исходный массив, используйте + sort! + И + sort_by! +.

В дополнение к сортировке значений, вы также можете избавиться от дубликатов.

Удаление дублирующих элементов

Иногда вы получаете списки данных, которые имеют некоторое дублирование. Вы можете перебирать массив и отфильтровывать дубликаты, но метод Ruby + uniq + делает это намного проще. Метод + uniq + возвращает новый массив со всеми удаленными дублирующимися значениями.

[1,2,3,4,1,5,3].uniq   # [1,2,3,4,5]

Иногда, когда вы объединяете два набора данных, вы получаете дубликаты. Возьмите эти два массива акул:

sharks = ["Tiger", "Great White"]
new_sharks = ["Tiger", "Hammerhead"]

Если мы добавим их вместе, мы получим дубликат записи:

sharks + new_sharks
# ["Tiger", "Great White", "Tiger", "Hammerhead"]

Вы можете использовать + uniq + для удаления дубликатов, но лучше не вводить их полностью. Вместо того, чтобы добавлять массивы вместе, используйте оператор pipe` + | + `, который объединяет массивы вместе:

sharks | new_sharks
# ["Tiger", "Great White", "Hammerhead"]

Массивы Ruby также поддерживают вычитание, что означает, что вы можете вычесть + new_sharks + из + sharks +, чтобы получить только новые значения:

sharks = ["Tiger", "Great White"]
new_sharks = ["Tiger", "Hammerhead"]
sharks - new_sharks   # ["Great White"]

Далее, давайте посмотрим, как манипулировать значением каждого элемента.

Преобразование данных

Метод + map + и его псевдоним + collect + могут преобразовывать содержимое массива, что означает, что он может выполнять операцию с каждым элементом в массиве.

Например, вы можете использовать + map + для выполнения арифметики для каждой записи в массиве и создания нового массива, содержащего новые значения:

numbers = [2,4,6,8]

# square each number
squared_numbers = numbers.map {|number| number * number}

print squared_numbers

Переменная + squared_numbers + - это массив исходных чисел в квадрате:

[4, 16, 36, 64]

+ map + часто используется в веб-приложениях для преобразования массива в элементы для раскрывающегося списка HTML. Вот очень упрощенная версия того, как это может выглядеть:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]

options = sharks.map {|shark| "<option>#{shark}</option>"}

print options

В массиве + options + теперь каждая акула заключена в HTML-тег + <option> </ option> +:

["<option>Hammerhead</option>", "<option>Great White</option>", "<option>Tiger</option>", "<option>Whale</option>"]

+ map + возвращает новый массив, оставляя исходный массив без изменений. Использование + map! + Изменило бы существующий массив. И помните, что + map + имеет псевдоним + collect +. Вы должны быть последовательными и использовать один или другой в своем коде.

Поскольку + map + возвращает новый массив, массив можно затем преобразовать и манипулировать или даже преобразовать в строку. Давайте посмотрим на это дальше.

Преобразование массива в строку

Все объекты в Ruby имеют метод + to_s +, который преобразует объект в строку. Это то, что использует оператор + print +. Учитывая наш массив + sharks +:

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]

Вызов метода + to_s + создает эту строку:

"[\"Hammerhead\", \"Great White\", \"Tiger\", \"Whale\"]"

Это отлично подходит для отладки, но не очень полезно в реальной программе.

Метод + join + преобразует массив в строку, но дает вам гораздо больший контроль над тем, как вы хотите объединить элементы. Метод + join + принимает аргумент, который указывает символ, который вы хотите использовать в качестве разделителя. Чтобы преобразовать массив акул в строку имен акул, разделенных пробелами, вы должны сделать что-то вроде этого:

shark_join.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join(" ")
print result
OutputHammerhead Great White Tiger Whale

Если вы хотите, чтобы каждое имя акулы разделялось запятой и пробелом, используйте запятую и пробел в качестве разделителя:

shark_join.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join(", ")
print result
OutputHammerhead, Great White, Tiger, Whale

Если вы не укажете аргумент для метода + join +, вы все равно получите строку, но в ней не будет разделителей:

shark_join.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
result = sharks.join
print result
OutputHammerheadGreat WhiteTigerWhale

Использование + join + в сочетании с + map + - быстрый способ преобразования массива данных в вывод. Используйте + map + для преобразования каждого элемента данных, затем используйте + join + для преобразования всего этого в строку, которую вы можете распечатать. Помните наш пример преобразования нашего массива + sharks + в массив элементов HTML? Вот снова тот же пример, но на этот раз мы будем использовать + join + для преобразования массива элементов в строку с символами перевода строки в качестве разделителя:

map.rb

sharks = ["Hammerhead", "Great White", "Tiger", "Whale"]
options = sharks.map {|shark| "<option>#{shark}</option>"}
output = options.join("\n")
print output
Output<option>Hammerhead</option>
<option>Great White</option>
<option>Tiger</option>
<option>Whale</option>

Вместо того, чтобы преобразовывать массив в строку, вы можете получить общее его содержимое или выполнить какой-либо другой вид преобразования, который приведет к единственному значению. Это дальше.

Сокращение массивов до единого значения

Когда вы работаете с набором данных, вы можете обнаружить, что вам нужно объединить данные в одно значение, например сумму. Один из способов сделать это - использовать переменную и метод + each +:

result = 0
[1, 2, 3].each {|num| result += num}
print result
Output6

Вы можете использовать метод + redu + +, чтобы сделать это вместо этого. Метод `+ redu + выполняет итерацию по массиву и сохраняет промежуточную сумму, выполняя двоичную операцию для каждого элемента.

Метод + redu + принимает начальное значение для результата, а также блок с двумя локальными значениями: ссылка на результат и ссылка на текущий элемент. Внутри блока вы указываете логику для вычисления конечного результата.

Поскольку мы хотим суммировать массив, мы инициализируем результат как + 0 +, а затем добавляем текущее значение к результату в блоке:

output = [1,2,3].reduce(0) {|result, current| result += current }
print output
Output6

Если вы планируете инициализировать результат как + 0 +, вы можете опустить аргумент и просто передать блок. Это автоматически установит результат на первое значение в массиве:

output = [1,2,3].reduce {|result, current| result += current }
print output
Output6

В методе + + + вы также указываете binary method или метод для одного объекта, который принимает в качестве аргумента другой объект, который он будет выполнять для каждой записи в массиве. + lower + затем использует результаты для создания единственного значения.

Когда вы пишете +2 + 2 + в Ruby, вы фактически вызываете метод + для целого числа + 2 +:

2.+(2)   # 4

Ruby использует некоторый syntactic sugar, поэтому вы можете выразить его как +2 + 2 +.

Метод + + + позволяет вам указать двоичный метод, передав его имя в виде символа. Это означает, что вы можете передать : + методу + lower + для суммирования массива:

output = [1, 2, 3].reduce(:+)
print output
Output6

Вы можете использовать + redu + + ', чтобы сделать больше, чем просто добавлять списки чисел. Вы можете использовать его для преобразования значений. Помните, что `+ lower + уменьшает массив до одного значения. Но нет правила, согласно которому одно значение не может быть другим массивом.

Допустим, у нас есть список значений, которые нам нужно преобразовать в целые числа. но нам нужны только значения, которые можно преобразовать в целые числа.

Мы могли бы использовать + rejects, чтобы выбросить нечисловые значения, а затем использовать` + map`, чтобы преобразовать оставшиеся значения в целые числа. Но мы можем сделать все это за один шаг с помощью + lower +. Вот как.

Используйте пустой массив в качестве значения инициализации. Затем в блоке преобразуйте текущее значение в целое число с помощью метода + Integer +. Если значение не может быть преобразовано в целое число, + Integer + вызовет исключение, которое вы можете перехватить и присвоить значению + nil +.

Затем возьмите значение и поместите его в массив, но только если это не + nil +.

Вот как выглядит код. Попробуйте это:

convert_array_of_values.rb

values = ["1", "2", "a", "3"]
integers = values.reduce([]) do |array, current|
 val = Integer(current) rescue nil
 array.push(val) unless val.nil?
 array
end
print integers
Output[1,2,3]

Всякий раз, когда у вас есть список элементов, которые необходимо преобразовать в одно значение, вы можете решить его с помощью `+ redu + +.

Заключение

В этом уроке вы использовали несколько методов для работы с массивами. Вы захватили отдельные элементы, извлекли значения путем поиска в массиве, отсортировали элементы, и вы преобразовали данные, создав новые массивы, строки и итоги. Вы можете применять эти концепции для решения многих общих проблем программирования с Ruby.

Обязательно ознакомьтесь с этими учебниками, чтобы продолжить изучение работы с данными в Ruby: