檢視原始碼 列舉秘笈
快速參考 Enum
模組,一個用於處理集合(稱為可列舉)的模組。下列大多數範例使用下列資料結構
cart = [
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]
有些範例使用 字串 =~ 部分
算子,檢查左邊的字串是否包含右邊的部分。
謂詞
any?(enum, fun)
iex> Enum.any?(cart, & &1.fruit == "orange")
true
iex> Enum.any?(cart, & &1.fruit == "pear")
false
any?
搭配空集合時總是為 false
iex> Enum.any?([], & &1.fruit == "orange")
false
all?(enum, fun)
iex> Enum.all?(cart, & &1.count > 0)
true
iex> Enum.all?(cart, & &1.count > 1)
false
all?
搭配空集合時總是為 true
iex> Enum.all?([], & &1.count > 0)
true
member?(enum, value)
iex> Enum.member?(cart, %{fruit: "apple", count: 3})
true
iex> Enum.member?(cart, :something_else)
false
item in enum
等於 Enum.member?(enum, item)
iex> %{fruit: "apple", count: 3} in cart
true
iex> :something_else in cart
false
empty?(enum)
iex> Enum.empty?(cart)
false
iex> Enum.empty?([])
true
過濾
filter(enum, fun)
iex> Enum.filter(cart, &(&1.fruit =~ "o"))
[%{fruit: "orange", count: 6}]
iex> Enum.filter(cart, &(&1.fruit =~ "e"))
[
%{fruit: "apple", count: 3},
%{fruit: "orange", count: 6}
]
reject(enum, fun)
iex> Enum.reject(cart, &(&1.fruit =~ "o"))
[
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
]
理解
過濾也可以使用理解式完成
iex> for item <- cart, item.fruit =~ "e" do
...> item
...> end
[
%{fruit: "apple", count: 3},
%{fruit: "orange", count: 6}
]
理解式中的模式比對也扮演過濾器的角色
iex> for %{count: 1, fruit: fruit} <- cart do
...> fruit
...> end
["banana"]
對應
map(enum, fun)
iex> Enum.map(cart, & &1.fruit)
["apple", "banana", "orange"]
iex> Enum.map(cart, fn item ->
...> %{item | count: item.count + 10}
...> end)
[
%{fruit: "apple", count: 13},
%{fruit: "banana", count: 11},
%{fruit: "orange", count: 16}
]
map_every(enum, nth, fun)
iex> Enum.map_every(cart, 2, fn item ->
...> %{item | count: item.count + 10}
...> end)
[
%{fruit: "apple", count: 13},
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 16}
]
理解式
對應也可以使用理解式完成
iex> for item <- cart do
...> item.fruit
...> end
["apple", "banana", "orange"]
你也可以同時過濾和對應
iex> for item <- cart, item.fruit =~ "e" do
...> item.fruit
...> end
["apple", "orange"]
副作用
each(enum, fun)
iex> Enum.each(cart, &IO.puts(&1.fruit))
apple
banana
orange
:ok
Enum.each/2
專門用於副作用。
累積
reduce(enum, acc, fun)
iex> Enum.reduce(cart, 0, fn item, acc ->
...> item.count + acc
...> end)
10
map_reduce(enum, acc, fun)
iex> Enum.map_reduce(cart, 0, fn item, acc ->
...> {item.fruit, item.count + acc}
...> end)
{["apple", "banana", "orange"], 10}
scan(enum, acc, fun)
iex> Enum.scan(cart, 0, fn item, acc ->
...> item.count + acc
...> end)
[3, 4, 10]
reduce_while(enum, acc, fun)
iex> Enum.reduce_while(cart, 0, fn item, acc ->
...> if item.fruit == "orange" do
...> {:halt, acc}
...> else
...> {:cont, item.count + acc}
...> end
...> end)
4
理解式
縮減也可以使用理解式完成
iex> for item <- cart, reduce: 0 do
...> acc -> item.count + acc
...> end
10
你也可以同時過濾和縮減
iex> for item <- cart, item.fruit =~ "e", reduce: 0 do
...> acc -> item.count + acc
...> end
9
聚合
count(enum)
iex> Enum.count(cart)
3
請參閱 Enum.count_until/2
以計算直到限制。
frequencies(enum)
iex> Enum.frequencies(["apple", "banana", "orange", "apple"])
%{"apple" => 2, "banana" => 1, "orange" => 1}
frequencies_by(enum, key_fun)
水果最後一個字母的頻率
iex> Enum.frequencies_by(cart, &String.last(&1.fruit))
%{"a" => 1, "e" => 2}
count(enum, fun)
iex> Enum.count(cart, &(&1.fruit =~ "e"))
2
iex> Enum.count(cart, &(&1.fruit =~ "y"))
0
請參閱 Enum.count_until/3
以使用函數計算直到限制。
sum(enum)
iex> cart |> Enum.map(& &1.count) |> Enum.sum()
10
product(enum)
iex> cart |> Enum.map(& &1.count) |> Enum.product()
18
排序
sort(enum, sorter \\ :asc)
iex> cart |> Enum.map(& &1.fruit) |> Enum.sort()
["apple", "banana", "orange"]
iex> cart |> Enum.map(& &1.fruit) |> Enum.sort(:desc)
["orange", "banana", "apple"]
排序結構時,請使用 Enum.sort/2
搭配模組作為排序器。
sort_by(enum, mapper, sorter \\ :asc)
iex> Enum.sort_by(cart, & &1.count)
[
%{fruit: "banana", count: 1},
%{fruit: "apple", count: 3},
%{fruit: "orange", count: 6}
]
iex> Enum.sort_by(cart, & &1.count, :desc)
[
%{fruit: "orange", count: 6},
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
]
當排序的依據為結構時,請使用 Enum.sort_by/3
搭配模組作為排序器。
min(enum)
iex> cart |> Enum.map(& &1.count) |> Enum.min()
1
比較結構時,請使用 Enum.min/2
搭配模組作為排序器。
min_by(enum, mapper)
iex> Enum.min_by(cart, & &1.count)
%{fruit: "banana", count: 1}
比較結構時,請使用 Enum.min_by/3
搭配模組作為排序器。
max(enum)
iex> cart |> Enum.map(& &1.count) |> Enum.max()
6
比較結構時,使用 Enum.max/2
,並使用模組作為排序器。
max_by(enum, mapper)
iex> Enum.max_by(cart, & &1.count)
%{fruit: "orange", count: 6}
比較結構時,使用 Enum.max_by/3
,並使用模組作為排序器。
串接和扁平化
concat(enums)
iex> Enum.concat([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
concat(left, right)
iex> Enum.concat([1, 2, 3], [4, 5, 6])
[1, 2, 3, 4, 5, 6]
flat_map(enum, fun)
iex> Enum.flat_map(cart, fn item ->
...> List.duplicate(item.fruit, item.count)
...> end)
["apple", "apple", "apple", "banana", "orange",
"orange", "orange", "orange", "orange", "orange"]
flat_map_reduce(enum, acc, fun)
iex> Enum.flat_map_reduce(cart, 0, fn item, acc ->
...> list = List.duplicate(item.fruit, item.count)
...> acc = acc + item.count
...> {list, acc}
...> end)
{["apple", "apple", "apple", "banana", "orange",
"orange", "orange", "orange", "orange", "orange"], 10}
理解
扁平化也可以使用理解來完成
iex> for item <- cart,
...> fruit <- List.duplicate(item.fruit, item.count) do
...> fruit
...> end
["apple", "apple", "apple", "banana", "orange",
"orange", "orange", "orange", "orange", "orange"]
轉換
into(enum, collectable)
iex> pairs = [{"apple", 3}, {"banana", 1}, {"orange", 6}]
iex> Enum.into(pairs, %{})
%{"apple" => 3, "banana" => 1, "orange" => 6}
into(enum, collectable, transform)
iex> Enum.into(cart, %{}, fn item ->
...> {item.fruit, item.count}
...> end)
%{"apple" => 3, "banana" => 1, "orange" => 6}
to_list(enum)
iex> Enum.to_list(1..5)
[1, 2, 3, 4, 5]
理解
轉換也可以使用理解來完成
iex> for item <- cart, into: %{} do
...> {item.fruit, item.count}
...> end
%{"apple" => 3, "banana" => 1, "orange" => 6}
重複和唯一
dedup(enum)
dedup
僅移除連續的重複項
iex> Enum.dedup([1, 2, 2, 3, 3, 3, 1, 2, 3])
[1, 2, 3, 1, 2, 3]
dedup_by(enum, fun)
移除給定屬性的連續項目
iex> Enum.dedup_by(cart, & &1.fruit =~ "a")
[%{fruit: "apple", count: 3}]
iex> Enum.dedup_by(cart, & &1.count < 5)
[
%{fruit: "apple", count: 3},
%{fruit: "orange", count: 6}
]
uniq(enum)
uniq
適用於整個集合
iex> Enum.uniq([1, 2, 2, 3, 3, 3, 1, 2, 3])
[1, 2, 3]
理解也支援 uniq: true
選項。
uniq_by(enum, fun)
取得水果最後一個字母為唯一的項目
iex> Enum.uniq_by(cart, &String.last(&1.fruit))
[
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
]
索引
at(enum, index, default \\ nil)
iex> Enum.at(cart, 0)
%{fruit: "apple", count: 3}
iex> Enum.at(cart, 10)
nil
iex> Enum.at(cart, 10, :none)
:none
不建議在迴圈中透過索引來存取清單。
fetch(enum, index)
iex> Enum.fetch(cart, 0)
{:ok, %{fruit: "apple", count: 3}}
iex> Enum.fetch(cart, 10)
:error
fetch!(enum, index)
iex> Enum.fetch!(cart, 0)
%{fruit: "apple", count: 3}
iex> Enum.fetch!(cart, 10)
** (Enum.OutOfBoundsError) out of bounds error
with_index(enum)
iex> Enum.with_index(cart)
[
{%{fruit: "apple", count: 3}, 0},
{%{fruit: "banana", count: 1}, 1},
{%{fruit: "orange", count: 6}, 2}
]
with_index(enum, fun)
iex> Enum.with_index(cart, fn item, index ->
...> {item.fruit, index}
...> end)
[
{"apple", 0},
{"banana", 1},
{"orange", 2}
]
尋找
find(enum, default \\ nil, fun)
iex> Enum.find(cart, &(&1.fruit =~ "o"))
%{fruit: "orange", count: 6}
iex> Enum.find(cart, &(&1.fruit =~ "y"))
nil
iex> Enum.find(cart, :none, &(&1.fruit =~ "y"))
:none
find_index(enum, fun)
iex> Enum.find_index(cart, &(&1.fruit =~ "o"))
2
iex> Enum.find_index(cart, &(&1.fruit =~ "y"))
nil
find_value(enum, default \\ nil, fun)
iex> Enum.find_value(cart, fn item ->
...> if item.count == 1, do: item.fruit, else: nil
...> end)
"banana"
iex> Enum.find_value(cart, :none, fn item ->
...> if item.count == 100, do: item.fruit, else: nil
...> end)
:none
分組
group_by(enum, key_fun)
依水果的最後一個字母分組
iex> Enum.group_by(cart, &String.last(&1.fruit))
%{
"a" => [%{fruit: "banana", count: 1}],
"e" => [
%{fruit: "apple", count: 3},
%{fruit: "orange", count: 6}
]
}
group_by(enum, key_fun, value_fun)
依水果的最後一個字母分組,並自訂值
iex> Enum.group_by(cart, &String.last(&1.fruit), & &1.fruit)
%{
"a" => ["banana"],
"e" => ["apple", "orange"]
}
合併與插入
join(enum, joiner \\ "")
iex> Enum.join(["apple", "banana", "orange"], ", ")
"apple, banana, orange"
map_join(enum, joiner \\ "", mapper)
iex> Enum.map_join(cart, ", ", & &1.fruit)
"apple, banana, orange"
intersperse(enum, separator \\ "")
iex> Enum.intersperse(["apple", "banana", "orange"], ", ")
["apple", ", ", "banana", ", ", "orange"]
map_intersperse(enum, separator \\ "", mapper)
iex> Enum.map_intersperse(cart, ", ", & &1.fruit)
["apple", ", ", "banana", ", ", "orange"]
切片
slice(enum, index_range)
iex> Enum.slice(cart, 0..1)
[
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
]
負值範圍從後方開始計算
iex> Enum.slice(cart, -2..-1)
[
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]
slice(enum, start_index, amount)
iex> Enum.slice(cart, 1, 2)
[
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]
slide(enum, range_or_single_index, insertion_index)
fruits = ["apple", "banana", "grape", "orange", "pear"]
iex> Enum.slide(fruits, 2, 0)
["grape", "apple", "banana", "orange", "pear"]
iex> Enum.slide(fruits, 2, 4)
["apple", "banana", "orange", "pear", "grape", ]
iex> Enum.slide(fruits, 1..3, 0)
["banana", "grape", "orange", "apple", "pear"]
iex> Enum.slide(fruits, 1..3, 4)
["banana", "pear", "grape", "orange", "apple"]
反轉
reverse(enum)
iex> Enum.reverse(cart)
[
%{fruit: "orange", count: 6},
%{fruit: "banana", count: 1},
%{fruit: "apple", count: 3}
]
reverse(enum, tail)
iex> Enum.reverse(cart, [:this_will_be, :the_tail])
[
%{fruit: "orange", count: 6},
%{fruit: "banana", count: 1},
%{fruit: "apple", count: 3},
:this_will_be,
:the_tail
]
reverse_slice(enum, start_index, count)
iex> Enum.reverse_slice(cart, 1, 2)
[
%{fruit: "apple", count: 3},
%{fruit: "orange", count: 6},
%{fruit: "banana", count: 1}
]
分割
split(enum, amount)
iex> Enum.split(cart, 1)
{[%{fruit: "apple", count: 3}],
[
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]}
負值索引從後方開始計算
iex> Enum.split(cart, -1)
{[
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
],
[%{fruit: "orange", count: 6}]}
split_while(enum, fun)
停止分割,只要為 false
iex> Enum.split_while(cart, &(&1.fruit =~ "e"))
{[%{fruit: "apple", count: 3}],
[
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]}
split_with(enum, fun)
分割整個集合
iex> Enum.split_with(cart, &(&1.fruit =~ "e"))
{[
%{fruit: "apple", count: 3},
%{fruit: "orange", count: 6}
],
[%{fruit: "banana", count: 1}]}
分割 (drop 和 take)
drop(enum, amount)
iex> Enum.drop(cart, 1)
[
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]
負值索引從後方開始計算
iex> Enum.drop(cart, -1)
[
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
]
drop_every(enum, nth)
iex> Enum.drop_every(cart, 2)
[%{fruit: "banana", count: 1}]
drop_while(enum, fun)
iex> Enum.drop_while(cart, &(&1.fruit =~ "e"))
[
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]
take(enum, amount)
iex> Enum.take(cart, 1)
[%{fruit: "apple", count: 3}]
負值索引從後方開始計算
iex> Enum.take(cart, -1)
[%{fruit: "orange", count: 6}]
take_every(enum, nth)
iex> Enum.take_every(cart, 2)
[
%{fruit: "apple", count: 3},
%{fruit: "orange", count: 6}
]
take_while(enum, fun)
iex> Enum.take_while(cart, &(&1.fruit =~ "e"))
[%{fruit: "apple", count: 3}]
隨機
random(enum)
每次呼叫結果會有所不同
iex> Enum.random(cart)
%{fruit: "orange", count: 6}
take_random(enum, count)
每次呼叫結果會有所不同
iex> Enum.take_random(cart, 2)
[
%{fruit: "orange", count: 6},
%{fruit: "apple", count: 3}
]
shuffle(enum)
每次呼叫結果會有所不同
iex> Enum.shuffle(cart)
[
%{fruit: "orange", count: 6},
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
]
分塊
chunk_by(enum, fun)
iex> Enum.chunk_by(cart, &String.length(&1.fruit))
[
[%{fruit: "apple", count: 3}],
[
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]
]
chunk_every(enum, count)
iex> Enum.chunk_every(cart, 2)
[
[
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
],
[%{fruit: "orange", count: 6}]
]
chunk_every(enum, count, step, leftover \\ [])
iex> Enum.chunk_every(cart, 2, 2, [:elements, :to_complete])
[
[
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
],
[
%{fruit: "orange", count: 6},
:elements
]
]
iex> Enum.chunk_every(cart, 2, 1, :discard)
[
[
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1}
],
[
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]
]
請參閱 Enum.chunk_while/4
以進行自訂分塊。
封裝
zip(enum1, enum2)
iex> fruits = ["apple", "banana", "orange"]
iex> counts = [3, 1, 6]
iex> Enum.zip(fruits, counts)
[{"apple", 3}, {"banana", 1}, {"orange", 6}]
請參閱 Enum.zip/1
以一次封裝多個集合。
zip_with(enum1, enum2, fun)
iex> fruits = ["apple", "banana", "orange"]
iex> counts = [3, 1, 6]
iex> Enum.zip_with(fruits, counts, fn fruit, count ->
...> %{fruit: fruit, count: count}
...> end)
[
%{fruit: "apple", count: 3},
%{fruit: "banana", count: 1},
%{fruit: "orange", count: 6}
]
請參閱 Enum.zip_with/2
以一次封裝多個集合。
zip_reduce(left, right, acc, fun)
iex> fruits = ["apple", "banana", "orange"]
iex> counts = [3, 1, 6]
iex> Enum.zip_reduce(fruits, counts, 0, fn fruit, count, acc ->
...> price = if fruit =~ "e", do: count * 2, else: count
...> acc + price
...> end)
19
請參閱 Enum.zip_reduce/3
以一次封裝多個集合。
unzip(list)
iex> cart |> Enum.map(&{&1.fruit, &1.count}) |> Enum.unzip()
{["apple", "banana", "orange"], [3, 1, 6]}