Skip to content

Commit 25771dd

Browse files
authored
Remove duplicates in Keyword.get_and_update/3 (elixir-lang#14868)
This commit also tweaks some variable names so that get_and_update/3 and get_and_update!/3 implementations match a bit closer.
1 parent e309da4 commit 25771dd

File tree

2 files changed

+19
-8
lines changed

2 files changed

+19
-8
lines changed

lib/elixir/lib/keyword.ex

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ defmodule Keyword do
437437
end
438438

439439
@doc """
440-
Gets the value from `key` and updates it, all in one pass.
440+
Gets the value for `key` and updates it in one pass, deleting duplicate keys.
441441
442442
The `fun` argument receives the value of `key` (or `nil` if `key`
443443
is not present) and must return a two-element tuple: the current value
@@ -483,7 +483,7 @@ defmodule Keyword do
483483
defp get_and_update([{key, current} | t], acc, key, fun) do
484484
case fun.(current) do
485485
{get, value} ->
486-
{get, :lists.reverse(acc, [{key, value} | t])}
486+
{get, :lists.reverse(acc, [{key, value} | delete(t, key)])}
487487

488488
:pop ->
489489
{current, :lists.reverse(acc, t)}
@@ -509,7 +509,8 @@ defmodule Keyword do
509509
end
510510

511511
@doc """
512-
Gets the value under `key` and updates it. Raises if there is no `key`.
512+
Gets the value for `key` and updates it in one pass, deleting duplicate keys,
513+
raising if `key` can't be found in `keywords`.
513514
514515
The `fun` argument receives the value under `key` and must return a
515516
two-element tuple: the current value (the retrieved value, which can be
@@ -545,21 +546,21 @@ defmodule Keyword do
545546
get_and_update!(keywords, key, fun, [])
546547
end
547548

548-
defp get_and_update!([{key, value} | keywords], key, fun, acc) do
549+
defp get_and_update!([{key, value} | t], key, fun, acc) do
549550
case fun.(value) do
550551
{get, value} ->
551-
{get, :lists.reverse(acc, [{key, value} | delete(keywords, key)])}
552+
{get, :lists.reverse(acc, [{key, value} | delete(t, key)])}
552553

553554
:pop ->
554-
{value, :lists.reverse(acc, keywords)}
555+
{value, :lists.reverse(acc, t)}
555556

556557
other ->
557558
raise "the given function must return a two-element tuple or :pop, got: #{inspect(other)}"
558559
end
559560
end
560561

561-
defp get_and_update!([{_, _} = e | keywords], key, fun, acc) do
562-
get_and_update!(keywords, key, fun, [e | acc])
562+
defp get_and_update!([{_, _} = h | t], key, fun, acc) do
563+
get_and_update!(t, key, fun, [h | acc])
563564
end
564565

565566
defp get_and_update!([], key, _fun, acc) when is_atom(key) do

lib/elixir/test/elixir/keyword_test.exs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ defmodule KeywordTest do
2828
assert Map.__info__(:functions) -- Keyword.__info__(:functions) == [from_struct: 1]
2929
end
3030

31+
test "get_and_update/3 removes duplicates from the input keyword list" do
32+
assert Keyword.get_and_update([a: 1, b: 2, a: 3], :a, fn value -> {value, value + 10} end) ==
33+
{1, [a: 11, b: 2]}
34+
end
35+
3136
test "get_and_update/3 raises on bad return value from the argument function" do
3237
message = "the given function must return a two-element tuple or :pop, got: 1"
3338

@@ -42,6 +47,11 @@ defmodule KeywordTest do
4247
end
4348
end
4449

50+
test "get_and_update!/3 removes duplicates from the input keyword list" do
51+
assert Keyword.get_and_update!([a: 1, b: 2, a: 3], :a, fn value -> {value, value + 10} end) ==
52+
{1, [a: 11, b: 2]}
53+
end
54+
4555
test "get_and_update!/3 raises on bad return value from the argument function" do
4656
message = "the given function must return a two-element tuple or :pop, got: 1"
4757

0 commit comments

Comments
 (0)