DEV Community

Kristian Pedersen
Kristian Pedersen

Posted on

#30daysofelm Day 22: Simple codewars.com challenges

This is day 22 of my 30 day Elm challenge

Code+demo: https://ellie-app.com/c2kXwQbnFbja1

1. About today's project

I like doing small CodeWars challenges sometimes, just to practice some basics.

They're often quite simple, but sometimes the simplicity can be deceiving, and occasionally I find myself not having read the requirements well enough.

My "next binary number with same number of 1's" was a CodeWars challenge. I also wrote about 4 CodeWars challenges on day 14.

While it's very good to push yourself like I've done lately, it can also be good to get some easy wins, while also picking up a new thing or two.

I hope today's writedown can be a nice resource for other beginners. Some Elm beginners immediately want to decode JSON and get into complex types, while others just want to know how to make an array of numbers from 1 to 100 (that's me).

Note: I usually skip a few CodeWars challenges, usually because they're poorly written, or they don't interest me.

2. Can number A be divided to get number B?

All we have to do is check if there's a remainder or not.

The remainder of 10 / 2 is 0, while the remainder of 9 / 2 is 1.

checkIfFactor : Int -> Int -> Bool
checkIfFactor factor base =
    remainderBy base factor == 0

checkIfFactor 10 2 -- True
checkIfFactor 9 2 -- False

Enter fullscreen mode Exit fullscreen mode

A slightly shorter solution would be this:

checkForFactor : Int -> Int -> Bool
checkForFactor a b = 
      modBy b a == 0
Enter fullscreen mode Exit fullscreen mode

The difference between modBy and remainderBy has to do with negative numbers: https://package.elm-lang.org/packages/elm/core/latest/Basics#modBy

3. Get the sum of all numbers from 1 to n

I'm sure I can solve this one easily, but it's still good to practice the very basics, and there might be solutions I haven't thought of.

Here's what I came up with, which is very easy to read:

summation : Int -> Int
summation n =
    List.range 1 n |> List.sum
Enter fullscreen mode Exit fullscreen mode

I saw some other interesting ones. I know which approach I prefer.

summation : Int -> Int
summation n =
  if modBy 2 n == 0 then
    n // 2 * (n + 1)
  else
    (n - 1) // 2 * (n + 1) + (n  + 1) // 2
Enter fullscreen mode Exit fullscreen mode
summation : Int -> Int
summation n = round (toFloat n / toFloat 2 * toFloat (n + 1))
Enter fullscreen mode Exit fullscreen mode

4. Get a list of 1 to n (also with partial application example)

The next one seemed even simpler one than the previous, but I decided to try it anyway. I was confident my solution would be the shortest and simplest.

Count the monkeys!
You take your son to the forest to see the monkeys. You know that there are a certain number there (n), but your son is too young to just appreciate the full number, he has to start counting them from 1.

As a good parent, you will sit and count with him. Given the number (n), populate an array with all numbers up to and including that number, but excluding zero.

For example:
monkeyCount(10) // --> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
monkeyCount(1) // --> [1]

And here's my solution. Easy!

monkeyCount : Int -> List Int
monkeyCount x = List.range 1 x
Enter fullscreen mode Exit fullscreen mode

But then to my surprise and delight, I found an even shorter solution! You can use partial application:

monkeyCount2 : Int -> List Int
monkeyCount2 = List.range 1
Enter fullscreen mode Exit fullscreen mode

To understand what's going on, let's look at the type annotation for List.range:

range : Int -> Int -> List Int

So when we store List.range 1, we've supplied 1 argument, and another one remains. What we get is a function that looks like this:

monkeyCount2 : Int -> List Int

I still think my solution is easier to read, but it's a good example of partial application.

Then you have examples that go the other way. No comment:

monkeyCount : Int -> List Int
monkeyCount = monkeyCountHelper []

monkeyCountHelper : (List Int) -> Int -> List Int
monkeyCountHelper c x = 
  case x of
    0 ->
      c
    _ ->
      monkeyCountHelper (x :: c) (x-1)
Enter fullscreen mode Exit fullscreen mode

5. Time since midnight in milliseconds

I eventually want to deal with time in my astronomy project. I've already written the most important functions, but it's good to just get this stuff into my head.

Given a number of hours, minutes and seconds, return the number of milliseconds that have passed since midnight.

  • Seconds to milliseconds = seconds * 1000
  • Minutes to milliseconds = minutes * 1000 * 60
  • Hours to milliseconds = hours * 1000 * 60 * 60

Going by this, I decided to submit this solution:

past : Int -> Int -> Int -> Int
past h m s = 
    (s * 1000) 
  + (m * 1000 * 60) 
  + (h * 1000 * 60 * 60)
Enter fullscreen mode Exit fullscreen mode

I could have written 60000 and 3600000, but again, I think that affects readability.

By far the most interesting solution was this one:

expand : Int -> Int
expand =
  (*) 60

past : Int -> Int -> Int -> Int
past h m s =
  h
    |> expand
    |> (+) m
    |> expand
    |> (+) s
    |> (*) 1000
Enter fullscreen mode Exit fullscreen mode

:D

6. Formatting large numbers

The resulting number of milliseconds since midnight is a big number. These are hard to read, so it's nice to add some formatting, like this:

const n = 1234567890
const formatted = n.toLocaleString("NO") // "1 234 567 890"
Enter fullscreen mode Exit fullscreen mode

I found cuducos/elm-format-number and included it in my project.

First I did elm install cuducos/elm-format-number, and then these were the imports I used:

import FormatNumber exposing (format)
import FormatNumber.Locales exposing (Decimals(..), usLocale)
Enter fullscreen mode Exit fullscreen mode

At first, I was disappointed that it adds .00 to the end by default, but that's easy to fix:

-- 49,062,000.00
toFloat (millisecondsSinceMidnight 13 37 42)
    |> format usLocale


-- 49,062,000
toFloat (millisecondsSinceMidnight 13 37 42)
    |> format usLocale
Enter fullscreen mode Exit fullscreen mode

7. Remove all spaces from a string

In advance, I think this should just be split " " |> join "" or something like that.

Here's what these two functions do:

split " " will add a new list item for every space it comes across.

"cat dog human" becomes ["cat", "dog", "human"]. The spaces aren't included - they're used as delimiters.

String.split "***" "a***b***c" becomes ["a", "b", "c"].

String.join turns an array of strings into one string, and adds a character (or none) between each item. String.join "-" ["o", "m", "g"] becomes "o-m-g".

Instead of the dash, you can also pass an empty string, like I did:

noSpace : String -> String
noSpace s =
    s |> String.split " " |> String.join ""
Enter fullscreen mode Exit fullscreen mode

After submitting my solution, I saw a more elegant approach:

noSpace2 : String -> String
noSpace2 s =
    s |> String.replace " " ""
Enter fullscreen mode Exit fullscreen mode

JavaScript has this functionality as well, but I keep forgetting about it. :)

"a b c".replaceAll(" ", "-") // a-b-c
Enter fullscreen mode Exit fullscreen mode

8. Conclusion

I really enjoyed practicing some basics, and I was excited to discover a couple of solutions I wouldn't have thought of.

See you tomorrow!

Top comments (2)

Collapse
 
g4bb3r profile image
Gabriel Torrecillas Sartori

Nice! Time to get real world challenges. I suggest learning how to use elm-spa, elm-ui, elm/json and elm/http. Just a reminder that you will probably start to hate building SPA in another stack ;)

Collapse
 
kristianpedersen profile image
Kristian Pedersen

Thanks! Yeah, making some more fully-featured projects is my goal after these 30 days.

I've tried a couple of the things you mention, but I'm very interested in understanding them properly. I've seen some people saying that elm-spa taught them a lot.

Realistically, I understand that when I get a job, it's likely to involve a lot of TypeScript and React, but at least I hope to have some good habits from Elm. :)