PassiveSupport.Item (passive_support v0.8.4)
Functions for handling arbitrary-shape values.
Link to this section Summary
Functions
Returns true
for empty enumerables, empty tuples, whitespace-only strings,
nil
, and false
; returns false
for any other value.
Traverses into item
through an arbitrary path
of keys and indices.
Returns the value for any present?/1
value and nil
for any blank?/1
value.
Returns true
of any value that is not blank?/1
Calls fun.(item)
, returning item
with any transformations done therein.
Calls fun.(item)
and returns item
unaltered.
Link to this section Types
Link to this section Functions
blank?(item)
Specs
Returns true
for empty enumerables, empty tuples, whitespace-only strings,
nil
, and false
; returns false
for any other value.
Note that while a string containing only whitespace can be considered blank,
a charlist of the same nature will return false
. Because charlists are
represented internally as lists of integers, a charlist of whitespace would
be indescernible from a list of numeric integers, neither of which would be
individually considered blank, and therefore should not be regarded as blank
in tandem.
Examples
iex> blank?({})
true
iex> blank?(%{})
true
iex> blank?(MapSet.new())
true
iex> blank?(0)
false
iex> blank?(nil)
true
iex> blank?(false)
true
iex> blank?(" ")
true
iex> blank?(' ') # [32, 32]
false
iex> blank?(" hi ")
false
dig(item, path \\ [], default \\ nil)
Specs
dig(traversable(), key_or_index() | [key_or_index()], t()) :: t()
Traverses into item
through an arbitrary path
of keys and indices.
Think of dig/3
as an extension of the concept at the heart of
Access.get/3
. There are times when the data you're working with
is a deeply-nested structure, and your bigger concern is what a
specific part of that data looks like. In cases where you are working
with one sort of data type (e.g., maps, structs, tuples, or lists),
then you know you can use the same function or macro to dig down
through each successive layer of nested data. However, when working
with disparate data types, keeping track of the structure of each
level of the nested data becomes an exercise in both patience,
and trial and error.
The assurance dig/3
provides is that, until arriving at a leaf node
within item
or fully traversing through path
, the function will
retrieve the element of item
at the next point within path
,
dispatching to the correct function for doing so based on the
structure of the data at the current level of traversal. If dig
arrives at a nil
value at any point in traversal, the remainder
of the path is dropped, and the value provided as default
(which,
by custom, defaults to nil
itself) is immediately returned to the
caller.
Note: The only event in which this function will raise an exception
is when it attempts to traverse the path
of a scalar value. To guarantee
consistency in the traversal rules, trying to traverse a tuple with
an out-of-bounds index results in nil
being returned instead of an
ArgumentError
being raised. Similarly, attempting to dig into a
struct at a keyword that it doesn't define will return nil
instead of
raising a KeyError
. For exception propagation, use dig!/3
instead.
Examples
iex> pets = %{
...> cats: [
...> %{name: "Lester", favorite_toy: "feather"},
...> %{name: "Janice", favorite_toy: "laser pointer"}
...> ],
...> dogs: [
...> %{name: "Scrump", favorite_toy: "rope"},
...> %{name: "Stitch", favorite_toy: "ball"}
...> ],
...> hyenas: [
...> %{"what is this" => "oh no", "hyenas can't be pets" => "too wild"}
...> ]
...> }
iex> dig(pets, [:dogs])
[%{name: "Scrump", favorite_toy: "rope"}, %{name: "Stitch", favorite_toy: "ball"}]
iex> dig(pets, [:cats, 1])
%{name: "Janice", favorite_toy: "laser pointer"}
iex> dig(pets, [:hyenas, 0, "what is this"])
"oh no"
iex> dig(pets, [:dogs, 0, :favorite_food, :ingredients])
nil
You can also use dig
through lists of nested data structures
by passing an empty list []
as a part of path
.
iex> connor_family = [
...> {"Sarah", %{favorite_movies: ["Dr. Strangelove", "China Town", "Citizen Kane"]}},
...> {"John", %{favorite_movies: ["Tron", "Star Wars", "The Fifth Element"]}},
...> {"Cameron", %{favorite_movies: ["Robocop", "Logan's Run", "The Animatrix - The Second Renaissance Part 2"]}}
...> ]
iex> dig(connor_family, [[], 1, :favorite_movies, 2])
["Citizen Kane", "The Fifth Element", "The Animatrix - The Second Renaissance Part 2"]
presence(item)
Specs
Returns the value for any present?/1
value and nil
for any blank?/1
value.
Examples
iex> presence({})
nil
iex> presence(%{})
nil
iex> presence(nil)
nil
iex> presence(false)
nil
iex> presence([false])
[false]
iex> presence(" ")
nil
iex> presence(" hi ")
" hi "
present?(item)
Specs
Returns true
of any value that is not blank?/1
Examples
iex> present?({})
false
iex> present?(%{})
false
iex> present?(MapSet.new(1..10))
true
iex> present?(0)
true
iex> present?(nil)
false
iex> present?(false)
false
iex> present?(" ")
false
iex> present?(' ')
true
iex> present?(" hi ")
true
tap(item, fun)
Calls fun.(item)
, returning item
with any transformations done therein.
Basically, I got tired of not being able to pipe a value into arbitrary positions of other functions.
Examples
iex> ", " |> Item.tap(&Enum.join(1..10, &1))
"1, 2, 3, 4, 5, 6, 7, 8, 9, 10"
iex> false |> Item.tap(&(unless &1, do: "oh!"))
"oh!"
iex> [5, 4, 3, 2, 1] |> hd |> Item.tap(&(&1 * &1))
25
iex> "hello world!" |> Item.tap(&Regex.scan(~r/lo?/, &1))
[["l"], ["lo"], ["l"]]
tee(item, fun)
Calls fun.(item)
and returns item
unaltered.
This function mimics the tee
program in shell systems,
allowing a copy of the passed-in item to be sent to another function
amid a pipeline of other functions that are transforming it
or handing it off for another value.
Examples
1..10 |> Enum.to_list |> tee(&Logger.info(inspect(&1, label: "`1..10` as a list")))
# (… output from Logger …)
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
some_data |> tee(&IO.inspect(&1, label: "encoding as json")) |> Jason.encode!
# => JSON-encoded data