Exercism: Roman Numeral

Hey!

Welcome back to another episode of exercism.io exercise wherein we practice our logic and problem solving capabilities.

Okay! so I got this new challenge called Converting a number into a Roman Numeral.

roman-numeral.png

It says there that it is easy, but as for me it wasn't hahaha!

Start to Analyze!

As for me, the key hints to solve the problem is by the power of algorithm and pattern matching! Hooray ๐Ÿ”ฅ

Honestly I struggled a lot on juggling different ideas on how to solve this problem. So first I started to gather some information that can be useful for me.

Solution

@doc """
  Convert the number to a roman numeral.
"""
@spec numeral(pos_integer) :: String.t()
def numeral(number) do
end

Given the function doc and spec, I started to think that I need to;

  • convert integer to string
  • split and turn them into an array
@doc """
  Convert the number to a roman numeral.
"""
@spec numeral(pos_integer) :: String.t()
def numeral(number) do
  numbers =
    number
    |> Integer.to_string()
    |> String.split("", trim: true)
end

In this way, I thought I could manipulate them one by one and match the corresponding values according to their place number. So to satisfy it;

  • using Enum.with_index() I jump into the data and transform them into arrays elements together with their indexes.
@doc """
  Convert the number to a roman numeral.
"""
@spec numeral(pos_integer) :: String.t()
def numeral(number) do
  numbers =
    number
    |> Integer.to_string()
    |> String.split("", trim: true)
    |> Enum.with_index()
end

Now the next thing that I did was to transform the data into a map because I thought in this was I could pattern match depending on their index as their place number

@doc """
  Convert the number to a roman numeral.
"""
@spec numeral(pos_integer) :: String.t()
def numeral(number) do
  numbers =
    number
    |> Integer.to_string()
    |> String.split("", trim: true)
    |> Enum.with_index()
    |> Enum.map(fn {x, index} ->
    {index, String.to_integer(x)}
    end)
    |> Map.new()
end

Finally, I created a private function named to_numeral/1 that accepts a map.

@doc """
  Convert the number to a roman numeral.
"""
@spec numeral(pos_integer) :: String.t()
def numeral(number) do
  numbers =
    number
    |> Integer.to_string()
    |> String.split("", trim: true)
    |> Enum.with_index()
    |> Enum.map(fn {x, index} ->
    {index, String.to_integer(x)}
    end)
    |> Map.new()
    |> to_numeral
end

WTF is to_numeral/1 been doing? ๐Ÿค”

So the next plan that I have is to create a new private function that will turn this Map into Roman Numeral values.

defp to_numeral(%{0 => thousands, 1 => hundreds, 2 => tens, 3 => ones}) do
  parse(thousands, :thousands) <> to_numeral(%{0 => hundreds, 1 => tens, 2 => ones})
end

defp to_numeral(%{0 => hundreds, 1 => tens, 2 => ones}) do
  parse(hundreds, :hundreds) <> to_numeral(%{0 => tens, 1 => ones})
end

defp to_numeral(%{0 => tens, 1 => ones}) do
  parse(tens, :tens) <> to_numeral(%{0 => ones})
end

defp to_numeral(%{0 => ones}) do
  parse(ones, :one)
end

Yeah I know! there are so many movings parts here. It kind a hard to admit that I did all of this but life isn't always perfect. I need to be dumb sometimes for me to learn. So you know how this ends.

I created a private function parse/2 that turns this numbers according to their equivalent value in roman characters.

So in all, I've created about 90+ line of code just to deliver my answer. For the next blog, my goal is to refactor this one and come up with a much better approach and apply it to my future problem solving at exercism: track elixir.

You can check out my solution here

ย