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.
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
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
iex session, we can check this out:
iex(1)> Kata.rgb(255,255,255) [255, 255, 255]
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
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
Presto, we give constraints to our inputs.
iex(2)> RGB.rgb(255,255,300) [255, 255, 255]
That's great, so
After this, we need to somehow transform our
Integer module gives us a very good function:
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"
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
And still in
iex(3)> Kata.rgb(255,255,300) ["FF", "FF", "FF"]
Kata.rgb(0,0,0) call in the katas detail?
iex(4)> Kata.rgb(0,0,0) ["0", "0", "0"] # 000
Ughh... not really what we expect though.
Kata.rgb(0,0,0) # We expect this output: 000000
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
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
We want to pad our input with
0 to get a string with a length of two
iex(5)> Kata.rgb(255,255,300) ["FF", "FF", "FF"] iex(6)> Kata.rgb(0,0,0) ["00", "00", "00"]
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
iex it runs this way.
iex(7)> RGB.rgb(0,0,0) "000000" iex(8)> RGB.rgb(255,255,300) "FFFFFF"
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.