Бесплатный курс по Ruby. Зарегистрируйтесь для отслеживания прогресса →

Ruby: Массивы как множества

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

Представьте себе задачу поиска взаимных друзей пользователей. Для формирования такого списка на уровне кода, нужно сравнить два массива (мои друзья и друзья друга) и найти пересечение, то есть общих друзей. В данном случае массивы с друзьями — это множества, а операция поиска общих элементов – пересечение (intersection).

Пересечение на Ruby выглядит так:

friends1 = ['igor', 'anna', 'nina', 'sergey']
friends2 = ['igor', 'petya', 'inna', 'anna']

# Выглядит как побитовое И, но это пересечение
friends1 & friends2 # ["igor", "anna"]
# или
# friends1.intersection(friends2)

Такой оператор очень удобен своей естественностью. Сразу понятно, о чём идёт речь. Как и в большинстве других операторов в Ruby, мы имеем дело с вызовами методов:

friends.&(friends2)

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

friends = ['anna', 'ivan']
friends1 & friends2 & friends # ["anna"]

У множеств и массивов с точки зрения математики есть одно принципиальное отличие, о котором нужно помнить. Во множествах каждый элемент представлен ровно один раз (то есть все элементы — уникальны), в то время как в массивах такого ограничения не существует. Операции над множествами рассматривают массивы не как массивы, а именно как множества. Они удаляют дубли в результирующем массиве:

[1, 3, 4] & [1, 3, 3, 8] # [1, 3]

Объединение

Множество, объединяющее в себе элементы исходных множеств.

 [1, 3, 4] | [1, 3, 3, 8]
 # [1, 3, 4, 8]

Дополнение

Множество, состоящее из элементов первого множества, за минусом элементов, совпадающих со вторым множеством. Или по простому — это разница между двумя множествами.

# 4 – единственный элемент из первого множества, которого нет во втором
[1, 3, 4] - [1, 3, 3, 8] # [4]

Задание

Иногда в программировании возникает задача поиска разницы между двумя наборами данных, такими как объекты. Например, при поиске различий в json-файлах. Для этого даже существуют специальные сервисы, например, http://www.jsondiff.com/ (попробуйте нажать на ссылку sample data и кнопку Compare).

Реализуйте функцию gen_diff(), которая сравнивает два объекта и возвращает результат сравнения в виде объекта. Ключами результирующего объекта будут все ключи из двух входящих объектов, а значением — строка с описанием отличий: added, deleted, changed или unchanged.

Возможные значения:

  • added — ключ отсутствовал в первом объекте, но был добавлен во второй
  • deleted — ключ был в первом объекте, но отсутствует во втором
  • changed — ключ присутствовал и в первом, и во втором объектах, но значения отличаются
  • unchanged — ключ присутствовал с одинаковыми значениями и в первом, и во втором объектах
gen_diff(
  { one: 'eon', two: 'two', four: true },
  { two: 'own', zero: 4, four: true }
);
# {
#   one: 'deleted',
#   two: 'changed',
#   four: 'unchanged',
#   zero: 'added'
# }
Упражнение не проходит проверку — что делать? 😶

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

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

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

Мой код отличается от решения учителя 🤔

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.

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

Прочитал урок — ничего не понятно 🙄

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.

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

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются https://github.com/hexlet-basics
Если вы столкнулись с трудностями и не знаете, что делать, задайте вопрос в нашем большом и дружном сообществе