DEV Community

Cover image for Some Elixir katas - Pt 1
Maartz
Maartz

Posted on

Some Elixir katas - Pt 1

During the previous quarantine I've decided to get some katas. Usually I tend to go on Exercism to grab some exercises.

But I've choose CodeWars to get fresher and community-driven exercises.

The plot is the following, you've 8 levels, called kyus, from 8 kyu to 1 kyu. Eight being the lowest or easiest level and one the tougher, much more a project than a real exercise.

I've made some 8, 7 and 6 level kyus, and last night I go my very first 5 kyu exercise. During this day I've passed a second one, and for the sake of explanation and writing, I want to go through the solving of both 5 kyu exercises in a series, maybe more.

Let's go

5kyu -- RGB to Hex Conversion

Here's the details

The rgb function is incomplete. Complete it so that passing in RGB decimal values will result in a hexadecimal representation being returned. Valid decimal values for RGB are 0 - 255. Any values that fall out of that range must be rounded to the closest valid value. Note: Your answer should always be 6 characters long, the shorthand with 3 will not work here. The following are examples of expected output values:

So we expect this:

Kata.rgb(255, 255, 255) # returns FFFFFF
Kata.rgb(255, 255, 300) # returns FFFFFF | It's false on purpose
Kata.rgb(0,0,0)         # returns 000000
Kata.rgb(148, 0, 211)   # returns 9400D3 | This is purple
Enter fullscreen mode Exit fullscreen mode

So first thing first, we need to grab the value passed in the function and put them in a list

def rgb(r,g,b) do
    [r,g,b]
end
Enter fullscreen mode Exit fullscreen mode

In an iex session, we can check this out:

iex(1)> Kata.rgb(255,255,255)
[255, 255, 255]
Enter fullscreen mode Exit fullscreen mode

Then, we should worry about this Kata.rgb(255, 255, 300) call. As I said before, it's false on purpose and we should take care of this. There's many ways to do so, like pattern matching, guard clauses etc. But I've decided to leverage the huge Elixir's standard library.

There's in fact two interesting functions in the Kernel module, min and max.

Let's map through each element of the list.

def rgb(r,g,b) do
    [r,g,b]
    |> Enum.map(fn x -> x |> max(0) |> min(255) end)
end
Enter fullscreen mode Exit fullscreen mode

Presto, we give constraints to our inputs.

iex(2)> RGB.rgb(255,255,300)
[255, 255, 255]
Enter fullscreen mode Exit fullscreen mode

That's great, so

After this, we need to somehow transform our 255 to FF.
Elixir's Integer module gives us a very good function: to_string.

If I refer to this function documentation, here's what it can do:

@spec to_string(integer(), 2..36) :: String.t()

Returns a binary which corresponds to the text representation of integer in the given base.

base can be an integer between 2 and 36.

Inlined by the compiler.

## Examples

    iex> Integer.to_string(100, 16)
    "64"

    iex> Integer.to_string(-100, 16)
    "-64"

    iex> Integer.to_string(882_681_651, 36)
    "ELIXIR"
Enter fullscreen mode Exit fullscreen mode

That's sweet because it totally fits our needs. We need a base 16 since it's hexadecimal.

Let's do this.

def rgb(r,g,b) do
    [r,g,b]
    |> Enum.map(fn x -> x |> max(0) |> min(255) end)
    |> Enum.map(fn x -> Integer.to_string(x, 16) end)
end
Enter fullscreen mode Exit fullscreen mode

And still in iex

iex(3)> Kata.rgb(255,255,300)
["FF", "FF", "FF"]
Enter fullscreen mode Exit fullscreen mode

Recall the Kata.rgb(0,0,0) call in the katas detail?

iex(4)> Kata.rgb(0,0,0)
["0", "0", "0"] # 000
Enter fullscreen mode Exit fullscreen mode

Ughh... not really what we expect though.

Kata.rgb(0,0,0) # We expect this output: 000000
Enter fullscreen mode Exit fullscreen mode

So we need to find a way to add those missings zeros.
Once again, the Elixir standard library come in handy!

This time, it's the String module with the pad_leading function.

Returns a new string padded with a leading filler which is made of elements from the padding.

Here's what we can read about it in the documentation.
Great, let's do this.

def rgb(r,g,b) do
    [r,g,b]
    |> Enum.map(fn x -> x |> max(0) |> min(255) end)
    |> Enum.map(fn x -> Integer.to_string(x, 16) end)
    |> Enum.map(fn x -> String.pad_leading(x, 2, "0") end)
end
Enter fullscreen mode Exit fullscreen mode

We want to pad our input with 0 to get a string with a length of two
Still in iex

iex(5)> Kata.rgb(255,255,300)
["FF", "FF", "FF"]
iex(6)> Kata.rgb(0,0,0)
["00", "00", "00"]
Enter fullscreen mode Exit fullscreen mode

At this point, we've almost finished the kata. We just need to join our list to be a single string. The Enum.join() will do the job.
Here what the full code looks like:

defmodule Kata do
    def rgb(r,g,b) do
        [r,g,b]
        |> Enum.map(fn x -> x |> max(0) |> min(255) end)
        |> Enum.map(fn x -> Integer.to_string(x, 16) end)
        |> Enum.map(fn x -> String.pad_leading(x, 2, "00") end)
        |> Enum.join()
    end
end
Enter fullscreen mode Exit fullscreen mode

And in iex it runs this way.

iex(7)> RGB.rgb(0,0,0)
"000000"
iex(8)> RGB.rgb(255,255,300)
"FFFFFF"
Enter fullscreen mode Exit fullscreen mode

So far so good, this 5 kyu kata is complete.
The pipe operator |> is very helpful in this scenario. Indeed, it let us think in function composition and also let the data flow through the functions. Each function gives us back a new list based on the passed one. We just need to pass a list and it give us back a list. Pretty neat!

This article comes from my personal blog.

Top comments (0)