Ассоциативные массивы, символы

В этом уроке мы расскажем вам о двух важных понятиях в программировании вообще и в Ruby в частности: ассоциативные массивы (их ещё часто называют «хэшами») и метки (или символы, «symbols»).

План урока

  1. Ассоциативные массивы или «хэши»
  2. Как пользоваться хэшами в Ruby
  3. Символы в Ruby (или метки)

Ассоциативные массив

Ассоциативный массив — это тип данных, которые представляет собой набор пар «ключ» — «значение». Вот простой пример:

Name: Вадим
Surname: Венедиктов
City: Москва

В руби для работы с ассоциативными массивами используют интерфейс хеш-таблицы, поэтому мы будем часто называть ассоциативные массивы хэшами.

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

{
  "name" => "Вадим",
  "surname" => "Венедиктов",
  "city" => "Москва"
}

Зачем нужны ассоциативные массивы?

Мы уже знакомы с обычными массивами. Можно сказать, что в обычных массивах «ключом» каждого элемента является его номер. То есть, чтобы вытащить нужный элемент из массива, достаточно знать его номер. Это не всегда удобно, ведь номер элемента в массиве ничего не говорит о назначении этого элемента.

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

Самые простые операции с хэшами в Ruby

Чтобы начать пользоваться хэшами, давайте немного узнаем о том, как с ними работать в ruby:

Создать ассоциативный массив:

associative_array = {
  "key1" => "value1",
  "key2" => "value2"
}

Обратите внимание, что как в качестве ключей, так и в качестве значений мы используем строки.

Получить элемент по ключу key1:

associative_array["key1"]

Добавить в хэш элемент с ключом key3 и значением value3:

associative_array["key3"] = "value3"

Проверить, есть ли в хэше элемент с ключом key1:

associative_array.key?("key1")

Удалить из хэша элемент с ключом key1

associative_array.delete("key1")

Получить массив (обычный) всех ключей хэша:

associative_array.keys

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

Использование ассоциативного массива в Ruby

Давайте откроем нашу программу про героев и злодеев (мы писали её в качестве домашней задачи к уроку 12, можете скопировать её оттуда или взять из материалов к этому уроку) и перепишем её с использованием ассоциативных массивов.

Программа выводит на экран антагониста того героя, которого укажет пользователь:

puts "Врага какого персонажа вы хотите узнать?"

hero = STDIN.gets.chomp

case hero
  when "Бэтмен" 
  then puts "Джокер!"

  when "Шерлок Холмс"
  then puts "Профессор Мориарти"

  when "Буратино"
  then puts "Карабас-Барабас"

  when "Фродо Бэггинс"
  then puts "Саурон"

  when "Моцарт"
  then puts "Сальери"

  else
  puts "Не удалось найти врага"
end

А теперь перепишем эту программу с использованием ассоциативных массивов вместо инструкции case:

puts "Врага какого персонажа вы хотите узнать?"

# Объявили новый ассоциативный массив с парами герой-антигерой
heros_antiheros = {
  "Бэтмен" => "Джокер",
  "Холмс" => "Мориарти",
  "Буратино" => "Карабас-Барабас",
  "Фродо Бэггинс" => "Саурон",
  "Моцарт" => "Сальери"
}

# Получили выбор пользователя
hero = STDIN.gets.chomp

# Выводим антигероя, находя нужный элемент в ассоциативном массиве
if heros_antiheros.has_key?(hero) # проверка на наличие ключа в хэше
  puts "Враг этого героя: #{heros_antiheros[hero]}"
else
  puts "Не удалось найти врага"
end

Обратите внимание, насколько понятнее и проще стал код нашей программы. Все пары герой-антигерой находятся в одном месте и их удобно просматривать, редактировать и добавлять новые.

Что можно хранить в хэшах

Как и в массиве, в ассоциативном массиве в качестве значений могут лежать любые объекты. Ключом также могут быть любые объекты, но мы рекомендуем пока пользоваться только строками (или метками, о которых расскажем позже).

{
  "number" => 42,
  "string" => 'Я строка',
  "file" => File.new('file.txt', 'r')
}

Хэши также, как и массивы, бывают вложенными:

{
  "key" => "value",
  "hash" => {
    "inner_key" => "inner_value"
  }
}

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

Таблица Менделеева

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

Создайте в RubyMine новый проект mendeleev и напишите в нём программу mendeleev.rb:

# Таблица, где символу элемента соответствует фамилия его первооткрывателя
table = {
    'H' => 'Кавендиш',
    'He' => 'Локьер, Жансен, Рамзай',
    'Li' => 'Арфведсон',
    'Be' => 'Воклен',
    'B' => 'Дэви и Гей-Люссак',
    'C' => 'неизвестен',
    'N' => 'Резерфорд',
    'O' => 'Пристли и Шееле',
    'F' => 'Муассан',
    'Ne' => 'Рамзай и Траверс'
}

# продолжите таблицу сами по вкусу
# https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D1%85%D0%B8%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D1%85_%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82%D0%BE%D0%B2

# выводим пользователю что у нас есть — вместе с количеством элементов
puts "У нас всего элементов: #{table.keys.size}"

# сами элементы — массив ключей хэша table
puts table.keys

puts "О каком элементе хотите узнать?"

element = gets.chomp

# проверка — есть ли в хэше элемент, который спросил пользователь
if table.has_key?(element)
  puts "Первооткрывателем этого элемента считается: #{table[element]}"
else
  puts "Извините, про такой элемент мы еще не знаем."
end

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

Символы в Ruby

Метка (по англ. symbol) — это особый тип данных. Это что-то вроде переменной, которая не указывает ни на какой объект, а лишь гордо носит своё собственное имя, которое само по себе и играет роль объекта-индикатора.

ВНИМАНИЕ! Правильное название меток в Руби — Символы

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

{
  key: value
}
{
  :key => value
}

Первый вариант более современный. Символы это очень мощный инструмент и без него современный рубист не представляет себе жизни.

Например, мы использовали их в ассоциативном массиве, когда передавали параметры в метод Pony.mail в программе для отправки почты из уроков про гемы.

Pony.mail({
  :subject => "Привет из программы на руби!", # задаем тему письма
  :body => body,  # задаем содержимое письма, его тело
  :to => send_to, # кому отправить письмо
  :from => my_mail, # наш обратный адрес, от кого письмо
  :via => :smtp,
  :via_options => {
    :address => 'smtp.mail.ru', # это хост, сервер отправки почты
    :port => '465', # порт
    :tls => true,   # если сервер работает в режиме TLS
    :user_name => my_mail, # используем наш адрес почты как логин
    :password => password, # задаем введенный в консоли пароль
    :authentication => :plain  # "обычный" тип авторизации по паролю
  }
})

Программа ожидает заранее определенный набор параметров и поэтому, чтобы это подчеркнуть, лучше в таких случаях использовать именно символы (метки) в качестве ключей хэша для параметров.

В этом уроке мы узнали о важном типе данных «Ассоциативный массив», который реализован в ruby с помощью хэшей, а в следующем уроке займёмся классами: узнаем, что такое наследование и как это помогает строить более сложные приложения.