views:

247

answers:

3

Hello, i have a list like this

["peter","1000","michell","2000","kelly","3000"]

and i would like to convert to

[("peter",1000),("michell", 2000),("kelly",3000)]

Please help. Thanks.

+7  A: 
cnv :: [String] -> [(String, Integer)]
cnv [] = []
cnv (k:v:t) = (k, read v) : cnv t

If you want to handle odd-length just add cnv [x] = variant before last one

ony
Can you explain this lines of code cnv (k:v:t) = (k, read v) : cnv t ? AFAIK, the k v t is a parameter from list k is the head and v is the tail and cnv t is next element as k. Is my understanding correct because previosuly i have function like this. convert :: [String] -> [(String, Integer)]convert [] = []convert (x:y:xs) = (x, y)convert xsand why when i add :xs it is not working ? Are the k v t is represents a single element in list or whole list ?
peterwkc
Not quite: in `k:v:t`, `k` is the head, and `v:t` is the tail. Thus, `k:v:t` puts the first two items of the list into `k` and `v` and the remaining tail in `t`. Your code has two obvious problems: (a) `(x,y)` has type `(String,String)`, not `(String,Integer)`; and (b) there's no colon before `convert xs`. (You can't just do `:xs`, because you need `[(String,Integer)]` but `xs` has type `[String]`.) Also, a formatting tip: indent lines with four blank spaces to get code blocks (or select your code and click the "101010" button), and surround code snippets with backticks (\`...code...\`).
Antal S-Z
(x:xs) That means x is the head and remaining element is tail for xs. Thanks for your explanation.
peterwkc
@Antal S-Z is right. `(k:v:t)` equivalent to `(k:(v:t))` because of declaration `infixr 5 :` in [`GHC.Types`](http://hackage.haskell.org/packages/archive/ghc-prim/0.1.0.0/doc/html/src/GHC-Types.html) (as well `data [] a = [] | a : [a]` will tell you about meaning of `(v:t)` or `[]`).
ony
(or select your code and click the "101010" button), and surround code snippets with backticks (`...code...`). Any example. How to do that manually ? I don't understand what you say here. (k:v:t) equivalent to (k:(v:t)) because of declaration infixr 5 : in GHC.Types (as well data [] a = [] | a : [a] will tell you about meaning of (v:t) or [])
peterwkc
The `infixr 5 :` statement says that `:` groups to the right with precedence 5. In other words, `w:x:y:z` is `w:(x:(y:z))`, not `((w:x):y):z`. The `5` tells you where it falls in the precedence chain; for instance, since `*` comes before `+`, `*` has precedence 7 whereas `+` has precedence 6. What the `data [] a = [] | a : [a]` declaration tells you is the definition of `:` and `[]`; that is, that they construct parts of lists, etc. As for formatting: just type \`before your code, and \` after it. Read [the editing help](http://stackoverflow.com/editing-help) for more.
Antal S-Z
The only problem with this function is non-exhaustive pattern matching. If the list has an odd number of elements, an error happens.
jetxee
+4  A: 

ony's solution is a bit shorter, but here's a non-recursive version using splitEvery from the very handy split library:

cnv = map (\[name, amount] -> (name, read amount :: Int)) . splitEvery 2

The steps here are somewhat clearer (for me, at least) than in the recursive version.

Travis Brown
This is definitely more natural. The split library doesn't get enough love. It's too bad, since it's incredibly useful.
Antal S-Z
+1  A: 

Exactly for a task like this I find it convenient to have a stride function to take every n-th element from the list:

stride _ [] = []
stride n (x:xs) = x : stride n (drop (n-1) xs)

It can be used to convert a list to pairs:

toPairs xs = zip (stride 2 xs) (stride 2 (drop 1 xs))

An example (note that the last element may be thrown away if it doesn't have pair):

ghci> stride 2 [1..5]
[1,3,5]
ghci> toPairs [1..7]
[(1,2),(3,4),(5,6)]

It can be even easily extended to triplets or longer tuples:

toTriplets xs = zip3 as bs cs
  where as = stride 3 xs
        bs = stride 3 $ drop 1 xs
        cs = stride 3 $ drop 2 xs

To perform conversion from String to integer in your example, you can map read function over the second stride:

let lst = ["peter","1000","michell","2000","kelly","3000"] in
zip (stride 2 lst) (map read . stride 2 . drop 1 $ lst) :: [(String,Int)]

which gives:

[("peter",1000),("michell",2000),("kelly",3000)]
jetxee