diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex index 8ef3539a4bc..10d126a8354 100644 --- a/lib/elixir/lib/keyword.ex +++ b/lib/elixir/lib/keyword.ex @@ -563,6 +563,49 @@ defmodule Keyword do end end + @doc """ + Alters the value stored under `key` to `value`, but only + if the entry `key` already exists in the keyword list. + + In the case a value is stored multiple times in the keyword list, + later occurrences are removed. + + ## Examples + + iex> Keyword.replace([a: 1], :b, 2) + [a: 1] + iex> Keyword.replace([a: 1, b: 2, a: 4], :a, 3) + [a: 3, b: 2] + + """ + @spec replace(t, key, value) :: t + def replace(keywords, key, value) when is_list(keywords) and is_atom(key) do + case :lists.keyfind(key, 1, keywords) do + {^key, _} -> [{key, value} | delete(keywords, key)] + false -> keywords + end + end + + @doc """ + Similar to `replace/3`, but will raise a `KeyError` + if the entry `key` does not exist. + + ## Examples + + iex> Keyword.replace!([a: 1, b: 2, a: 4], :a, 3) + [a: 3, b: 2] + iex> Keyword.replace!([a: 1], :b, 2) + ** (KeyError) key :b not found in: [a: 1] + + """ + @spec replace!(t, key, value) :: t + def replace!(keywords, key, value) when is_list(keywords) and is_atom(key) do + case :lists.keyfind(key, 1, keywords) do + {^key, _} -> [{key, value} | delete(keywords, key)] + false -> raise KeyError, key: key, term: keywords + end + end + @doc """ Checks if two keywords are equal. diff --git a/lib/elixir/lib/map.ex b/lib/elixir/lib/map.ex index aa328b3a513..01c69ec209d 100644 --- a/lib/elixir/lib/map.ex +++ b/lib/elixir/lib/map.ex @@ -94,7 +94,7 @@ defmodule Map do @type key :: any @type value :: any - @compile {:inline, fetch: 2, put: 3, delete: 2, has_key?: 2} + @compile {:inline, fetch: 2, put: 3, delete: 2, has_key?: 2, replace: 3} @doc """ Returns all keys from `map`. @@ -271,11 +271,48 @@ defmodule Map do @spec put_new(map, key, value) :: map def put_new(map, key, value) do case has_key?(map, key) do - true -> map + true -> map false -> put(map, key, value) end end + @doc """ + Alters the value stored under `key` to `value`, but only + if the entry `key` already exists in `map`. + + ## Examples + + iex> Map.replace(%{a: 1}, :b, 2) + %{a: 1} + iex> Map.replace(%{a: 1, b: 2}, :a, 3) + %{a: 3, b: 2} + + """ + @spec replace(map, key, value) :: map + def replace(map, key, value) do + case has_key?(map, key) do + true -> :maps.update(key, value, map) + false -> map + end + end + + @doc """ + Similar to `replace/3`, but will raise a `KeyError` + if the key does not exist in the map. + + ## Examples + + 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} + + """ + @spec replace!(map, key, value) :: map + def replace!(map, key, value) do + :maps.update(key, value, map) + end + @doc """ Evaluates `fun` and puts the result under `key` in `map` unless `key` is already present. @@ -300,7 +337,7 @@ defmodule Map do @spec put_new_lazy(map, key, (() -> value)) :: map def put_new_lazy(map, key, fun) when is_function(fun, 0) do case has_key?(map, key) do - true -> map + true -> map false -> put(map, key, fun.()) end end