檢視原始碼 Map (Elixir v1.16.2)
Map 是 Elixir 中的「首選」鍵值資料結構。
Map 可使用 %{}
語法建立,而鍵值對可表示為 key => value
iex> %{}
%{}
iex> %{"one" => :two, 3 => "four"}
%{3 => "four", "one" => :two}
Map 中的鍵值對不遵循任何順序(這就是上面範例中列印的 Map 順序與建立的 Map 順序不同的原因)。
Map 對鍵類型沒有任何限制:任何東西都可以是 Map 中的鍵。作為鍵值結構,Map 不允許重複的鍵。鍵使用完全相等運算子 (===/2
) 進行比較。如果在 Map 文字中定義了衝突的鍵,則最後一個會優先。
當鍵值對中的鍵為原子時,可以使用 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 中的鍵可透過此模組中的一些函式(例如 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 是否相等。
從指定的 map
中取得特定 key
的值。
從指定的 map
中取得特定 key
的值,如果 map
不包含 key
,則會產生錯誤。
傳回一個 map,其中只包含 map
中 fun
傳回真值的那些配對。
根據指定的 keys
和固定的 value
建立一個 map。
將 struct
轉換為 map。
取得 map
中特定 key
的值。
一次取得 key
的值並更新它。
一次取得 key
的值並更新它。如果沒有 key
,則會引發例外。
取得 map
中特定 key
的值。
傳回指定的 key
是否存在於指定的 map
中。
取兩個 map 的交集,傳回一個包含共用 key 的 map。
取兩個 map 的交集,傳回一個包含共用 key 的 map,並透過函式解決衝突。
傳回 map
中的所有 key。
將兩個 map 合併成一個。
將兩個 map 合併成一個,透過指定的 fun
解決衝突。
傳回一個新的空 map。
從 enumerable
建立一個 map。
透過指定的轉換函式從 enumerable
建立一個 map。
移除 map
中與 key
關聯的值,並傳回該值和更新後的 map。
移除 map
中與 key
關聯的值,並傳回該值和更新後的 map,如果 key
不存在,則會引發例外。
延遲傳回並移除 map
中與 key
關聯的值。
將給定的 value
放入 map
中 key
的下方。
將給定的 value
放入 key
的下方,除非項目 key
已存在於 map
中。
評估 fun
並將結果放入 map
中 key
的下方,除非 key
已存在。
傳回 map
中排除 fun
傳回真值對應項目的 map。
僅在 key
已存在於 map
中時,將值放入 key
的下方。
僅在 key
已存在於 map
中時,將值放入 key
的下方。
僅在 key
已存在於 map
中時,使用給定的函數取代 key
下方的值。
取得 map
中對應給定 keys
的所有項目,並將其萃取到一個獨立的 map 中。
根據給定的函數 fun
,將 map
分割成兩個 map。
傳回一個新的 map,其中包含 map
中所有 key 為 keys
的 key-value 對。
將 map
轉換為 list。
使用給定的函數更新 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
中的 key,則會忽略這些 key。
範例
iex> Map.drop(%{a: 1, b: 2, c: 3}, [:b, :d])
%{a: 1, c: 3}
檢查兩個 map 是否相等。
如果兩個 map 包含相同的 key,且這些 key 包含相同的值,則視為相等。
請注意此功能的存在是為了完整性,因此 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,其中只包含 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
建立一個 map。
範例
iex> Map.from_keys([1, 2, 3], :number)
%{1 => :number, 2 => :number, 3 => :number}
將 struct
轉換為 map。
它接受結構模組或結構本身,並從給定的結構或從給定模組產生的新結構中移除 __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
取兩個 map 的交集,傳回一個包含共用 key 的 map。
傳回的映射中,其值為 map2
中相交鍵的值。
由編譯器內嵌。
範例
iex> Map.intersect(%{a: 1, b: 2}, %{b: "b", c: "c"})
%{b: "b"}
取兩個 map 的交集,傳回一個包含共用 key 的 map,並透過函式解決衝突。
當有重複鍵時,將呼叫所提供的函式;其參數為 key
(重複的鍵)、value1
(key
在 map1
中的值)和 value2
(key
在 map2
中的值)。fun
傳回的值將用作結果映射中 key
下的值。
範例
iex> Map.intersect(%{a: 1, b: 2}, %{b: 2, c: 3}, fn _k, v1, v2 ->
...> v1 + v2
...> end)
%{b: 4}
傳回 map
中的所有 key。
由編譯器內嵌。
範例
Map.keys(%{a: 1, b: 2})
[:a, :b]
將兩個 map 合併成一個。
map2
中的所有鍵都將加入 map1
,並覆寫任何現有的鍵(亦即 map2
中的鍵「優先」於 map1
中的鍵)。
如果您有一個結構,並且想要將一組鍵合併到結構中,請勿使用此函式,因為它會將右側的所有鍵合併到結構中,即使該鍵不是結構的一部分。請改用 struct/2
。
由編譯器內嵌。
範例
iex> Map.merge(%{a: 1, b: 2}, %{a: 3, d: 4})
%{a: 3, b: 2, d: 4}
將兩個 map 合併成一個,透過指定的 fun
解決衝突。
map2
中的所有鍵都將加入 map1
。當有重複鍵時,將呼叫所提供的函式;其參數為 key
(重複的鍵)、value1
(key
在 map1
中的值)和 value2
(key
在 map2
中的值)。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()
傳回一個新的空 map。
範例
iex> Map.new()
%{}
@spec new(Enumerable.t()) :: map()
從 enumerable
建立一個 map。
重複的鍵會被移除;最新的鍵會保留。
範例
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
建立一個 map。
重複的鍵會被移除;最新的鍵會保留。
範例
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
關聯的值,並傳回該值和更新後的 map。
如果 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
關聯的值,並傳回該值和更新後的 map,如果 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
傳回真值對應項目的 map。
另請參閱 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 中。
傳回一個元組,其中包含新的映射和已移除金鑰的舊映射。
對於在 map
中沒有條目的金鑰,將會忽略。
範例
iex> Map.split(%{a: 1, b: 2, c: 3}, [:a, :c, :e])
{%{a: 1, c: 3}, %{b: 2}}
根據給定的函數 fun
,將 map
分割成兩個 map。
fun
會將 map
中的每個 {key, value}
對作為其唯一參數。傳回一個元組,其中第一個 map 包含 map
中所有套用 fun
後傳回真值元素,而第二個 map 則包含所有套用 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,其中包含 map
中所有 key 為 keys
的 key-value 對。
如果 keys
包含不存在於 map
中的 key,則會忽略這些 key。
範例
iex> Map.take(%{a: 1, b: 2, c: 3}, [:a, :c, :e])
%{a: 1, c: 3}
將 map
轉換為 list。
map 中的每個 key-value 對會在結果清單中轉換成一個二元組 {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]