DEV Community

Kristian Pedersen
Kristian Pedersen

Posted on

#30daysofelm Day 25: Displaying a List (List String)

This is day 25 of my 30 day Elm challenge

Today we'll create a very basic chess board view from a 8x8 List.

This is the final result:

A8B8C8D8E8F8G8H8
A7B7C7D7E7F7G7H7
A6B6C6D6E6F6G6H6
A5B5C5D5E5F5G5H5
A4B4C4D4E4F4G4H4
A3B3C3D3E3F3G3H3
A2B2C2D2E2F2G2H2
A1B1C1D1E1F1G1H1
Enter fullscreen mode Exit fullscreen mode

Demo/code: https://ellie-app.com/c3QTnvkpn7ha1

1. Creating a single row

This is easy in Elm:

List.range 1 8 -- [1, 2, 3, 4, 5, 6, 7, 8]
Enter fullscreen mode Exit fullscreen mode

If you want the list to contain something else, you can use List.map:

List.range 1 8 
    |> List.map (\item -> "lol") -- ["lol", "lol", "lol", "lol", "lol", "lol", "lol", "lol", ]
Enter fullscreen mode Exit fullscreen mode

2. Creating a grid (list of rows)

Instead of having a list of numbers, we can imagine each number being replaced with a List:

verySimpleGrid : List (List Int)
verySimpleGrid =
    List.range 1 8 -- [1, 2, 3, 4, 5, 6, 7, 8]
        |> List.map (\number -> List.range 1 8)
        -- Each individual number in the previous list is replaced with [1, 2, 3, 4, 5, 6, 7, 8]
        -- [[1, 2, 3, 4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7, 8], etc]
Enter fullscreen mode Exit fullscreen mode

3. Creating a grid with chess position names (A1, A2, .. H8)

This was my initial approach:

letters : String
letters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"


grid : List (List String)
grid =
    List.range 1 8
        |> List.map (\row -> List.range 1 8)
        |> List.indexedMap
            (\y row ->
                List.map
                    (\x ->
                        String.slice (x - 1) (x + 0) letters ++ String.fromInt (y + 1)
                    )
                    row
            )
        |> List.reverse
Enter fullscreen mode Exit fullscreen mode

letters is one of my favorite hardcoded things I've done. Very nice!

The first two lines of the grid function are the same as before.

List.indexedMap works the same way as JavaScript's regular Array.map, except it's index first, and then element.

  1. First, we're getting the Y index and its row (List String).
  2. Then, using List.map on row, we get each X value.
  3. The X coordinates should go from A to H. This is done with String.slice, which cuts from index1 up to (not including) index2.
  4. The Y coordinates work as usual, except chess board have 1-based indexing.
  5. The rows are reversed, so that A1 isn't at the top of the board.

4. Refactoring: Named functions look nicer than long anonymous ones

Looking at the code above, all that indentation is awkward to read, and none of the functions passed to List.map or List.indexedMap are reusable.

I get shivers from anonymous functions that span more than one line. I'm feeling lazy today, but I'm doing this just so I can sleep well at night.

letters : String
letters =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"


singleCell : Int -> Int -> String
singleCell x y =
    String.slice (x - 1) (x + 0) letters ++ String.fromInt (y + 1)


chessRow : Int -> List Int -> List String
chessRow y row =
    row |> List.map (\x -> singleCell x y)


grid : List (List String)
grid =
    List.range 1 8
        |> List.map (\row -> List.range 1 8)
        |> List.indexedMap chessRow
        |> List.reverse
Enter fullscreen mode Exit fullscreen mode

I think that is so much nicer to read, I'm actually a bit impressed at how much of a difference that makes.

5. Creating the view

All that's left is getting the grid displayed in the browser. I guess I could have done nested List.maps, but again, I think separate functions look nicer.

First, we need to put each row into one div.

Then those divs all get put into one main div.

rowDiv : List String -> Html a
rowDiv row =
    div [] (row |> List.map (\boardPosition -> text boardPosition))


main : Html a
main =
    div [] (grid |> List.map rowDiv)
Enter fullscreen mode Exit fullscreen mode

Looking good. See you tomorrow!

Top comments (0)