Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elm -- parse text file into Html

I'm very new to Elm (and newish to FP in general) I'm having some trouble wrapping my head around how to do certain things.

I'm using ports currently to read in a text file and pass it off to elm (index.html):

<script type="text/javascript">
  // Show the stamp module in the "elm-div" div.
  var div = document.getElementById('elm-div');
  var golf = Elm.embed(Elm.Golf, div, { reset:[], openFromFile: "" });

  var upload = document.getElementById('fileinput');

  upload.onchange = function (e) {
      reader = new FileReader();

      reader.onload = function (event) {
          data = event.target.result;
          //file's text data is sent to 'openfromfile' port
          golf.ports.openFromFile.send(data);
          }
      reader.readAsText(upload.files[0]);
      };
</script>

Golf.elm (so far):

module Golf where

import Html exposing (Html, Attribute, text, toElement, div, input)
import Html.Attributes exposing (..)
import Color exposing (..)
import Signal exposing ((<~),(~))
import String

port reset : Signal ()   
port openFromFile : Signal String

getLines : Signal (List String)
getLines = String.lines <~ openFromFile

I'm having trouble thinking about how the Golf.elm file should be structured. I have text data in a CSV format (delimited by ',') where:

"Round Number", "Par", "steve", "kyle", "rick"
1, 3, 5, 8, 1
2, 5, 3, 7, 8
3, 4, 6, 5, 4
4, 3, 2, 4, 3
5, 2, 5, 7, 4

What I want to do is read the CSV and show an html table with the scores relative to par for each player/round (score= number - par) but The fact that I'm starting not with a regular model in record format but a Signal (List String) has me completely lost.

I sent my getLines back through ports to console.log it so I know I'm reading the file correctly and generating a Signal (List String) properly from the text source but I have no where to go from here.

like image 254
leshow Avatar asked Jun 20 '26 02:06

leshow


1 Answers

Explanation

You may start with types:

type alias CSV = { headers : Maybe (List String)
                 , records : List  (List String)
                 }

You have:

getLines : Signal (List String)

but needs:

getCSV   : Signal CSV

Use core Signal.map:

map : (a -> result) -> Signal a -> Signal result

Then type signature will be (a = List String, result = CSV):

map0     : (List String -> CSV) -> Signal (List String) -> Signal CSV

Missing part is:

parseCSV : List String -> CSV

Combine all things together:

getCSV : Signal CSV
getCSV = Signal.map parseCSV getLines

Result

-- ...

getCSV : Signal CSV
getCSV = Signal.map badParseCSV getLines

type alias CSV = { headers : Maybe (List String)
                 , records : List  (List String)
                 }

badParseCSV : List String -> CSV
badParseCSV xs =
  let parseLine = List.map (trimQuotes << String.trim)
              <<  String.split ","
      trimQuotes x = if String.startsWith "\"" x 
                     && String.endsWith "\"" x
                     then String.dropRight 1 <| String.dropLeft 1 x
                     else x
      records0 = List.map parseLine
              <| List.filter (\x -> not (String.isEmpty x))
              <| List.drop 1 xs
      headers0 = Maybe.map parseLine <| List.head xs
  in  { headers = headers0
      , records = records0}

view : CSV -> Html
view csv =
  let rows    = List.map (\xs -> Html.tr [] (cols xs)) csv.records
      cols xs = List.map col xs
      col  x  = Html.td [] [ text x]
      ths  xs = List.map (\x -> Html.th [] [text x]) xs
      headers = Maybe.withDefault [] <| Maybe.map ths csv.headers
  in Html.table [] [ Html.thead [] headers
                   , Html.tbody [] rows
                   ]

main : Signal Html
main = Signal.map view getCSV
like image 99
Alex Voikov Avatar answered Jun 22 '26 16:06

Alex Voikov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!