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

Elixir: Словари

Словарь (Map) хранит пары ключ-значение. Это еще одна динамическая структура данных в которую можно добавлять и удалять элементы.

Словарь создается с помощью конструкции %{key1 => value1, key2 => value2}. Для обращения по ключу используются квадратные скобки:

my_map = %{"a" => 1, "b" => 2}
my_map["a"] # 1
my_map["b"] # 2

Часто в качестве ключей используют атомы:

other_map = %{:a => 1, :b => 2}
other_map[:a] # 1
other_map[:b] # 2

Для ключей атомов (и только в этом случае) можно использовать синтаксический сахар:

other_map = %{a: 1, b: 2}
other_map.a # 1
other_map.b # 2

Обращение через квадратные скобки и обращение через точку работают по-разному в случае, когда ключ отсутствует в словаре:

other_map[:c] # nil
other_map.c  # ** (KeyError) key :c not found in: %{a: 1, b: 2}

Как видим, в первом случае возвращается значение nil, а во втором случае генерируется исключение.

Функция Map.get работает так же, как обращение через квадратные скобки. Но она позволяет указать дефолтное значение для отсутствующего ключа:

Map.get(other_map, :a) # 1
Map.get(other_map, :c) # nil
Map.get(other_map, :c, 42) # 42

Для добавления нового ключа или для изменения значения существующего ключа используется функция Map.put:

Map.put(other_map, :c, 42) # %{a: 1, b: 2, c: 42}
Map.put(other_map, :a, 42) # %{a: 42, b: 2}

Словарь, как и все остальные значения в Эликсир, является иммутабельным. Поэтому функция Map.put возвращает новый словарь, а старый остается неизменным:

new_map = Map.put(other_map, :c, 42)
IO.puts(inspect(new_map)) # => %{a: 1, b: 2, c: 42}
IO.puts(inspect(other_map)) # => %{a: 1, b: 2}

Для изменения значения существующего ключа есть синтаксический сахар:

%{other_map | :a => 42} # %{a: 42, b: 2}
%{other_map | :a => 42 , :b => 43} # %{a: 42, b: 43}

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

А добавить новый ключ этот синтаксис не позволяет:

%{other_map | :c => 42} # ** (KeyError) key :c not found in: %{a: 1, b: 2}

Для удаления ключа используется функция Map.delete. Она тоже возвращает новый словарь, а старый остается неизменным:

Map.delete(other_map, :a) # %{b: 2}

Задание

Реализуйте функцию keys_sum, которая принимает словарь и два ключа, извлекает значения по этим ключам, и возвращает сумму значений. Если ключа в словаре нет, то соответствующее значение не учитывается.

Реализуйте функцию keys_product, которая принимает словарь и два ключа, извлекает значения по этим ключам, и возвращает произведение значений. Если ключа в словаре нет, то соответствующее значение не учитывается.

Реализуйте функцию copy_key, которая принимает два словаря, ключ, и значение по умолчанию. По ключу нужно извлечь значение из первого словаря и вставить во второй словарь. Если в первом словаре нет такого ключа, то во второй словарь нужно вставить значение по умолчанию. Если во втором словаре уже есть такой ключ, то его значение меняется.

map = %{a: 1, b: 2, c: 42}
Solution.keys_sum(map, :a, :b)
# => 3
Solution.keys_sum(map, :a, :c)
# => 43
Solution.keys_sum(map, :c, :b)
# => 44

map = %{one: 1, five: 5, ten: 10}
Solution.keys_product(map, :one, :five)
# => 5
Solution.keys_product(map, :five, :ten)
# => 50
Solution.keys_product(map, :two, :ten)
# => 10

map1 = %{a: 1, b: 2}
map2 = %{c: 3, d: 4}

Solution.copy_key(map1, map2, :a, 42)
# => %{c: 3, d: 4, a: 1}
Solution.copy_key(map1, map2, :b, 42)
# => %{c: 3, d: 4, b: 2}

Solution.copy_key(map2, map1, :d, 42)
# => %{a: 1, b: 2, d: 4}
Solution.copy_key(map2, map1, :e, 42)
# => %{a: 1, b: 2, e: 42}
Упражнение не проходит проверку — что делать? 😶

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

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

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

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

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

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

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

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

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

Полезное


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