前言
Map 是 elixir 中经常会用到的一种 key-value 数据结构。
它有着 使用简单,顺序无关,可以用任何值做 key,可模式匹配 等优点。
历史
Map 引入 erlang 的时间并不长,是在 OTP17 中新增的数据结构。
可参看这篇文章
常用操作
elixir 对 Map 的支持非常好,在 Kernel 模块中就提供许多操作 Map 的函数。
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> pop_in(users, ["john", :age])
{27, %{"john" => %{}, "meg" => %{age: 23}}}
iex> put_in(users, ["john", :age], 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}
iex> update_in(users, ["john", :age], &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}
iex> get_in(users, ["john", :age])
27
iex> get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}
此外,还可以这样访问 Map 中的某个值(使用第二种方法时 key 必须为 atom):
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> users["john"][:age]
27
iex> map1 = %{a: %{b: 1}, c: 2}
iex> map1.a.b
1
还可以这样修改一个 map (key 必须为已经存在的,且必须为 atom):
iex> map2 = %{a: 1, b: 2}
iex> %{map2 | a: 3}
%{a: 3, b: 2}
struct
为了给 Map 增添编译时的结构检查, 以及设置默认值的功能,elixir 提供了 struct。
struct 即是带有 :__struct__
key 的 map。要使用struct, 必须先定义:
iex> defmodule User do
...> defstruct name: 123
...> end
iex> user = %User{}
%User{name: 123}
iex> user.__struct__
User
iex> %{__struct__: User, name: 456}
%User{name: 456}
注意,一个 struct 模块需要实现了 Access Behaviour 之后,才可以使用put_in
等函数。
MapSet
有时,我们会需要这样一种数据结构:一个无序列表,里面的所有元素都是唯一的。
elixir 在 Map 的基础上实现了 MapSet。
它的原理很简单:
iex> MapSet.new([123, :a, "hello"])
#MapSet<[123, :a, "hello"]>
iex> %MapSet{map: %{123 => [], :a => [], "hello" => []}}
#MapSet<[123, :a, "hello"]>
Range
Range 也是一种 struct。
iex> %Range{first: 1, last: 100}
1..100
其他
模式匹配时,
%{}
可以匹配任意的 Map,包括 struct 和 MapSet。两个 Map 之间比较大小时,首先比较 key 的个数,然后是 key 的大小,
最后是 value 的大小。比较 key 的大小时,浮点数永远大于整数:
iex> a = %{1.1 => []}
%{1.1 => []}
iex> b = %{2 => []}
%{2 => []}
iex> a > b
true