Skip to content

Commit d25ce48

Browse files
committed
Bring back filter/reject on Map and Keyword
They are already being used in projects, which means it is too late for a deprecation. The docs instead discuss when to use them.
1 parent 66b4ab1 commit d25ce48

File tree

2 files changed

+119
-34
lines changed

2 files changed

+119
-34
lines changed

lib/elixir/lib/keyword.ex

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,16 +1317,61 @@ defmodule Keyword do
13171317
length(keywords)
13181318
end
13191319

1320-
@doc false
1321-
@deprecated "Use Enum.filter/2 instead"
1322-
def filter(keywords, fun) when is_list(keywords) do
1323-
Enum.filter(keywords, fun)
1320+
@doc """
1321+
Returns a keyword list containing only the entries from `keywords`
1322+
for which the function `fun` returns a truthy value.
1323+
1324+
See also `reject/2` which discards all entries where the function
1325+
returns a truthy value.
1326+
1327+
## Examples
1328+
1329+
iex> Keyword.filter([one: 1, two: 2, three: 3], fn {_key, val} -> rem(val, 2) == 1 end)
1330+
[one: 1, three: 3]
1331+
1332+
"""
1333+
@doc since: "1.13.0"
1334+
@spec filter(t, ({key, value} -> as_boolean(term))) :: t
1335+
def filter(keywords, fun) when is_list(keywords) and is_function(fun, 1) do
1336+
do_filter(keywords, fun)
13241337
end
13251338

1326-
@doc false
1327-
@deprecated "Use Keyword.new/2 instead"
1328-
def reject(keywords, fun) when is_list(keywords) do
1329-
Enum.reject(keywords, fun)
1339+
defp do_filter([], _fun), do: []
1340+
1341+
defp do_filter([{_, _} = entry | entries], fun) do
1342+
if fun.(entry) do
1343+
[entry | do_filter(entries, fun)]
1344+
else
1345+
do_filter(entries, fun)
1346+
end
1347+
end
1348+
1349+
@doc """
1350+
Returns a keyword list excluding the entries from `keywords`
1351+
for which the function `fun` returns a truthy value.
1352+
1353+
See also `filter/2`.
1354+
1355+
## Examples
1356+
1357+
iex> Keyword.reject([one: 1, two: 2, three: 3], fn {_key, val} -> rem(val, 2) == 1 end)
1358+
[two: 2]
1359+
1360+
"""
1361+
@doc since: "1.13.0"
1362+
@spec reject(t, ({key, value} -> as_boolean(term))) :: t
1363+
def reject(keywords, fun) when is_list(keywords) and is_function(fun, 1) do
1364+
do_reject(keywords, fun)
1365+
end
1366+
1367+
defp do_reject([], _fun), do: []
1368+
1369+
defp do_reject([{_, _} = entry | entries], fun) do
1370+
if fun.(entry) do
1371+
do_reject(entries, fun)
1372+
else
1373+
[entry | do_reject(entries, fun)]
1374+
end
13301375
end
13311376

13321377
@doc false

lib/elixir/lib/map.ex

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -214,24 +214,7 @@ defmodule Map do
214214
215215
"""
216216
@spec new(Enumerable.t(), (term -> {key, value})) :: map
217-
def new(enumerable, transform)
218-
def new(%_{} = enumerable, transform), do: new_from_enum(enumerable, transform)
219-
def new(%{} = map, transform), do: new_from_map(map, transform)
220-
def new(enumerable, transform), do: new_from_enum(enumerable, transform)
221-
222-
defp new_from_map(map, transform) when is_function(transform, 1) do
223-
iter = :maps.iterator(map)
224-
next = :maps.next(iter)
225-
:maps.from_list(do_map(next, transform))
226-
end
227-
228-
defp do_map(:none, _fun), do: []
229-
230-
defp do_map({key, value, iter}, transform) do
231-
[transform.({key, value}) | do_map(:maps.next(iter), transform)]
232-
end
233-
234-
defp new_from_enum(enumerable, transform) when is_function(transform, 1) do
217+
def new(enumerable, transform) when is_function(transform, 1) do
235218
enumerable
236219
|> Enum.map(transform)
237220
|> :maps.from_list()
@@ -989,16 +972,73 @@ defmodule Map do
989972
map_size(map)
990973
end
991974

992-
@doc false
993-
@deprecated "Use Enum.filter/2 instead"
994-
def filter(map, fun) when is_map(map) do
995-
Enum.filter(map, fun) |> :maps.from_list()
975+
@doc """
976+
Returns a map containing only those pairs from `map`
977+
for which `fun` returns a truthy value.
978+
979+
`fun` receives the key and value of each of the
980+
elements in the map as a key-value pair.
981+
982+
See also `reject/2` which discards all elements where the
983+
function returns a truthy value.
984+
985+
> Note: if you find yourself doing multiple calls to `Map.filter/2`
986+
> and `Map.reject/2` in a pipeline, it is likely more efficient
987+
> to use `Enum.map/2` and `Enum.filter/2` instead and convert to
988+
> a map at the end using `Map.new/1`.
989+
990+
## Examples
991+
992+
iex> Map.filter(%{one: 1, two: 2, three: 3}, fn {_key, val} -> rem(val, 2) == 1 end)
993+
%{one: 1, three: 3}
994+
995+
"""
996+
@doc since: "1.13.0"
997+
@spec filter(map, ({key, value} -> as_boolean(term))) :: map
998+
def filter(map, fun) when is_map(map) and is_function(fun, 1) do
999+
iter = :maps.iterator(map)
1000+
next = :maps.next(iter)
1001+
:maps.from_list(do_filter(next, fun))
9961002
end
9971003

998-
@doc false
999-
@deprecated "Use Enum.reject/2 instead"
1000-
def reject(map, fun) when is_map(map) do
1001-
Enum.reject(map, fun) |> :maps.from_list()
1004+
defp do_filter(:none, _fun), do: []
1005+
1006+
defp do_filter({key, value, iter}, fun) do
1007+
if fun.({key, value}) do
1008+
[{key, value} | do_filter(:maps.next(iter), fun)]
1009+
else
1010+
do_filter(:maps.next(iter), fun)
1011+
end
1012+
end
1013+
1014+
@doc """
1015+
Returns map excluding the pairs from `map` for which `fun` returns
1016+
a truthy value.
1017+
1018+
See also `filter/2`.
1019+
1020+
## Examples
1021+
1022+
iex> Map.reject(%{one: 1, two: 2, three: 3}, fn {_key, val} -> rem(val, 2) == 1 end)
1023+
%{two: 2}
1024+
1025+
"""
1026+
@doc since: "1.13.0"
1027+
@spec reject(map, ({key, value} -> as_boolean(term))) :: map
1028+
def reject(map, fun) when is_map(map) and is_function(fun, 1) do
1029+
iter = :maps.iterator(map)
1030+
next = :maps.next(iter)
1031+
:maps.from_list(do_reject(next, fun))
1032+
end
1033+
1034+
defp do_reject(:none, _fun), do: []
1035+
1036+
defp do_reject({key, value, iter}, fun) do
1037+
if fun.({key, value}) do
1038+
do_reject(:maps.next(iter), fun)
1039+
else
1040+
[{key, value} | do_reject(:maps.next(iter), fun)]
1041+
end
10021042
end
10031043

10041044
@doc false

0 commit comments

Comments
 (0)