Skip to content

Commit 1ca8086

Browse files
authored
Add Map.replace_lazy/3 and Keyword.replace_lazy/3 (#11474)
1 parent b9b4971 commit 1ca8086

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

lib/elixir/lib/keyword.ex

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,40 @@ defmodule Keyword do
867867
raise KeyError, key: key, term: original
868868
end
869869

870+
@doc """
871+
Replaces the value under `key` using the given function only if
872+
`key` already exists in `keywords`.
873+
874+
In comparison to `replace/3`, this can be useful when it's expensive to calculate the value.
875+
876+
If `key` does not exist, the original keyword list is returned unchanged.
877+
878+
## Examples
879+
880+
iex> Keyword.replace_lazy([{:a, 1}, {:b, 2}], :a, fn v -> v * 4 end)
881+
[{:a, 4}, {:b, 2}]
882+
883+
iex> Keyword.replace_lazy([{:a, 1}, {:b, 2}], :c, fn v -> v * 4 end)
884+
[{:a, 1}, {:b, 2}]
885+
886+
"""
887+
@doc since: "1.14.0"
888+
@spec replace_lazy(t, key, (existing_value :: value -> new_value :: value)) :: t
889+
def replace_lazy(keywords, key, fun)
890+
when is_list(keywords) and is_atom(key) and is_function(fun, 1) do
891+
do_replace_lazy(keywords, key, fun)
892+
end
893+
894+
defp do_replace_lazy([{key, value} | keywords], key, fun) do
895+
[{key, fun.(value)} | delete(keywords, key)]
896+
end
897+
898+
defp do_replace_lazy([{_, _} = e | keywords], key, fun) do
899+
[e | replace_lazy(keywords, key, fun)]
900+
end
901+
902+
defp do_replace_lazy([], _key, _value), do: []
903+
870904
@doc """
871905
Checks if two keywords are equal.
872906

lib/elixir/lib/map.ex

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,32 @@ defmodule Map do
371371
:maps.update(key, value, map)
372372
end
373373

374+
@doc """
375+
Replaces the value under `key` using the given function only if
376+
`key` already exists in `map`.
377+
378+
In comparison to `replace/3`, this can be useful when it's expensive to calculate the value.
379+
380+
If `key` does not exist, the original map is returned unchanged.
381+
382+
## Examples
383+
384+
iex> Map.replace_lazy(%{a: 1, b: 2}, :a, fn v -> v * 4 end)
385+
%{a: 4, b: 2}
386+
387+
iex> Map.replace_lazy(%{a: 1, b: 2}, :c, fn v -> v * 4 end)
388+
%{a: 1, b: 2}
389+
390+
"""
391+
@doc since: "1.14.0"
392+
@spec replace_lazy(map, key, (existing_value :: value -> new_value :: value)) :: map
393+
def replace_lazy(map, key, fun) when is_map(map) and is_function(fun, 1) do
394+
case map do
395+
%{^key => val} -> %{map | key => fun.(val)}
396+
%{} -> map
397+
end
398+
end
399+
374400
@doc """
375401
Evaluates `fun` and puts the result under `key`
376402
in `map` unless `key` is already present.

0 commit comments

Comments
 (0)