Akos Fajszi
Akos Fajszi

Reputation: 83

Haskell compression on list of lists

I imagined the following code:

compress :: [[a]] -> [(Int,a)]
compress  [[]] = []
compress  [(x:xs)] = (1 + length (takeWhile x xs), x) : compress [(dropWhile x xs)]

I want to count each element in a list of lists. There are identical elements in each list, for example: [[1,1,1], [2,2]].

I can only achieve the following output:

[(1,[1,1,1]), (1,[2,2])]

, but what I really need is this:

[(3,1), (2,2)].

I can't get deep enough to count the element in each list and probably made it more complicated with takeWhile dropWhile, hence each list has the same element in it.

Upvotes: 0

Views: 696

Answers (2)

chepner
chepner

Reputation: 531075

What you want is to

  1. Filter out empty lists
  2. Apply length and head to the remaining lists
  3. Collect the results in the final output.

Control.Arrow provides a useful operator &&& which, when specialized to functions, looks like

f &&& g = \x -> (f x, g x)

With that, you can simply write

compress = map (length &&& head) . filter (not . null)

Using a list comprehension, it's just

compress xs = [(length x, y) | x@(y:_) <- xs]

The pattern match implicitly filters out the empty lists while extracting the first element without a call to head.

Upvotes: 3

Redu
Redu

Reputation: 26161

This seems to be a better fit for a folding job since conditionally you will drop some of the sublists (ie when length is 0). One approach could be

Prelude> let comp = foldr (\s r -> if null s then r else (length s, head s):r) []
Prelude> comp [[1,1,1],[2,2,2],[],[5,5,5,5,5,5]]
[(3,1),(3,2),(6,5)]

Upvotes: 2

Related Questions