Skip to content

Commit fd936bb

Browse files
author
José Valim
committed
Merge pull request #2350 from ericmj/keyword-error
Raise if giving non-keywords to Keyword
2 parents 3e54d33 + 3471898 commit fd936bb

File tree

2 files changed

+73
-14
lines changed

2 files changed

+73
-14
lines changed

lib/elixir/lib/keyword.ex

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ defmodule Keyword do
2424
in `Enum` and `List` can also be applied.
2525
"""
2626

27+
@compile :inline_list_funcs
2728
@behaviour Dict
2829

2930
@type key :: atom
@@ -174,7 +175,12 @@ defmodule Keyword do
174175
"""
175176
@spec get_values(t, key) :: [value]
176177
def get_values(keywords, key) when is_list(keywords) and is_atom(key) do
177-
for {k, v} <- keywords, key == k, do: v
178+
fun = fn
179+
{k, v} when k === key -> {true, v}
180+
{_, _} -> false
181+
end
182+
183+
:lists.filtermap(fun, keywords)
178184
end
179185

180186
@doc """
@@ -192,7 +198,7 @@ defmodule Keyword do
192198
"""
193199
@spec keys(t) :: [key]
194200
def keys(keywords) when is_list(keywords) do
195-
for {key, _} <- keywords, do: key
201+
:lists.map(fn {k, _} -> k end, keywords)
196202
end
197203

198204
@doc """
@@ -206,11 +212,11 @@ defmodule Keyword do
206212
"""
207213
@spec values(t) :: [value]
208214
def values(keywords) when is_list(keywords) do
209-
for {_, value} <- keywords, do: value
215+
:lists.map(fn {_, v} -> v end, keywords)
210216
end
211217

212218
@doc """
213-
Deletes the entry in the keyword list for a `key` with `value`.
219+
Deletes the entries in the keyword list for a `key` with `value`.
214220
If no `key` with `value` exists, returns the keyword list unchanged.
215221
216222
## Examples
@@ -227,11 +233,11 @@ defmodule Keyword do
227233
"""
228234
@spec delete(t, key, value) :: t
229235
def delete(keywords, key, value) when is_list(keywords) and is_atom(key) do
230-
for {k, v} = tuple <- keywords, key != k or value != v, do: tuple
236+
:lists.filter(fn {k, v} -> k != key or v != value end, keywords)
231237
end
232238

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

256262
@doc """
@@ -339,7 +345,8 @@ defmodule Keyword do
339345
"""
340346
@spec merge(t, t) :: t
341347
def merge(d1, d2) when is_list(d1) and is_list(d2) do
342-
d2 ++ for({k, _} = tuple <- d1, not has_key?(d2, k), do: tuple)
348+
fun = fn {k, _v} -> not has_key?(d2, k) end
349+
d2 ++ :lists.filter(fun, d1)
343350
end
344351

345352
@doc """
@@ -467,16 +474,16 @@ defmodule Keyword do
467474
468475
"""
469476
def split(keywords, keys) when is_list(keywords) do
470-
acc = {[], []}
471-
472-
{take, drop} = Enum.reduce keywords, acc, fn({k, v}, {take, drop}) ->
477+
fun = fn {k, v}, {take, drop} ->
473478
case k in keys do
474479
true -> {[{k, v}|take], drop}
475480
false -> {take, [{k, v}|drop]}
476481
end
477482
end
478483

479-
{Enum.reverse(take), Enum.reverse(drop)}
484+
acc = {[], []}
485+
{take, drop} = :lists.foldl(fun, acc, keywords)
486+
{:lists.reverse(take), :lists.reverse(drop)}
480487
end
481488

482489
@doc """
@@ -497,7 +504,7 @@ defmodule Keyword do
497504
498505
"""
499506
def take(keywords, keys) when is_list(keywords) do
500-
for {k, _} = tuple <- keywords, k in keys, do: tuple
507+
:lists.filter(fn {k, _} -> k in keys end, keywords)
501508
end
502509

503510
@doc """
@@ -517,7 +524,7 @@ defmodule Keyword do
517524
518525
"""
519526
def drop(keywords, keys) when is_list(keywords) do
520-
for {k, _} = tuple <- keywords, not k in keys, do: tuple
527+
:lists.filter(fn {k, _} -> not k in keys end, keywords)
521528
end
522529

523530
@doc """

lib/elixir/test/elixir/keyword_test.exs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,39 @@ defmodule KeywordTest do
6262
test "keys/1" do
6363
assert Keyword.keys(create_keywords) == [:first_key, :second_key]
6464
assert Keyword.keys(create_empty_keywords) == []
65+
66+
assert_raise FunctionClauseError, fn ->
67+
Keyword.keys([:foo])
68+
end
6569
end
6670

6771
test "values/1" do
6872
assert Keyword.values(create_keywords) == [1, 2]
6973
assert Keyword.values(create_empty_keywords) == []
74+
75+
assert_raise FunctionClauseError, fn ->
76+
Keyword.values([:foo])
77+
end
7078
end
7179

7280
test "delete/2" do
7381
assert Keyword.delete(create_keywords, :second_key) == [first_key: 1]
7482
assert Keyword.delete(create_keywords, :other_key) == [first_key: 1, second_key: 2]
7583
assert Keyword.delete(create_empty_keywords, :other_key) == []
84+
85+
assert_raise FunctionClauseError, fn ->
86+
Keyword.delete([:foo], :foo)
87+
end
88+
end
89+
90+
test "delete/3" do
91+
keywords = [a: 1, b: 2, c: 3, a: 2]
92+
assert Keyword.delete(keywords, :a, 2) == [a: 1, b: 2, c: 3]
93+
assert Keyword.delete(keywords, :a, 1) == [b: 2, c: 3, a: 2]
94+
95+
assert_raise FunctionClauseError, fn ->
96+
Keyword.delete([:foo], :foo, 0)
97+
end
7698
end
7799

78100
test "put/3" do
@@ -90,6 +112,10 @@ defmodule KeywordTest do
90112
assert Keyword.merge(create_keywords, create_empty_keywords) == [first_key: 1, second_key: 2]
91113
assert Keyword.merge(create_keywords, create_keywords) == [first_key: 1, second_key: 2]
92114
assert Keyword.merge(create_empty_keywords, create_empty_keywords) == []
115+
116+
assert_raise FunctionClauseError, fn ->
117+
Keyword.delete([:foo], [:foo])
118+
end
93119
end
94120

95121
test "merge/3" do
@@ -136,6 +162,10 @@ defmodule Keyword.DuplicatedTest do
136162
assert Keyword.get_values(create_keywords, :first_key) == [1, 2]
137163
assert Keyword.get_values(create_keywords, :second_key) == [2]
138164
assert Keyword.get_values(create_keywords, :other_key) == []
165+
166+
assert_raise FunctionClauseError, fn ->
167+
Keyword.get_values([:foo], :foo)
168+
end
139169
end
140170

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

224+
test "take/2" do
225+
assert Keyword.take([], []) == []
226+
assert Keyword.take([a: 0, b: 1, a: 2], []) == []
227+
assert Keyword.take([a: 0, b: 1, a: 2], [:a]) == [a: 0, a: 2]
228+
assert Keyword.take([a: 0, b: 1, a: 2], [:b]) == [b: 1]
229+
230+
assert_raise FunctionClauseError, fn ->
231+
Keyword.take([:foo], [:foo])
232+
end
233+
end
234+
235+
test "drop/2" do
236+
assert Keyword.drop([], []) == []
237+
assert Keyword.drop([a: 0, b: 1, a: 2], []) == [a: 0, b: 1, a: 2]
238+
assert Keyword.drop([a: 0, b: 1, a: 2], [:a]) == [b: 1]
239+
assert Keyword.drop([a: 0, b: 1, a: 2], [:b]) == [a: 0, a: 2]
240+
241+
assert_raise FunctionClauseError, fn ->
242+
Keyword.drop([:foo], [:foo])
243+
end
244+
end
245+
194246
defp create_empty_keywords, do: []
195247
defp create_keywords, do: [first_key: 1, first_key: 2, second_key: 2]
196248
end

0 commit comments

Comments
 (0)