Search code examples
elm

how to add class to all columns of a table in elm script


The elm scripts gets a csv string csv from backend code. I want to create a table and the class name of each cell should be the column name.

The rows of the table can be obtained from rows = String.split "\n" csv. If I don't need to define classes, a table can be created by applying doubly nested List.map on rows. But in order to give class to each cell, I need to save the first element of row which contains the column (or class) names and pair it with each of the remaining rows. Since there is no for loop in elm, my strategy is to define a recursive function to create all tr elements and concatenate the tr elements using List.map. The input of this function is the first row called names and another row called cols. They are both List String. Each time it runs, it creates a td using the heads (from List.head) of the two Lists and then pass the remaining elements of the two lists (from List.drop) for recursion. But I couldn't concatenate the two returned components.

createRow names cols =
    case cols of
        [] -> text ""
        _ ->
            let
                c0 = (List.head cols)
                c = (List.drop 1 cols)
                n0 = (List.head names)
                n = (List.drop 1 names)
            in
                td [] [text <| toString <| c0]
                , createRow n c 

What should I put in the in block? Or is there a better way to achieve my purpose?


Solution

  • In a recursive function, you need to pass the (partially-computed) result along, so that you can modify it in each call, and return it once you've finished recursing. So that might look something like this:

    createRow : List String -> List String -> List (Html msg) -> List (Html msg)
    createRow names cols cells =
        case cols of 
            [] -> cells
            col :: remainingCols ->
                let
                    name = 
                        names
                        |> List.head
                        |> Maybe.withDefault ""
                    remainingNames = List.drop 1 names
                    cell =
                        td [class name] [text col]
                in
                createRow remainingNames remaningCols (cells ++ [ cell ])
    

    A couple of other notable changes here:

    • I've used pattern matching to extract the head of the cols list from the rest (also called the tail)
    • List.head returns a Maybe a (in this case, a is String), so I've added a call to Maybe.withDefault
    • Since cols is a List String, you don't need to call toString and can pass it directly to text
    • When you call this function for the first time, you'll pass in [] for the cells argument