Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

groupBy with multiple test functions

Tags:

list

haskell

Is there a better and more concise way to write the following code in Haskell? I've tried using if..else but that is getting less readable than the following. I want to avoid traversing the xs list (which is huge!) 8 times to just separate the elements into 8 groups. groupBy from Data.List takes only one test condition function: (a -> a -> Bool) -> [a] -> [[a]].

x1 = filter (check condition1) xs
x2 = filter (check condition2) xs
x3 = filter (check condition3) xs
x4 = filter (check condition4) xs
x5 = filter (check condition5) xs
x6 = filter (check condition6) xs
x7 = filter (check condition7) xs
x8 = filter (check condition8) xs
results = [x1,x2,x3,x4,x5,x6,x7,x8]
like image 559
vis Avatar asked Dec 03 '22 04:12

vis


1 Answers

This only traverses the list once:

import Data.Functor
import Control.Monad

filterN :: [a -> Bool] -> [a] -> [[a]]
filterN ps =
    map catMaybes . transpose .
    map (\x -> map (\p -> x <$ guard (p x)) ps)

For each element of the list, the map produces a list of Maybes, each Maybe corresponding to one of the predicates; it is Nothing if the element does not satisfy the predicate, or Just x if it does satisfy the predicate. Then, the transpose shuffles all these lists so that the list is organised by predicate, rather than by element, and the map catMaybes discards the entries for elements that did not satisfy a predicate.

Some explanation: x <$ m is fmap (const x) m, and for Maybe, guard b is if b then Just () else Nothing, so x <$ guard b is if b then Just x else Nothing.

The map could also be written as map (\x -> [x <$ guard (p x) | p <- ps]).

like image 123
ehird Avatar answered Dec 28 '22 02:12

ehird