Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Extend the Dict protocol to work with tuple lists

  • Loading branch information...
commit 061c0a3a5d3db69d40e747782d624fb8814b5163 1 parent f373e67
Devin Torres authored
11 lib/elixir/lib/access.ex
View
@@ -21,17 +21,22 @@ end
defimpl Access, for: List do
@doc """
- Access the given key in a keyword list.
+ Access the given key in a tuple list.
## Examples
keywords = [a: 1, b: 2]
keywords[:a] #=> 1
+ star_ratings = [{1.0, "★"}, {1.5, "★☆"}, {2.0, "★★"}]
+ star_ratings[1.5] #=> "★☆"
+
"""
- def access(list, atom) when is_atom(atom) do
- Keyword.get(list, atom)
+ def access([], _key), do: nil
+
+ def access(list, key) do
+ Dict.get(list, key)
end
end
125 lib/elixir/lib/dict.ex
View
@@ -57,10 +57,14 @@ defmodule Dict do
"""
@spec keys(t) :: [key]
- def keys(dict) do
+ def keys(dict) when is_tuple(dict) do
elem(dict, 0).keys(dict)
end
+ def keys(dict) when is_list(dict) do
+ lc { key, _ } inlist dict, do: key
+ end
+
@doc """
Returns a list containing all dict's values.
@@ -71,10 +75,14 @@ defmodule Dict do
"""
@spec values(t) :: [value]
- def values(dict) do
+ def values(dict) when is_tuple(dict) do
elem(dict, 0).values(dict)
end
+ def values(dict) when is_list(dict) do
+ lc { _, value } inlist dict, do: value
+ end
+
@doc """
Returns the number of elements in `dict`.
@@ -85,10 +93,14 @@ defmodule Dict do
"""
@spec size(t) :: non_neg_integer
- def size(dict) do
+ def size(dict) when is_tuple(dict) do
elem(dict, 0).size(dict)
end
+ def size(dict) when is_list(dict) do
+ length(dict)
+ end
+
@doc """
Returns whether the given key exists in the given dict.
@@ -100,10 +112,14 @@ defmodule Dict do
"""
@spec has_key?(t, key) :: boolean
- def has_key?(dict, key) do
+ def has_key?(dict, key) when is_tuple(dict) do
elem(dict, 0).has_key?(dict, key)
end
+ def has_key?(dict, key) when is_list(dict) do
+ :lists.keymember(key, 1, dict)
+ end
+
@doc """
Returns the value associated with `key` in `dict`. If `dict` does not
contain `key`, returns `default` (or nil if not provided).
@@ -117,11 +133,22 @@ defmodule Dict do
"""
@spec get(t, key) :: value | nil
+ def get(dict, key) do
+ get(dict, key, nil)
+ end
+
@spec get(t, key, value) :: value
- def get(dict, key, default // nil) do
+ def get(dict, key, default) when is_tuple(dict) do
elem(dict, 0).get(dict, key, default)
end
+ def get(dict, key, default) when is_list(dict) do
+ case :lists.keyfind(key, 1, dict) do
+ { ^key, value } -> value
+ false -> default
+ end
+ end
+
@doc """
Returns the value associated with `key` in `dict`. If `dict` does not
contain `key`, it raises `KeyError`.
@@ -134,10 +161,17 @@ defmodule Dict do
"""
@spec get!(t, key) :: value | no_return
- def get!(dict, key) do
+ def get!(dict, key) when is_tuple(dict) do
elem(dict, 0).get!(dict, key)
end
+ def get!(dict, key) when is_list(dict) do
+ case :lists.keyfind(key, 1, dict) do
+ { ^key, value } -> value
+ false -> raise(KeyError, key: key)
+ end
+ end
+
@doc """
Stores the given `value` under `key` in `dict`.
If `dict` already has `key`, the stored value is replaced by the new one.
@@ -150,10 +184,14 @@ defmodule Dict do
"""
@spec put(t, key, value) :: t
- def put(dict, key, val) do
+ def put(dict, key, val) when is_tuple(dict) do
elem(dict, 0).put(dict, key, val)
end
+ def put(dict, key, val) when is_list(dict) do
+ [{key, val}|delete(dict, key)]
+ end
+
@doc """
Puts the given `value` under `key` in `dict` unless `key` already exists.
@@ -165,10 +203,17 @@ defmodule Dict do
"""
@spec put_new(t, key, value) :: t
- def put_new(dict, key, val) do
+ def put_new(dict, key, val) when is_tuple(dict) do
elem(dict, 0).put_new(dict, key, val)
end
+ def put_new(dict, key, val) when is_list(dict) do
+ case :lists.keyfind(key, 1, dict) do
+ { ^key, _ } -> dict
+ false -> [{key,val}|dict]
+ end
+ end
+
@doc """
Removes the entry stored under the given key from `dict`.
If `dict` does not contain `key`, returns the dictionary unchanged.
@@ -183,10 +228,14 @@ defmodule Dict do
"""
@spec delete(t, key) :: t
- def delete(dict, key) do
+ def delete(dict, key) when is_tuple(dict) do
elem(dict, 0).delete(dict, key)
end
+ def delete(dict, key) when is_list(dict) do
+ lc { k, _ } = tuple inlist dict, key != k, do: tuple
+ end
+
@doc """
Merges two dicts into one. If the dicts have duplicated entries,
the one given as second argument wins. In case the second argument
@@ -202,6 +251,10 @@ defmodule Dict do
"""
@spec merge(t, t) :: t
+ def merge(dict1, dict2) when is_list(dict1) and is_list(dict2) do
+ dict2 ++ lc({ k, _ } = tuple inlist dict1, not has_key?(dict2, k), do: tuple)
+ end
+
def merge(dict1, dict2) do
merge(dict1, dict2, fn(_k, _v1, v2) -> v2 end)
end
@@ -221,10 +274,22 @@ defmodule Dict do
"""
@spec merge(t, t, (key, value, value -> value)) :: t
- def merge(dict1, dict2, fun) do
+ def merge(dict1, dict2, fun) when is_tuple(dict1) do
elem(dict1, 0).merge(dict1, dict2, fun)
end
+ def merge(dict1, dict2, fun) when is_list(dict1) and is_list(dict2) do
+ do_merge(dict2, dict1, fun)
+ end
+
+ defp do_merge([{ k, v2 }|t], acc, fun) do
+ do_merge t, update(acc, k, v2, fn(v1) -> fun.(k, v1, v2) end), fun
+ end
+
+ defp do_merge([], acc, _fun) do
+ acc
+ end
+
@doc """
Update a value in `dict` by calling `fun` on the value to get a new
value. An exception is generated if `key` is not present in the dict.
@@ -237,10 +302,22 @@ defmodule Dict do
"""
@spec update(t, key, (value -> value)) :: t
- def update(dict, key, fun) do
+ def update(dict, key, fun) when is_tuple(dict) do
elem(dict, 0).update(dict, key, fun)
end
+ def update([{key, value}|dict], key, fun) do
+ [{key, fun.(value)}|delete(dict, key)]
+ end
+
+ def update([{_, _} = e|dict], key, fun) do
+ [e|update(dict, key, fun)]
+ end
+
+ def update([], key, _fun) do
+ raise(KeyError, key: key)
+ end
+
@doc """
Update a value in `dict` by calling `fun` on the value to get a new value. If
`key` is not present in `dict` then `initial` will be stored as the first
@@ -254,24 +331,44 @@ defmodule Dict do
"""
@spec update(t, key, value, (value -> value)) :: t
- def update(dict, key, initial, fun) do
+ def update(dict, key, initial, fun) when is_tuple(dict) do
elem(dict, 0).update(dict, key, initial, fun)
end
+ def update([{key, value}|dict], key, _initial, fun) do
+ [{key, fun.(value)}|delete(dict, key)]
+ end
+
+ def update([{_, _} = e|dict], key, initial, fun) do
+ [e|update(dict, key, initial, fun)]
+ end
+
+ def update([], key, initial, _fun) do
+ [{key, initial}]
+ end
+
@doc """
Returns an empty dict of the same type as `dict`.
"""
@spec empty(t) :: t
- def empty(dict) do
+ def empty(dict) when is_tuple(dict) do
elem(dict, 0).empty(dict)
end
+ def empty(dict) when is_list(dict) do
+ []
+ end
+
@doc """
Returns a list of key-value pairs stored in `dict`.
No particular order is enforced.
"""
@spec to_list(t) :: list
- def to_list(dict) do
+ def to_list(dict) when is_tuple(dict) do
elem(dict, 0).to_list(dict)
end
+
+ def to_list(dict) when is_list(dict) do
+ dict
+ end
end
40 lib/elixir/test/elixir/dict_test.exs
View
@@ -83,15 +83,25 @@ defmodule DictTest.Common do
dict1 = new_dict Enum.zip ["a", "b", "c"], [1, 2, 3]
dict2 = new_dict Enum.zip ["a", "c", "d"], [3, :a, 0]
+ merged = Dict.merge(dict1, dict2)
final = new_dict Enum.zip ["a", "b", "c", "d"], [3, 2, :a, 0]
- assert Dict.merge(dict1, dict2) == final
+
+ cmp = fn {k1, _}, {k2, _} -> k1 < k2 end
+ actual = Enum.sort(Dict.to_list(merged), cmp)
+ expected = Enum.sort(final, cmp)
+ assert expected == actual
end
test :merge_with_enum do
dict1 = new_dict Enum.zip ["a", "b", "c"], [1, 2, 3]
dict2 = Enum.zip ["a", "c", "d"], [3, :a, 0]
+ merged = Dict.merge(dict1, dict2)
final = new_dict(Enum.zip ["a", "b", "c", "d"], [3, 2, :a, 0])
- assert Dict.merge(dict1, dict2) == final
+
+ cmp = fn {k1, _}, {k2, _} -> k1 < k2 end
+ actual = Enum.sort(Dict.to_list(merged), cmp)
+ expected = Enum.sort(final, cmp)
+ assert expected == actual
end
test :merge_with_function do
@@ -126,11 +136,25 @@ defmodule DictTest.Common do
assert empty_dict == Dict.empty new_dict
end
- defp empty_dict, do: unquote(module).new
+ case unquote(module) do
+ List ->
+ defp empty_dict, do: []
+
+ defp new_dict(list // [{"first_key", 1}, {"second_key", 2}])
+ defp new_dict(list), do: list
+ defp new_dict(list, transform) do
+ Enum.reduce list, [], fn i, acc ->
+ { k, v } = transform.(i)
+ [{ k, v }] ++ acc
+ end
+ end
+ _ ->
+ defp empty_dict, do: unquote(module).new
- defp new_dict(list // [{"first_key", 1}, {"second_key", 2}])
- defp new_dict(list), do: unquote(module).new list
- defp new_dict(list, transform), do: unquote(module).new list, transform
+ defp new_dict(list // [{"first_key", 1}, {"second_key", 2}])
+ defp new_dict(list), do: unquote(module).new list
+ defp new_dict(list, transform), do: unquote(module).new list, transform
+ end
end
end
end
@@ -164,3 +188,7 @@ defmodule Binary.DictTest do
assert merged["first_key"] == 13
end
end
+
+defmodule ListDictTest do
+ use DictTest.Common, List
+end
Please sign in to comment.
Something went wrong with that request. Please try again.