Here is a code sample with tests where I have a function that checks if the value passed in is in an allowable set of values and if it isn't, it raises an exception, but if the value passed in is nil or a blank string, it just returns false. The idea here is that any sort of blank value means is there is no value. If there is a non-blank value that doesn't match one of the valid values, then we do want to raise an exception because that value is invalid. Also imagine this value is coming from a command line argument or Phoenix request param where we can't control the input value be just nil, we have to check for all permutations of an empty value, nil, an empty string or a string containing just spaces.
ExUnit.start()
defmodule ValidColor do
@valid_colors MapSet.new(~w[red green blue])
def valid?(color) do
cond do
blank?(color) -> false
MapSet.member?(@valid_colors, color) -> true
true -> raise "invalid color"
end
end
defp blank?(value) do
case value do
nil -> true
v when is_binary(v) -> v =~ ~r/\A\s*\z/
_ -> false
end
end
end
defmodule ValidColorTest do
use ExUnit.Case, async: true
test "nil" do
refute ValidColor.valid?(nil)
end
test "empty string" do
refute ValidColor.valid?("")
end
test "string with only blank characters" do
refute ValidColor.valid?(" ")
end
test "invalid color" do
assert_raise RuntimeError, "invalid color", fn ->
ValidColor.valid?("yellow")
end
end
test "valid color" do
assert ValidColor.valid?("red")
end
end
This seems to be a case when a blank?
helper function is useful, as I have done in this example. There seems to be a sentiment among Elixir programmers that this type of helper method is unnecessary and can be done with pattern matching.
How can you implement a function like this with just pattern matching and not the blank?
helper function?
While all the answers given here are somehow correct, they are not semantically perfect. What you want is actually to treat nil
as an empty string and reject it. Do it straightforward:
defp blank?(str_or_nil),
do: "" == str_or_nil |> to_string() |> String.trim()
Please note, that this solution, unlike other given here so far, works with all the spaces, defined as spaces in UTF-8 specification.
The nil
case is easy:
defp blank?(nil), do: true
To check if a string consists of only whitespace characters, more involved pattern matching is required:
# Empty string is always blank.
defp blank?(""), do: true
# If the string's first byte is a space, tab, newline, or carriage return,
# we recursively check the remaining part of the string.
defp blank?(<<h, t::binary>>) when h in ' \t\n\r', do: blank?(t)
# Otherwise, it's not blank.
defp blank?(_), do: false
You can also use a case/2
:
def blank?(str) do
case str do
nil -> true
"" -> true
" " <> r -> blank?(r)
_ -> false
end
end
NB: in this case we only consider spaces, pattern matching on "\n","\r" and "\t" must also be considered as @dogbert did in his answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With