Бесплатный курс по 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, которая принимает два словаря, ключ, и значение по умолчанию. По ключу нужно извлечь значение из первого словаря и вставить во второй словарь. Если в первом словаре нет такого ключа, то во второй словарь нужно вставить значение по умолчанию. Если во втором словаре уже есть такой ключ, то его значение меняется.

defmodule Solution do

  def keys_sum(map, key1, key2) do
    # TODO реализация
  end

  def keys_product(map, key1, key2) do
    # TODO реализация
  end

  def copy_key(from_map, to_map, key, default_value) do
    # TODO реализация
  end

end
Упражнение не проходит проверку — что делать? 😶

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

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

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

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

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

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

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

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

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

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