I would like to output a table in html format. Basically I would like something like :
[[a]] -> <table>
What is the easiest way to do so ?
The easiest way to generate Html is probably blaze:
import Text.Blaze.Html5 (table, td, tr, toHtml, ToMarkup, Html)
import Control.Monad (forM_, mapM_)
myTable :: (ToMarkup a) => [[a]] -> Html
myTable xs = table $ forM_ xs (tr . mapM_ (td . toHtml))
Note that you need to use renderHtml from Text.Blaze.Renderer.* to get a ByteString, String or Text.
Edit: During writing the answer @Zeta already posted a better solution using blaze-html. So I recommend using his solution (see section "Words of Warning" for the listening of this solutions disadvantages...) ;-)
Here is an implementation:
-- file test.hs:
insideTag :: String -> String -> String
insideTag tag content = "<" ++ tag ++ ">" ++ content ++ "</" ++ tag ++ ">"
toTable :: Show a => [[a]] -> String
toTable = insideTag "table" . concatMap (insideTag "tr") . map (concatMap (insideTag "td" . show))
main :: IO ()
main = do
putStrLn $ toTable [[1,2,3],[4,5,6],[7,8,9]]
return ()
The command runhaskell test.hs will now print
<table><tr><td>1</td><td>2</td><td>3</td></tr><tr><td>4</td><td>5</td><td>6</td></tr><tr><td>7</td><td>8</td><td>9</td></tr></table>
insideTag encapsulates content inside a html tag:
ghci> let insideTag tag content = "<" ++ tag ++ ">" ++ content ++ "</" ++ tag ++ ">"
ghci> insideTag "h1" "hello world"
"<h1>hello world</h1>"
map (concatMap (insideTag "td" . show)) list encapsulate the inner elements into <td> tags and concatenate them:
ghci> map (concatMap (insideTag "td" . show)) [[1,2], [3,4]]
["<td>1</td><td>2</td>","<td>3</td><td>4</td>"]
The same can be done for the outer list:
ghci> concatMap (insideTag "tr") ["<td>1</td><td>2</td>","<td>3</td><td>4</td>"]
"<tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr>"
The last string only has to be encapsulate into a <table> tag:
ghci> insideTag "table" "<tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr>"
"<table><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></table>"
The above code uses the normal [Char] type for strings which is not memory efficient. So I recommend that you use Data.Text if you deal with big tables (toTable remains the same, you just have to change show to pack . show; insideTag has to reimplemented for Data.Text).
There is also no HTML escaping for the table content!!! So the above code is vulnerable to XSS attacts. So do not use the above code, if the produced HTML shall be included in a website (especially if the website user has an influence on the table content)!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With