exercism/elixir/run-length-encoding/lib/run_length_encoder.ex

47 lines
1.7 KiB
Elixir

defmodule RunLengthEncoder do
@digits ~w(0 1 2 3 4 5 6 7 8 9)
defguardp is_digit(char) when char in @digits
defp encode_reducer(char, []), do: [{char, 1}]
defp encode_reducer(char, [{char, count} | rest]), do: [{char, count + 1} | rest]
defp encode_reducer(char, rest), do: [{char, 1} | rest]
defp encode_mapper({char, 1}), do: char
defp encode_mapper({char, count}), do: "#{count}#{char}"
defp decode_reducer(digit, []) when is_digit(digit), do: [{nil, digit}]
defp decode_reducer(digit, [{nil, count} | rest]) when is_digit(digit), do: [{nil, count <> digit} | rest]
defp decode_reducer(char, [{nil, count} | rest]) when not is_digit(char), do: [{char, String.to_integer(count)} | rest]
defp decode_reducer(digit, rest) when is_digit(digit), do: [{nil, digit} | rest]
defp decode_reducer(char, rest), do: [{char, 1} | rest]
defp decode_mapper({char, 1}), do: char
defp decode_mapper({char, count}), do: String.duplicate(char, count)
@doc """
Generates a string where consecutive elements are represented as a data value and count.
"AABBBCCCC" => "2A3B4C"
For this example, assume all input are strings, that are all uppercase letters.
It should also be able to reconstruct the data into its original form.
"2A3B4C" => "AABBBCCCC"
"""
@spec encode(String.t()) :: String.t()
def encode(string) do
string
|> String.graphemes()
|> Enum.reduce([], &encode_reducer/2)
|> Enum.reverse()
|> Enum.map_join(&encode_mapper/1)
end
@spec decode(String.t()) :: String.t()
def decode(string) do
string
|> String.graphemes()
|> Enum.reduce([], &decode_reducer/2)
|> Enum.reverse()
|> Enum.map_join(&decode_mapper/1)
end
end