Hamza Halabi
Hamza Halabi

Reputation: 21

Haskell convert string of char and int to a paired list

Hi guys I am looking for a function

expand :: String -> [(Char,Int)]

that takes a string of character and numbers like "a5b3c2" and change it to a paired list of the string like "[('a',5),('b',3),('c',2)]" in that same form.

Example:

expand "a5b4c2"
[('a',5),('b',4),('c',2)]

expand "d9d3"
[('d',9),('d',3)]

I had already made a function that does the opposite of the above, all I am trying to do is figure out how to do the inverse of that. Example:

flatten :: [(Char, Int)] -> String
flatten [] = []
flatten ((ca,cb):cs) = ca:(show cb ++ flatten cs)

Upvotes: 1

Views: 2152

Answers (5)

fp_mora
fp_mora

Reputation: 714

Again, I prefer list comprehensions and/or tail recursion functions. If a function is not a fold then it returns a list. List comprehensions return lists.

let l = "a5b3c2"
[ (a,(read [b] :: Int)) | (a,b) <- zip l (tail l), elem b "1234567890"]

[('a',5),('b',3),('c',2)]

I didn't know about the chunksOf function when I posted this. chunksOf is a very handy function and I'm already using it a lot. Also I think I like the ord function over the read function. When you have pairs, you know where things are at and can process accordingly.

[ (a,(ord b)-48) | (a:b:c) <- chunksOf 2 "a5b4c3"]

Upvotes: 0

castle-bravo
castle-bravo

Reputation: 1429

A relatively simple solution:

import Data.Char (digitToInt)

expand :: String -> [(Char, Int)]
expand (x:x':xs) = (x, digitToInt x'):expand xs
expand _        = []

Upvotes: 0

Franky
Franky

Reputation: 2376

Without any complicated imports, assuming alternating chars and (single) digits, you can use a simple recursion:

f :: [(Char, Int)] -> String
f (c:d:xs) = (c,read [d]):f xs
f x        = x

Upvotes: 0

chepner
chepner

Reputation: 531265

Use a parsing library like Parsec. The learning curve is a little steep (I'm not even sure this is a good example), but you can describe parsers like this is very little code.

import qualified Text.Parsec as T

parseDigit :: T.Parsec String () Int
parseDigit = fmap (read . pure) T.digit

myParser = T.many ((,) <$> T.letter <*> parseDigit)

Then

> T.parse myParser "" "a5b4c2"
Right [('a', 5),('b',4),('c',2)]

So, your expand could be defined as

import Data.Either
expand :: String -> [(Char, Int)]
expand s = fromRight [] (T.parse myParser "" s)

to return an empty list in the event the parser fails on the input string.

Upvotes: 2

St&#233;phane Laurent
St&#233;phane Laurent

Reputation: 84529

Is it ok like this?

import Data.List (intersperse)
import Data.List.Split (splitPlaces, wordsBy)
mystring = "a5b2c3'

>>> map (\[x,y] -> (head x, read y :: Int)) $ splitPlaces (replicate (length mystring `div` 2) 2) $ wordsBy (==',') $ intersperse ',' mystring
[('a',5),('b',2),('c',3)]

Simpler, thanks to @4castle:

import Data.List (intersperse)
import Data.List.Split (chunksOf, wordsBy)
map (\[x,y] -> (head x, read y :: Int)) $ chunksOf 2 $ wordsBy (==',') $ intersperse ',' mystring

Even simpler, still thanks to @4castle:

import Data.List.Split (chunksOf)
map (\[x,y] -> (x, read [y] :: Int)) $ chunksOf 2 mystring

Upvotes: 2

Related Questions