views:

624

answers:

4

I want to print list of strings like shown below.

|Name|Country|Age|
------------------
|1   |USA    |20 |
|2   |UK     |19 |

I was able to achieve this using the following.

printfieldName :: [String] -> String
printfieldName [] = []
printfieldName (x:xs)  = "|" ++ x ++ "\t" ++ printfieldName (xs)

Is it possible to achieve this using the inbuilt function 'unwords'. I was able print it using 'unwords' but was unable to place | between the words.

+3  A: 

Firstly, I would write it like this:

printfieldName []     = []
printfieldName (x:xs) = "|" ++ x ++ "\t" ++ printfieldName xs

Well, actually, no, like this:

concatMap (\x -> '|' : x ++ "\t")

Well, maybe more like:

concatMap (printf "|%s\t")

OK. So can it be done as 'unwords'?

-- | 'unwords' is an inverse operation to 'words'.
-- It joins words with separating spaces.
unwords                 :: [String] -> String
unwords []              =  ""
unwords ws              =  foldr1 (\w s -> w ++ ' ':s) ws

No. But see if you can write concatMap as a foldr...

Don Stewart
i tried this right now, i get an error when i executeSyntax error in input (unexpected backslash (lambda))printfieldName :: [String] -> StringprintfieldName []=[]concatMap (\x -> '|' : x ++ "\t")concatMap (printf "|%s\t")thanks
pier
@dlna need more infoPrelude Text.Printf> putStrLn $ concatMap (printf "|%s\t") (map (:[]) "hello")|h |e |l |l |o
Don Stewart
+3  A: 

I see there is a additional space between '|' and word, so you can use this function:

printfieldName x = unwords (map ((++) "|") x)  ++ "|"

little explanation:

(++) "|" - creates a function which take prefixes each word with "|", so
(++) "|" "test" -> "|test"

then, map applies this function to a list of words converting it to ["|1", "|USA", "|20", ... ]

then unwords joins them into a string with spaces between words. The ++ "|" is needed to add final |

Ruslan Abdulkhalik
im new to haskell, could you please explain the flow of the function u've written. It works! :)thanks
pier
Cool!(++) "|" creates a function which take prefixes each word with "|", so (++) "|" "test" -> "|test"then, `map' applies this function to a list of words converting it to ["|1", "|USA", "|20", ... ] then `unwords' joins them into a string with spaces between words. The `++ "|"' is needed to add final |
Ruslan Abdulkhalik
sorry, all newlines in the comments were lost. I am new here :)
Ruslan Abdulkhalik
Yeah, comments really don't have formatting to speak of. It's generally better to add explanations by editing the post.
Chuck
+4  A: 

Data.List has a function called intersperse. Perhaps you can use that.

printfieldName xs = "|" ++ unwords (intersperse "|\t" xs) ++ "|"
Apocalisp
A: 

Perhaps a little over the top from what you asked, but:

formatTable :: [String] -> [[String]] -> String
formatTable header rows =
    formatRow header ++ dashRow ++ concatMap formatRow rows

    where formatRow cells = bracket '|' (spread cells)
          dashRow         = bracket '+' (map (\n -> replicate n '-') widths)
          bracket c cells = concatMap (c:) cells ++ (c:"\n")

          spread cells    = zipWith pad widths cells
          pad n s         = take n (s ++ repeat ' ')

          widths = foldr maxLengths (repeat 0) (header : rows)
          maxLengths = zipWith (\c l -> max (length c) l)

Then, for example:

> let h = words "Name Country Age"
> let rows = map words ["1 USA 20", "2 UK 19"]
> h
["Name","Country","Age"]
> rows
[["1","USA","20"],["2","UK","19"]]
> putStr $ formatTable h rows
|Name|Country|Age|
+----+-------+---+
|1   |USA    |20 |
|2   |UK     |19 |
MtnViewMark