查看源代码 Map (Elixir v1.16.2)
地图是 Elixir 中的“首选”键值对数据结构。
地图可以使用 %{}
语法创建,键值对可以表示为 key => value
iex> %{}
%{}
iex> %{"one" => :two, 3 => "four"}
%{3 => "four", "one" => :two}
地图中的键值对不遵循任何顺序(这就是为什么上面示例中打印的地图与创建的地图具有不同顺序的原因)。
地图对键类型没有任何限制:任何东西都可以作为地图中的键。作为键值结构,地图不允许重复键。键使用精确相等运算符 (===/2
) 进行比较。如果在地图字面量中定义了冲突键,则最后一个键优先。
当键值对中的键为原子时,可以使用 key: value
简写语法(如许多其他特殊形式一样)。
iex> %{a: 1, b: 2}
%{a: 1, b: 2}
如果你想将简写语法与 =>
混用,则简写语法必须放在最后。
iex> %{"hello" => "world", a: 1, b: 2}
%{:a => 1, :b => 2, "hello" => "world"}
可以使用此模块中的某些函数(例如 Map.get/3
或 Map.fetch/2
)或通过 Access
模块提供的 map[]
语法来访问地图中的键。
iex> map = %{a: 1, b: 2}
iex> Map.fetch(map, :a)
{:ok, 1}
iex> map[:b]
2
iex> map["non_existing_key"]
nil
要访问原子键,也可以使用 map.key
表示法。请注意,如果 map
不包含键 :key
,则 map.key
将引发 KeyError
,而 map[:key]
将返回 nil
。
map = %{foo: "bar", baz: "bong"}
map.foo
#=> "bar"
map.non_existing_key
** (KeyError) key :non_existing_key not found in: %{baz: "bong", foo: "bar"}
避免使用括号
在访问字段时,例如
data.key()
,不要添加括号。如果使用括号,Elixir 将期望data
是代表模块的原子,并尝试调用其中的函数key/0
。
访问键的两种语法揭示了地图的双重性质。 map[key]
语法用于可能具有任何类型任何键的动态创建地图。 map.key
用于包含预定原子键集的地图,这些键预计始终存在。通过 defstruct/1
定义的结构体是此类“静态地图”的一个示例,其中键也可以在编译时进行检查。
地图可以进行模式匹配。当地图位于模式匹配的左侧时,如果右侧的地图包含左侧的键,并且它们的键值与左侧的键值匹配,则将匹配。这意味着空地图与每个地图都匹配。
iex> %{} = %{foo: "bar"}
%{foo: "bar"}
iex> %{a: a} = %{:a => 1, "b" => 2, [:c, :e, :e] => 3}
iex> a
1
但这将引发 MatchError
异常。
%{:c => 3} = %{:a => 1, 2 => :b}
变量可以作为地图键使用,无论是写入地图字面量还是进行匹配时。
iex> n = 1
1
iex> %{n => :one}
%{1 => :one}
iex> %{^n => :one} = %{1 => :one, 2 => :two, 3 => :three}
%{1 => :one, 2 => :two, 3 => :three}
地图还支持特定的更新语法来更新存储在现有键下的值。可以使用原子键语法进行更新
iex> map = %{one: 1, two: 2}
iex> %{map | one: "one"}
%{one: "one", two: 2}
或任何其他键
iex> other_map = %{"three" => 3, "four" => 4}
iex> %{other_map | "three" => "three"}
%{"four" => 4, "three" => "three"}
当更新地图中不存在的键时,将引发 KeyError
异常。
%{map | three: 3}
此模块中需要查找特定键的函数在对数时间内工作。这意味着查找键所需的时间随着地图的增长而增长,但它与地图大小不成正比。与在列表中查找元素相比,它的性能更好,因为列表具有线性时间复杂度。某些函数,例如 keys/1
和 values/1
,在线性时间内运行,因为它们需要访问地图中的每个元素。
地图还实现了 Enumerable
协议,因此许多用于处理地图的函数都可以在 Enum
模块中找到。此外,以下用于地图的函数可以在 Kernel
中找到。
摘要
函数
删除 map
中特定 key
的条目。
从 map
中删除给定的 keys
。
检查两个地图是否相等。
获取给定 map
中特定 key
的值。
获取给定 map
中特定 key
的值,如果 map
不包含 key
,则会报错。
返回一个地图,其中只包含 map
中 fun
返回真值的对。
从给定的 keys
和固定的 value
构建一个地图。
将 struct
转换为地图。
获取 map
中特定 key
的值。
获取 key
的值并更新它,一次完成所有操作。
获取 key
的值并更新它,一次完成所有操作。如果不存在 key
,则会引发异常。
获取 map
中特定 key
的值。
返回给定 key
是否存在于给定 map
中。
将两个地图进行交集运算,返回一个包含公共键的地图。
将两个地图进行交集运算,返回一个包含公共键的地图,并通过函数解决冲突。
返回 map
中的所有键。
将两个地图合并成一个。
将两个地图合并成一个,通过给定的 fun
解决冲突。
返回一个新的空地图。
从 enumerable
创建一个地图。
通过给定的转换函数,从 enumerable
创建一个地图。
删除 map
中与 key
关联的值,并返回该值和更新后的地图。
删除 map
中与 key
关联的值,并返回该值和更新后的地图,如果 key
不存在,则会引发异常。
延迟返回并删除 map
中与 key
关联的值。
将给定的 value
放在 map
中的 key
下。
将给定的 value
放在 key
下,除非 key
条目已存在于 map
中。
评估 fun
,并将结果放在 map
中的 key
下,除非 key
已经存在。
返回不包含 map
中 fun
返回真值的对的地图。
仅当 key
已存在于 map
中时,才将值放在 key
下。
仅当 key
已存在于 map
中时,才将值放在 key
下。
仅当 key
已存在于 map
中时,才使用给定的函数替换 key
下的值。
获取 map
中与给定 keys
对应的所有条目,并将它们提取到一个单独的地图中。
根据给定的函数 fun
将 map
拆分为两个地图。
返回一个新的地图,其中包含 map
中所有键值对,其中键在 keys
中。
将 map
转换为列表。
使用给定的函数更新 map
中的 key
。
使用给定的函数更新 key
。
返回 map
中的所有值。
类型
函数
删除 map
中特定 key
的条目。
如果 key
不存在,则返回不变的 map
。
由编译器内联。
示例
iex> Map.delete(%{a: 1, b: 2}, :a)
%{b: 2}
iex> Map.delete(%{b: 2}, :a)
%{b: 2}
从 map
中删除给定的 keys
。
如果 keys
包含不在 map
中的键,则会简单地忽略这些键。
示例
iex> Map.drop(%{a: 1, b: 2, c: 3}, [:b, :d])
%{a: 1, c: 3}
检查两个地图是否相等。
如果两个地图包含相同的键,并且这些键包含相同的值,则这两个地图被认为是相等的。
请注意,此函数是为了完整性而存在的,因此 Map
和 Keyword
模块提供了类似的 API。实际上,开发人员通常使用 ==/2
或 ===/2
直接比较地图。
示例
iex> Map.equal?(%{a: 1, b: 2}, %{b: 2, a: 1})
true
iex> Map.equal?(%{a: 1, b: 2}, %{b: 1, a: 2})
false
键和值的比较使用 ===/3
进行,这意味着整数与浮点数不等价。
iex> Map.equal?(%{a: 1.0}, %{a: 1})
false
获取给定 map
中特定 key
的值。
如果 map
包含给定的 key
,则其值将以 {:ok, value}
的形式返回。如果 map
不包含 key
,则返回 :error
。
由编译器内联。
示例
iex> Map.fetch(%{a: 1}, :a)
{:ok, 1}
iex> Map.fetch(%{a: 1}, :b)
:error
获取给定 map
中特定 key
的值,如果 map
不包含 key
,则会报错。
如果 map
包含 key
,则返回相应的值。如果 map
不包含 key
,则会引发 KeyError
异常。
由编译器内联。
示例
iex> Map.fetch!(%{a: 1}, :a)
1
返回一个地图,其中只包含 map
中 fun
返回真值的对。
fun
接收映射中每个元素的键和值,作为一个键值对。
另请参阅 reject/2
,它会丢弃函数返回真值的所有元素。
性能注意事项
如果您发现自己在管道中多次调用
Map.filter/2
和Map.reject/2
,使用Enum.map/2
和Enum.filter/2
并最终使用Map.new/1
转换为映射可能更有效率。
示例
iex> Map.filter(%{one: 1, two: 2, three: 3}, fn {_key, val} -> rem(val, 2) == 1 end)
%{one: 1, three: 3}
从给定的 keys
和固定的 value
构建一个地图。
示例
iex> Map.from_keys([1, 2, 3], :number)
%{1 => :number, 2 => :number, 3 => :number}
将 struct
转换为地图。
它接受结构体模块或结构体本身,并简单地从给定的结构体或从给定模块生成的新的结构体中移除 __struct__
字段。
示例
defmodule User do
defstruct [:name]
end
Map.from_struct(User)
#=> %{name: nil}
Map.from_struct(%User{name: "john"})
#=> %{name: "john"}
获取 map
中特定 key
的值。
如果 key
出现在 map
中,则返回其值 value
。否则,返回 default
。
如果未提供 default
,则使用 nil
。
示例
iex> Map.get(%{}, :a)
nil
iex> Map.get(%{a: 1}, :a)
1
iex> Map.get(%{a: 1}, :b)
nil
iex> Map.get(%{a: 1}, :b, 3)
3
iex> Map.get(%{a: nil}, :a, 1)
nil
@spec get_and_update( map(), key(), (value() | nil -> {current_value, new_value :: value()} | :pop) ) :: {current_value, new_map :: map()} when current_value: value()
获取 key
的值并更新它,一次完成所有操作。
fun
使用 map
中 key
下的当前值(如果 key
不存在于 map
中,则为 nil
)调用,并且必须返回一个包含两个元素的元组:当前值(检索到的值,可以在返回之前对其进行操作)以及要存储在结果新映射中 key
下的新值。 fun
也可以返回 :pop
,这意味着当前值将从 map
中删除并返回(使此函数的行为类似于 Map.pop(map, key)
)。
返回的值是一个包含两个元素的元组,其中包含 fun
返回的当前值和一个新的映射,其中包含在 key
下更新的值。
示例
iex> Map.get_and_update(%{a: 1}, :a, fn current_value ->
...> {current_value, "new value!"}
...> end)
{1, %{a: "new value!"}}
iex> Map.get_and_update(%{a: 1}, :b, fn current_value ->
...> {current_value, "new value!"}
...> end)
{nil, %{a: 1, b: "new value!"}}
iex> Map.get_and_update(%{a: 1}, :a, fn _ -> :pop end)
{1, %{}}
iex> Map.get_and_update(%{a: 1}, :b, fn _ -> :pop end)
{nil, %{a: 1}}
@spec get_and_update!( map(), key(), (value() -> {current_value, new_value :: value()} | :pop) ) :: {current_value, map()} when current_value: value()
获取 key
的值并更新它,一次完成所有操作。如果不存在 key
,则会引发异常。
行为与 get_and_update/3
完全相同,但如果 key
不存在于 map
中,则会引发 KeyError
异常。
示例
iex> Map.get_and_update!(%{a: 1}, :a, fn current_value ->
...> {current_value, "new value!"}
...> end)
{1, %{a: "new value!"}}
iex> Map.get_and_update!(%{a: 1}, :b, fn current_value ->
...> {current_value, "new value!"}
...> end)
** (KeyError) key :b not found in: %{a: 1}
iex> Map.get_and_update!(%{a: 1}, :a, fn _ ->
...> :pop
...> end)
{1, %{}}
获取 map
中特定 key
的值。
如果 key
出现在 map
中,则返回其值 value
。否则,将评估 fun
并返回其结果。
如果默认值计算起来非常昂贵,或者通常难以设置和拆卸,这很有用。
示例
iex> map = %{a: 1}
iex> fun = fn ->
...> # some expensive operation here
...> 13
...> end
iex> Map.get_lazy(map, :a, fun)
1
iex> Map.get_lazy(map, :b, fun)
13
返回给定 key
是否存在于给定 map
中。
由编译器内联。
示例
iex> Map.has_key?(%{a: 1}, :a)
true
iex> Map.has_key?(%{a: 1}, :b)
false
将两个地图进行交集运算,返回一个包含公共键的地图。
返回映射中的值是 map2
中相交键的值。
由编译器内联。
示例
iex> Map.intersect(%{a: 1, b: 2}, %{b: "b", c: "c"})
%{b: "b"}
将两个地图进行交集运算,返回一个包含公共键的地图,并通过函数解决冲突。
当存在重复键时,将调用给定的函数;其参数是 key
(重复键)、value1
(map1
中 key
的值)和 value2
(map2
中 key
的值)。fun
返回的值用作结果映射中 key
下的值。
示例
iex> Map.intersect(%{a: 1, b: 2}, %{b: 2, c: 3}, fn _k, v1, v2 ->
...> v1 + v2
...> end)
%{b: 4}
返回 map
中的所有键。
由编译器内联。
示例
Map.keys(%{a: 1, b: 2})
[:a, :b]
将两个地图合并成一个。
将 map2
中的所有键添加到 map1
,覆盖任何现有的键(即,map2
中的键“优先于”map1
中的键)。
如果您有一个结构体,并且想要将一组键合并到结构体中,请不要使用此函数,因为它会将右侧的所有键合并到结构体中,即使该键不是结构体的一部分。而是使用 struct/2
。
由编译器内联。
示例
iex> Map.merge(%{a: 1, b: 2}, %{a: 3, d: 4})
%{a: 3, b: 2, d: 4}
将两个地图合并成一个,通过给定的 fun
解决冲突。
将 map2
中的所有键添加到 map1
。当存在重复键时,将调用给定的函数;其参数是 key
(重复键)、value1
(map1
中 key
的值)和 value2
(map2
中 key
的值)。fun
返回的值用作结果映射中 key
下的值。
示例
iex> Map.merge(%{a: 1, b: 2}, %{a: 3, d: 4}, fn _k, v1, v2 ->
...> v1 + v2
...> end)
%{a: 4, b: 2, d: 4}
@spec new() :: map()
返回一个新的空地图。
示例
iex> Map.new()
%{}
@spec new(Enumerable.t()) :: map()
从 enumerable
创建一个地图。
重复的键将被移除;最新的键将保留。
示例
iex> Map.new([{:b, 1}, {:a, 2}])
%{a: 2, b: 1}
iex> Map.new(a: 1, a: 2, a: 3)
%{a: 3}
@spec new(Enumerable.t(), (term() -> {key(), value()})) :: map()
通过给定的转换函数,从 enumerable
创建一个地图。
重复的键将被移除;最新的键将保留。
示例
iex> Map.new([:a, :b], fn x -> {x, x} end)
%{a: :a, b: :b}
iex> Map.new(%{a: 2, b: 3, c: 4}, fn {key, val} -> {key, val * 2} end)
%{a: 4, b: 6, c: 8}
@spec pop(map(), key(), default) :: {value(), updated_map :: map()} | {default, map()} when default: value()
删除 map
中与 key
关联的值,并返回该值和更新后的地图。
如果 key
出现在 map
中,则返回 {value, updated_map}
,其中 value
是键的值,updated_map
是从 map
中删除 key
的结果。如果 key
不存在于 map
中,则返回 {default, map}
。
示例
iex> Map.pop(%{a: 1}, :a)
{1, %{}}
iex> Map.pop(%{a: 1}, :b)
{nil, %{a: 1}}
iex> Map.pop(%{a: 1}, :b, 3)
{3, %{a: 1}}
删除 map
中与 key
关联的值,并返回该值和更新后的地图,如果 key
不存在,则会引发异常。
行为与 pop/3
相同,但如果 key
不存在于 map
中,则会引发异常。
示例
iex> Map.pop!(%{a: 1}, :a)
{1, %{}}
iex> Map.pop!(%{a: 1, b: 2}, :a)
{1, %{b: 2}}
iex> Map.pop!(%{a: 1}, :b)
** (KeyError) key :b not found in: %{a: 1}
延迟返回并删除 map
中与 key
关联的值。
如果 key
出现在 map
中,则返回 {value, new_map}
,其中 value
是键的值,new_map
是从 map
中删除 key
的结果。如果 key
不存在于 map
中,则返回 {fun_result, map}
,其中 fun_result
是应用 fun
的结果。
如果默认值计算起来非常昂贵,或者通常难以设置和拆卸,这很有用。
示例
iex> map = %{a: 1}
iex> fun = fn ->
...> # some expensive operation here
...> 13
...> end
iex> Map.pop_lazy(map, :a, fun)
{1, %{}}
iex> Map.pop_lazy(map, :b, fun)
{13, %{a: 1}}
将给定的 value
放在 map
中的 key
下。
由编译器内联。
示例
iex> Map.put(%{a: 1}, :b, 2)
%{a: 1, b: 2}
iex> Map.put(%{a: 1, b: 2}, :a, 3)
%{a: 3, b: 2}
将给定的 value
放在 key
下,除非 key
条目已存在于 map
中。
示例
iex> Map.put_new(%{a: 1}, :b, 2)
%{a: 1, b: 2}
iex> Map.put_new(%{a: 1, b: 2}, :a, 3)
%{a: 1, b: 2}
评估 fun
,并将结果放在 map
中的 key
下,除非 key
已经存在。
如果您只想在 key
不存在的情况下计算要放在 key
下的值,例如,当该值计算起来非常昂贵,或者通常难以设置和拆卸时,此函数非常有用。
示例
iex> map = %{a: 1}
iex> fun = fn ->
...> # some expensive operation here
...> 3
...> end
iex> Map.put_new_lazy(map, :a, fun)
%{a: 1}
iex> Map.put_new_lazy(map, :b, fun)
%{a: 1, b: 3}
返回不包含 map
中 fun
返回真值的对的地图。
另请参阅 filter/2
。
示例
iex> Map.reject(%{one: 1, two: 2, three: 3}, fn {_key, val} -> rem(val, 2) == 1 end)
%{two: 2}
仅当 key
已存在于 map
中时,才将值放在 key
下。
示例
iex> Map.replace(%{a: 1, b: 2}, :a, 3)
%{a: 3, b: 2}
iex> Map.replace(%{a: 1}, :b, 2)
%{a: 1}
仅当 key
已存在于 map
中时,才将值放在 key
下。
如果 key
不存在于 map
中,则会引发 KeyError
异常。
由编译器内联。
示例
iex> Map.replace!(%{a: 1, b: 2}, :a, 3)
%{a: 3, b: 2}
iex> Map.replace!(%{a: 1}, :b, 2)
** (KeyError) key :b not found in: %{a: 1}
仅当 key
已存在于 map
中时,才使用给定的函数替换 key
下的值。
与 replace/3
相比,当计算该值很昂贵时,这很有用。
如果 key
不存在,则返回原始映射,保持不变。
示例
iex> Map.replace_lazy(%{a: 1, b: 2}, :a, fn v -> v * 4 end)
%{a: 4, b: 2}
iex> Map.replace_lazy(%{a: 1, b: 2}, :c, fn v -> v * 4 end)
%{a: 1, b: 2}
获取 map
中与给定 keys
对应的所有条目,并将它们提取到一个单独的地图中。
返回一个包含新映射和旧映射的元组,其中旧映射已删除键。
在 map
中没有条目对应的键将被忽略。
示例
iex> Map.split(%{a: 1, b: 2, c: 3}, [:a, :c, :e])
{%{a: 1, c: 3}, %{b: 2}}
根据给定的函数 fun
将 map
拆分为两个地图。
fun
接收 map
中的每个 {key, value}
对作为其唯一参数。返回一个元组,其中第一个映射包含 map
中所有应用 fun
返回真值的元素,第二个映射包含所有应用 fun
返回假值(false
或 nil
)的元素。
示例
iex> Map.split_with(%{a: 1, b: 2, c: 3, d: 4}, fn {_k, v} -> rem(v, 2) == 0 end)
{%{b: 2, d: 4}, %{a: 1, c: 3}}
iex> Map.split_with(%{a: 1, b: -2, c: 1, d: -3}, fn {k, _v} -> k in [:b, :d] end)
{%{b: -2, d: -3}, %{a: 1, c: 1}}
iex> Map.split_with(%{a: 1, b: -2, c: 1, d: -3}, fn {_k, v} -> v > 50 end)
{%{}, %{a: 1, b: -2, c: 1, d: -3}}
iex> Map.split_with(%{}, fn {_k, v} -> v > 50 end)
{%{}, %{}}
返回一个新的地图,其中包含 map
中所有键值对,其中键在 keys
中。
如果 keys
包含不在 map
中的键,则会简单地忽略这些键。
示例
iex> Map.take(%{a: 1, b: 2, c: 3}, [:a, :c, :e])
%{a: 1, c: 3}
将 map
转换为列表。
映射中的每个键值对都转换为结果列表中的一个包含两个元素的元组 {key, value}
。
由编译器内联。
示例
iex> Map.to_list(%{a: 1})
[a: 1]
iex> Map.to_list(%{1 => 2})
[{1, 2}]
@spec update( map(), key(), default :: value(), (existing_value :: value() -> new_value :: value()) ) :: map()
使用给定的函数更新 map
中的 key
。
如果 key
出现在 map
中,则将现有值传递给 fun
,其结果用作 key
的更新值。如果 key
不存在于 map
中,则将 default
插入为 key
的值。默认值不会通过更新函数传递。
示例
iex> Map.update(%{a: 1}, :a, 13, fn existing_value -> existing_value * 2 end)
%{a: 2}
iex> Map.update(%{a: 1}, :b, 11, fn existing_value -> existing_value * 2 end)
%{a: 1, b: 11}
使用给定的函数更新 key
。
如果 key
存在于 map
中,则将现有值传递给 fun
,其结果将用作 key
的更新值。如果 key
不存在于 map
中,则会引发一个 KeyError
异常。
示例
iex> Map.update!(%{a: 1}, :a, &(&1 * 2))
%{a: 2}
iex> Map.update!(%{a: 1}, :b, &(&1 * 2))
** (KeyError) key :b not found in: %{a: 1}
返回 map
中的所有值。
由编译器内联。
示例
Map.values(%{a: 1, b: 2})
[1, 2]