You can use groupBy
to keep the groups of tuples apart:
import Data.Function (on)
import Data.Ord (comparing)
import Data.List
possible_keys :: Ord k => [(k, [c])] -> [[c]]
possible_keys = concat . fmap sequenceA . fmap (fmap snd)
. groupBy ((==) `on` fst) . sortBy (comparing fst)
(I'm assuming the input list isn't necessarily sorted. I'm sorting by the keys alone to make it minimally invasive. comparing fst
is the same as compare `on` fst
; unfortunately there isn't an analogous equating
in the standard library yet. sequenceA
is the same as sequence
, only more general.)
This can be slimmed down a bit. The second functor law allows us to combine consecutive uses of fmap
:
possible_keys :: Ord k => [(k, [c])] -> [[c]]
possible_keys = concat . fmap (sequenceA . fmap snd)
. groupBy ((==) `on` fst) . sortBy (comparing fst)
fmap
followed by sequenceA
is traverse
:
possible_keys :: Ord k => [(k, [c])] -> [[c]]
possible_keys = concat . fmap (traverse snd)
. groupBy ((==) `on` fst) . sortBy (comparing fst)
Finally, fmap
followed by concat
on a list is concatMap
(or (=<<)
, but that would be a little too cryptic here):
possible_keys :: Ord k => [(k, [c])] -> [[c]]
possible_keys = concatMap (traverse snd)
. groupBy ((==) `on` fst) . sortBy (comparing fst)
Note that this will generate strings of length one for keys with just one tuple -- sequenceA ["abc"]
is ["a","b","c"]
. If you don't want that, you can filter
the groups of tuples immediately after the groupBy
to get rid of those with just one tuple.