42 lines
977 B
Elixir
42 lines
977 B
Elixir
|
defmodule IsbnVerifier do
|
||
|
@doc """
|
||
|
Checks if a string is a valid ISBN-10 identifier
|
||
|
|
||
|
## Examples
|
||
|
|
||
|
iex> IsbnVerifier.isbn?("3-598-21507-X")
|
||
|
true
|
||
|
|
||
|
iex> IsbnVerifier.isbn?("3-598-2K507-0")
|
||
|
false
|
||
|
|
||
|
"""
|
||
|
|
||
|
@regex ~r/^(?<digits>\d{9})(?<check_digit>[0-9X])$/
|
||
|
|
||
|
@spec isbn?(String.t()) :: boolean
|
||
|
def isbn?(isbn) do
|
||
|
with isbn_without_dashes <- String.replace(isbn, "-", ""),
|
||
|
%{"digits" => digits, "check_digit" => check_digit} <-
|
||
|
Regex.named_captures(@regex, isbn_without_dashes) do
|
||
|
rem(check_sum(digits) + check_digit(check_digit), 11) == 0
|
||
|
else
|
||
|
_ -> false
|
||
|
end
|
||
|
end
|
||
|
|
||
|
defp check_sum(digits) do
|
||
|
{1, check_sum} =
|
||
|
digits
|
||
|
|> String.graphemes()
|
||
|
|> Enum.reduce({10, 0}, fn digit, {index, acc} ->
|
||
|
{index - 1, acc + String.to_integer(digit) * index}
|
||
|
end)
|
||
|
|
||
|
check_sum
|
||
|
end
|
||
|
|
||
|
defp check_digit("X"), do: 10
|
||
|
defp check_digit(string), do: String.to_integer(string)
|
||
|
end
|