Bootstrap

Maps - это key-value структуры данных.

Maps - это key-value структуры данных.

Maps - это key-value структуры данных.

Неизменяемость и создание нового `Map` в Elixir

В Elixir структуры данных, такие как Map, являются неизменяемыми. Это означает, что вы не можете изменить их "на месте". Вместо этого, когда вы пытаетесь изменить Map, вы создаете новую структуру данных, а старая остаётся нетронутой. Это важное свойство, которое помогает избежать побочных эффектов в функциональном программировании.

Разбор вашего примера

Ваш пример:

☯

Terminal:

⌕

≡

✕
map = %{a: 1, b: 2}
new_map = %{map | a: 3}
  • Исходный map: Вы имеете map с двумя ключами :a и :b, где :a имеет значение 1, а :b — 2.
  • Создание нового Map: Когда вы делаете %{map | a: 3}, вы создаете новый Map, в котором ключ :a обновляется на 3. Новый Map будет выглядеть так: %{a: 3, b: 2}.

Где старая версия `Map`?

  • Старая версия (или исходный map), где :a равно 1, всё еще существует в памяти и доступна. Вы можете ссылаться на неё с помощью переменной map, которая была определена раньше: ``elixir IO.inspect(map) # Выведет %{a: 1, b: 2} IO.inspect(new_map) # Выведет %{a: 3, b: 2} ``

Преимущества неизменяемости

  1. Безопасность: Поскольку данные неизменяемы, вы исключаете возможность случайного изменения значений в коде.
  2. Простота отладки: Легче отслеживать состояние данных в программе, так как вы всегда знаете, что изменения создают новые копии.
  3. Поддержка параллелизма: Неизменяемые структуры обеспечивают безопасный доступ из разных потоков или процессов, поскольку данные не будут изменяться "на лету".

Заключение

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


ключи строки

m = %{"a" => 42, "b" => 100}

кдючи атомы

m2 = %{:a => 42, :b =>100}

iex(1)> m = %{"a" => 42, "b" => 100}

%{"a" => 42, "b" => 100}

iex(2)> m["b"]

100

iex(3)> m["c"]

nil

iex(4)> m2 = %{:a => 42, :b =>100}

%{a: 42, b: 100}

iex(12)> %{:a => 42, :b =>100} = %{a: 42, b: 100}

%{a: 42, b: 100}

iex(5)> m[:a]

nil

iex(6)> m2[:a]

42

iex(7)> m2.a

42

iex(8)> m2.c

** (KeyError) key :c not found in: %{a: 42, b: 100}

iex(8)> Map.get(m,"a")

42

iex(9)> Map.get(m2,"a")

nil

iex(10)> Map.get(m2,"a",:default)

:default

iex(11)> Map.fetch(m2, :b)

{:ok, 100}

iex(13)> Map.fetch(m2, :c)

:error

с восклицательным знаком

iex(14)> Map.fetch!(m2, :c)

** (KeyError) key :c not found in: %{a: 42, b: 100}

(stdlib 4.2) :maps.get(:c, %{a: 42, b: 100})

iex(14)> Map.fetch!(m2, :b)

100

имутабельность данных не меняет мапу а создаёт новый мам

iex(15)> Map.put(m, :c, 500)

%{:c => 500, "a" => 42, "b" => 100}

iex(16)> m

%{"a" => 42, "b" => 100}

iex(17)> m3=Map.put(m, :c, 500)

%{:c => 500, "a" => 42, "b" => 100}

iex(18)> m

%{"a" => 42, "b" => 100}

iex(19)> m3

%{:c => 500, "a" => 42, "b" => 100}

существующем ключе меняется значение

iex(20)> m3=Map.put(m3, :c, 5050)

%{:c => 5050, "a" => 42, "b" => 100}

iex(21)> m3

%{:c => 5050, "a" => 42, "b" => 100}

изменить значение существующих ключей:

%{"a" => 42, "b" => 100}

iex(2)> %{ma: 43, b: 108}

** (KeyError) key :a not found in: %{"a" => 42, "b" => 100}

(stdlib 4.2) :maps.update(:a, 43, %{"a" => 42, "b" => 100})

(stdlib 4.2) erl_eval.erl:309: anonymous fn/2 in :erl_eval.expr/6

(stdlib 4.2) lists.erl:1350: :lists.foldl/3

iex(2)> %{m"a" 43, "b" 108}

** (SyntaxError) iex:2:11: syntax error before: "43"

2%{m"a" 43, "b" 108}
^
iex(2)> %{m"a" => 43, "b" => 108}

%{"a" => 43, "b" => 108}

iex(3)> m

%{"a" => 42, "b" => 100}

Отлично ?

Ниже — полная таблица всех функций для работы с Map в Elixir, с короткими описаниями и примерами, в том же формате, что и предыдущие шпаргалки (для String и List).

Актуально для Elixir 1.17 / OTP 27.


? Модуль `Map`

> Map — основной модуль для работы с ассоциативными коллекциями (%{ключ => значение}),

> где ключи могут быть любыми сравнимыми типами (обычно — атомы, числа или строки).


? Создание и базовые операции

ФункцияНазначениеПример
%{}Литерал map%{a: 1, b: 2}
Map.new/0Пустая картаMap.new() #=> %{}
Map.new/1Из перечисленияMap.new([{:a,1},{:b,2}]) #=> %{a: 1, b: 2}
Map.new/2Преобразование с функциейMap.new([1,2,3], fn x -> {x, x*x} end) #=> %{1=>1,2=>4,3=>9}
%{map | key => val}Обновление значения (ключ должен существовать)m=%{a:1}; %{m | a:2} #=> %{a:2}
Map.put/3Добавить/заменить паруMap.put(%{a:1}, :b, 2) #=> %{a:1,b:2}
Map.put_new/3Добавить только если ключа нетMap.put_new(%{a:1}, :a, 9) #=> %{a:1}
Map.put_new_lazy/3Вычислить значение ленивоMap.put_new_lazy(%{}, :x, fn -> 42 end)
Map.update/4Обновить значение с функциейMap.update(%{a:1}, :a, 0, &(&1+1)) #=> %{a:2}
Map.update!/3То же, но без значения по умолчаниюMap.update!(%{a:1}, :a, &(&1+1)) #=> %{a:2}
Map.replace!/3Заменить, если ключ существуетMap.replace!(%{a:1}, :a, 9) #=> %{a:9}
Map.replace/3Заменить, не создавая новый ключMap.replace(%{a:1}, :b, 2) #=> %{a:1}

? Чтение и доступ

ФункцияНазначениеПример
Map.get/2Получить значениеMap.get(%{a:1}, :a) #=> 1
Map.get/3С дефолтным значениемMap.get(%{}, :x, 0) #=> 0
Map.fetch/2Возвращает {:ok, val} или :errorMap.fetch(%{a:1}, :a) #=> {:ok,1}
Map.fetch!/2Как get, но бросает ошибкуMap.fetch!(%{a:1}, :b) #=> KeyError
Map.get_lazy/3Значение по умолчанию вычисляется функциейMap.get_lazy(%{}, :x, fn -> 99 end)
Map.pop/2Извлечь и удалить ключMap.pop(%{a:1,b:2}, :a) #=> {1, %{b:2}}
Map.pop/3С дефолтным значениемMap.pop(%{}, :x, 0) #=> {0, %{}}
Map.pop_lazy/3Дефолт вычисляется функциейMap.pop_lazy(%{}, :x, fn -> 5 end)
Map.get_and_update/3Получить и изменить через функциюMap.get_and_update(%{a:1}, :a, fn val -> {val, val+1} end)
Map.get_and_update!/3Как выше, но без дефолтаMap.get_and_update!(%{a:1}, :a, fn v -> {v, v*2} end)

? Удаление ключей

ФункцияНазначениеПример
Map.delete/2Удалить по ключуMap.delete(%{a:1,b:2}, :a) #=> %{b:2}
Map.drop/2Удалить несколько ключейMap.drop(%{a:1,b:2,c:3}, [:a,:b]) #=> %{c:3}
Map.take/2Оставить только указанные ключиMap.take(%{a:1,b:2}, [:a]) #=> %{a:1}
Map.pop/2Извлечь и удалитьMap.pop(%{x:1}, :x) #=> {1, %{}}

? Информация и проверка

ФункцияНазначениеПример
Map.has_key?/2Проверяет наличие ключаMap.has_key?(%{a:1}, :a) #=> true
Map.equal?/2Проверяет равенство двух картMap.equal?(%{a:1}, %{a:1}) #=> true
map_size/1 (Kernel)Кол-во парmap_size(%{a:1,b:2}) #=> 2

? Извлечение ключей и значений

ФункцияНазначениеПример
Map.keys/1Список ключейMap.keys(%{a: 1,b: 2}) #=> [:a,:b]
Map.values/1Список значенийMap.values(%{a: 1,b: 2}) #=> [1,2]
Map.to_list/1Преобразовать в keyword-парыMap.to_list(%{a: 1,b: 2}) #=> [a:1,b:2]
Enum.into/2Преобразовать коллекцию в mapEnum.into([{:a , 1}], %{}) #=> %{a:1}

? Объединение и слияние

ФункцияНазначениеПример
Map.merge/2Объединяет два mapMap.merge(%{a:1}, %{b:2}) #=> %{a:1,b:2}
Map.merge/3С пользовательской функциейMap.merge(%{a:1}, %{a:10}, fn _k,v1,v2 -> v1+v2 end) #=> %{a:11}
Enum.into/2Слияние коллекции в mapEnum.into([{:x,9}], %{a:1}) #=> %{a:1,x:9}

? Преобразование

ФункцияНазначениеПример
Map.from_struct/1Структура → mapMap.from_struct(%URI{path: "/foo"})
Map.to_list/1Map → список парMap.to_list(%{a:1,b:2}) #=> [a:1,b:2]
Map.new/1Список пар → mapMap.new([{:a,1},{:b,2}])

? Структуры (`struct`)

ФункцияНазначениеПример
Map.from_struct/1Преобразует структуру в mapMap.from_struct(%URI{path: "/foo"})
Map.merge/2Объединяет два map или структурыMap.merge(%URI{}, %{path: "/"})
Kernel.struct/2Создаёт структуру из mapstruct(User, %{name: "Tom"})
Kernel.struct!/2То же, но требует все ключиstruct!(User, %{name: "Tom", age: 20})

? Сопоставление с образцом

КонструкцияНазначениеПример
%{key: val} = mapДеструктуризация%{a: x} = %{a: 10, b: 20} #=> x=10
%{key => value}Динамические ключиkey=:a; %{^key => v} = %{a: 1} #=> v=1
Map.fetch!/2Проверка при pattern matchMap.fetch!(map, :x)

? Из `Enum` и `Kernel` (часто применяются к map)

ФункцияНазначениеПример
Enum.map/2Преобразовать парыEnum.map(%{a:1,b:2}, fn {k,v} -> {k,v*2} end)
Enum.each/2ИтерацияEnum.each(%{a:1}, fn {k,v} -> IO.puts("#{k}=#{v}") end)
Enum.filter/2Фильтрация по условиюEnum.filter(%{a:1,b:2}, fn {_,v} -> v>1 end)
Enum.into/2Преобразовать в mapEnum.into([{:x,1}], %{})
Map.merge/2Объединить два mapMap.merge(%{a:1}, %{b:2})

? Пример частых комбинаций

ЦельПример
Получить или задать значение по ключуm = %{x: 1}; m[:x] #=> 1; %{m | x: 2}
Объединить с дефолтамиMap.merge(%{a:1}, %{a:99,b:2}, fn _,v1,_ -> v1 end)
Удалить несколько ключейMap.drop(m, [:a,:b])
Оставить только нужныеMap.take(m, [:id,:name])
Проверить ключMap.has_key?(m, :id)
Преобразовать значенияEnum.map(m, fn {k,v} -> {k, v*2} end) | > Enum.into(%{})

⚙️ Полезные из `Kernel` (работают с map напрямую)

Функция/операторНазначениеПример
m[:key]Получение значения (или nil)%{a:1}[:a] #=> 1
%{m | key: value}Обновить существующий ключ%{a:1,b:2} | > Map.put(:c,3)
map_size/1Количество парmap_size(%{a:1,b:2}) #=> 2
is_map/1Проверка типаis_map(%{}) #=> true

Примеры использования функции `Map.put` в Elixir

Функция Map.put позволяет добавлять или обновлять значения в структуре данных типа Map. Она принимает три аргумента:

  1. Исходный Map.
  2. Ключ, под которым нужно сохранить значение.
  3. Значение, которое нужно сохранить.

После выполнения Map.put возвращается новая версия Map, содержащая обновленные данные.


Примеры использования

1. Добавление нового элемента

☯

Terminal:

⌕

≡

✕
map = %{a: 1, b: 2}
updated_map = Map.put(map, :c, 3)

# Результат
# updated_map будет %{a: 1, b: 2, c: 3}

2. Обновление существующего элемента

☯

Terminal:

⌕

≡

✕
map = %{a: 1, b: 2}
updated_map = Map.put(map, :b, 3)

# Результат
# updated_map будет %{a: 1, b: 3}

3. Использование с различными типами ключей и значений

☯

Terminal:

⌕

≡

✕
map = %{"name" => "John", age: 30}
updated_map = Map.put(map, :email, "john@example.com")

# Результат
# updated_map будет %{"name" => "John", age: 30, email: "john@example.com"}

4. Вложенные Maps

☯

Terminal:

⌕

≡

✕
map = %{user: %{name: "Alice", age: 25}}
updated_map = Map.put(map, :user, Map.put(map.user, :age, 26))

# Результат
# updated_map будет %{user: %{name: "Alice", age: 26}}

5. Использование в функциях

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

☯

Terminal:

⌕

≡

✕
defmodule User do
  def update_email(user, new_email) do
	Map.put(user, :email, new_email)
  end
end

user = %{name: "Alice", email: "old_email@example.com"}
updated_user = User.update_email(user, "new_email@example.com")

# Результат
# updated_user будет %{name: "Alice", email: "new_email@example.com"}

Копирование материалов разрешается только с указанием автора Roman Sakhno и индексируемой прямой ссылкой на сайт (http://itdid.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/sahroman.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/sahroman.

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

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:

    Она выглядит вот так: Как настроить свой компьютер

  2. Текстовая ссылка:

    Она выглядит вот так: Как настроить свой компьютер

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):

Комментарии (0):

Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.