Packages

A collection of extra utilities and extensions to the Elixir standard library

Current section

Files

Jump to
extra lib keyword extra.ex
Raw

lib/keyword/extra.ex

defmodule Keyword.Extra do
@moduledoc """
Extensions to the built-in `Keyword` module.
"""
@type key :: Keyword.key
@type value :: Keyword.value
@type t :: Keyword.t
@compile {:inline, assert_key!: 3}
@compile {:inline, refute_key!: 3}
@doc """
Takes all entries corresponding to the given keys and returns them in a new
keyword list in the order that `keys` arrived.
## Examples
iex> Keyword.Extra.take_ordered([c: 3, b: 2, a: 1], [:a, :b])
[a: 1, b: 2]
iex> Keyword.Extra.take_ordered([x: 1, y: 2], [:x, :y, :z])
[x: 1, y: 2]
iex> location = %{state: "NY", city: "Manhattan", zip_code: "10009"}
...> Keyword.Extra.take_ordered(location, [:city, :state, :zip_code])
[city: "Manhattan", state: "NY", zip_code: "10009"]
"""
@spec take_ordered(t, [key]) :: t
@spec take_ordered(map, [key]) :: t
def take_ordered([{key, _value} | _rest] = kw_list, keys)
when is_atom(key),
do: do_take_ordered(kw_list, keys)
def take_ordered(%{} = map, keys) do
map
|> Keyword.new
|> do_take_ordered(keys)
end
@spec do_take_ordered(t, [key]) :: t
defp do_take_ordered(kw_list, keys) do
keys
|> Stream.filter(&Keyword.has_key?(kw_list, &1))
|> Enum.map(&get_pair(kw_list, &1))
end
@spec get_pair(t, key) :: {key, value} | nil
defp get_pair(kw_list, key) do
with {:ok, value} <- Keyword.fetch(kw_list, key) do
{key, value}
else
:error -> nil
end
end
@doc """
Raises an ArgumentError error if `list` contains the provided `key`.
Takes an optional argument specifying the error message.
"""
@spec refute_key!(t, key, String.t) :: :ok | no_return
def refute_key!(list, key, message \\ "Key is not allowed to be in list") do
if Keyword.has_key?(list, key) do
raise ArgumentError, message
else
:ok
end
end
@doc """
Raises an ArgumentError error if `list` does not have the provided `key`.
## Options
* `:message`: `binary`, the message to raise with when the value is missing.
* `:allow_nil_value`: `boolean`, Default: `true`. Set to true if a `nil` value
should not raise.
"""
@spec assert_key!(t, key, keyword) :: :ok | no_return
def assert_key!(list, key, opts \\ []) do
message =
Keyword.get(opts, :message, "#{inspect(key)} is required to be in keyword list.")
allow_nil_value? =
Keyword.get(opts, :allow_nil_value, true)
case Keyword.fetch(list, key) do
{:ok, nil} when not allow_nil_value? ->
raise(ArgumentError, message)
:error ->
raise(ArgumentError, message)
_ -> :ok
end
end
@doc """
Fetches the values for the specified `keys`.
If any of the `keys` do not exist, a `KeyError` is raised.
## Examples
iex> Keyword.Extra.fetch_keys!([color: "blue"], [:color])
["blue"]
"""
@spec fetch_keys!(t, [key]) :: [value]
def fetch_keys!(list, keys),
do: keys |> Enum.map(&Keyword.fetch!(list, &1))
@doc """
Gets the values for the specified `keys`.
Pass default values for keys using the following format: `{key, default}`. If any of the `keys` do
not exist, return `nil` or use the default value if supplied.
## Examples
iex> Keyword.Extra.get_keys([ssn: "1234", age: 41], [:age, :ssn])
[41, "1234"]
iex> Keyword.Extra.get_keys([first_name: "Bob"], [first_name: :missing, last_name: :missing])
["Bob", :missing]
"""
@spec get_keys(t, [key | {key, default :: any}]) :: [value]
def get_keys(list, keys) do
keys |> Enum.map(fn
{key, default} -> Keyword.get(list, key, default)
key -> Keyword.get(list, key)
end)
end
end