Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions lib/elixir/lib/keyword.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule Keyword do
in `Enum` and `List` can also be applied.
"""

@compile :inline_list_funcs
@behaviour Dict

@type key :: atom
Expand Down Expand Up @@ -174,7 +175,12 @@ defmodule Keyword do
"""
@spec get_values(t, key) :: [value]
def get_values(keywords, key) when is_list(keywords) and is_atom(key) do
for {k, v} <- keywords, key == k, do: v
fun = fn
{k, v} when k === key -> {true, v}
{_, _} -> false
end

:lists.filtermap(fun, keywords)
end

@doc """
Expand All @@ -192,7 +198,7 @@ defmodule Keyword do
"""
@spec keys(t) :: [key]
def keys(keywords) when is_list(keywords) do
for {key, _} <- keywords, do: key
:lists.map(fn {k, _} -> k end, keywords)
end

@doc """
Expand All @@ -206,11 +212,11 @@ defmodule Keyword do
"""
@spec values(t) :: [value]
def values(keywords) when is_list(keywords) do
for {_, value} <- keywords, do: value
:lists.map(fn {_, v} -> v end, keywords)
end

@doc """
Deletes the entry in the keyword list for a `key` with `value`.
Deletes the entries in the keyword list for a `key` with `value`.
If no `key` with `value` exists, returns the keyword list unchanged.

## Examples
Expand All @@ -227,11 +233,11 @@ defmodule Keyword do
"""
@spec delete(t, key, value) :: t
def delete(keywords, key, value) when is_list(keywords) and is_atom(key) do
for {k, v} = tuple <- keywords, key != k or value != v, do: tuple
:lists.filter(fn {k, v} -> k != key or v != value end, keywords)
end

@doc """
Deletes all entries in the keyword list for a specific `key`.
Deletes the entries in the keyword list for a specific `key`.
If the `key` does not exist, returns the keyword list unchanged.
Use `delete_first` to delete just the first entry in case of
duplicated keys.
Expand All @@ -250,7 +256,7 @@ defmodule Keyword do
"""
@spec delete(t, key) :: t
def delete(keywords, key) when is_list(keywords) and is_atom(key) do
for {k, _} = tuple <- keywords, key != k, do: tuple
:lists.filter(fn {k, _} -> k != key end, keywords)
end

@doc """
Expand Down Expand Up @@ -339,7 +345,8 @@ defmodule Keyword do
"""
@spec merge(t, t) :: t
def merge(d1, d2) when is_list(d1) and is_list(d2) do
d2 ++ for({k, _} = tuple <- d1, not has_key?(d2, k), do: tuple)
fun = fn {k, _v} -> not has_key?(d2, k) end
d2 ++ :lists.filter(fun, d1)
end

@doc """
Expand Down Expand Up @@ -467,16 +474,16 @@ defmodule Keyword do

"""
def split(keywords, keys) when is_list(keywords) do
acc = {[], []}

{take, drop} = Enum.reduce keywords, acc, fn({k, v}, {take, drop}) ->
fun = fn {k, v}, {take, drop} ->
case k in keys do
true -> {[{k, v}|take], drop}
false -> {take, [{k, v}|drop]}
end
end

{Enum.reverse(take), Enum.reverse(drop)}
acc = {[], []}
{take, drop} = :lists.foldl(fun, acc, keywords)
{:lists.reverse(take), :lists.reverse(drop)}
end

@doc """
Expand All @@ -497,7 +504,7 @@ defmodule Keyword do

"""
def take(keywords, keys) when is_list(keywords) do
for {k, _} = tuple <- keywords, k in keys, do: tuple
:lists.filter(fn {k, _} -> k in keys end, keywords)
end

@doc """
Expand All @@ -517,7 +524,7 @@ defmodule Keyword do

"""
def drop(keywords, keys) when is_list(keywords) do
for {k, _} = tuple <- keywords, not k in keys, do: tuple
:lists.filter(fn {k, _} -> not k in keys end, keywords)
end

@doc """
Expand Down
52 changes: 52 additions & 0 deletions lib/elixir/test/elixir/keyword_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,39 @@ defmodule KeywordTest do
test "keys/1" do
assert Keyword.keys(create_keywords) == [:first_key, :second_key]
assert Keyword.keys(create_empty_keywords) == []

assert_raise FunctionClauseError, fn ->
Keyword.keys([:foo])
end
end

test "values/1" do
assert Keyword.values(create_keywords) == [1, 2]
assert Keyword.values(create_empty_keywords) == []

assert_raise FunctionClauseError, fn ->
Keyword.values([:foo])
end
end

test "delete/2" do
assert Keyword.delete(create_keywords, :second_key) == [first_key: 1]
assert Keyword.delete(create_keywords, :other_key) == [first_key: 1, second_key: 2]
assert Keyword.delete(create_empty_keywords, :other_key) == []

assert_raise FunctionClauseError, fn ->
Keyword.delete([:foo], :foo)
end
end

test "delete/3" do
keywords = [a: 1, b: 2, c: 3, a: 2]
assert Keyword.delete(keywords, :a, 2) == [a: 1, b: 2, c: 3]
assert Keyword.delete(keywords, :a, 1) == [b: 2, c: 3, a: 2]

assert_raise FunctionClauseError, fn ->
Keyword.delete([:foo], :foo, 0)
end
end

test "put/3" do
Expand All @@ -90,6 +112,10 @@ defmodule KeywordTest do
assert Keyword.merge(create_keywords, create_empty_keywords) == [first_key: 1, second_key: 2]
assert Keyword.merge(create_keywords, create_keywords) == [first_key: 1, second_key: 2]
assert Keyword.merge(create_empty_keywords, create_empty_keywords) == []

assert_raise FunctionClauseError, fn ->
Keyword.delete([:foo], [:foo])
end
end

test "merge/3" do
Expand Down Expand Up @@ -136,6 +162,10 @@ defmodule Keyword.DuplicatedTest do
assert Keyword.get_values(create_keywords, :first_key) == [1, 2]
assert Keyword.get_values(create_keywords, :second_key) == [2]
assert Keyword.get_values(create_keywords, :other_key) == []

assert_raise FunctionClauseError, fn ->
Keyword.get_values([:foo], :foo)
end
end

test "keys/1" do
Expand Down Expand Up @@ -191,6 +221,28 @@ defmodule Keyword.DuplicatedTest do
assert Keyword.has_key?([a: 1], :b) == false
end

test "take/2" do
assert Keyword.take([], []) == []
assert Keyword.take([a: 0, b: 1, a: 2], []) == []
assert Keyword.take([a: 0, b: 1, a: 2], [:a]) == [a: 0, a: 2]
assert Keyword.take([a: 0, b: 1, a: 2], [:b]) == [b: 1]

assert_raise FunctionClauseError, fn ->
Keyword.take([:foo], [:foo])
end
end

test "drop/2" do
assert Keyword.drop([], []) == []
assert Keyword.drop([a: 0, b: 1, a: 2], []) == [a: 0, b: 1, a: 2]
assert Keyword.drop([a: 0, b: 1, a: 2], [:a]) == [b: 1]
assert Keyword.drop([a: 0, b: 1, a: 2], [:b]) == [a: 0, a: 2]

assert_raise FunctionClauseError, fn ->
Keyword.drop([:foo], [:foo])
end
end

defp create_empty_keywords, do: []
defp create_keywords, do: [first_key: 1, first_key: 2, second_key: 2]
end