ndmitchell / extra

Extra Haskell functions
Other
94 stars 37 forks source link

`groupOnKey :: Eq b => (a -> k) -> [a] -> [(k, [a])]` #93

Closed sullyj3 closed 2 years ago

sullyj3 commented 2 years ago

What

A variant of groupOn that pairs each group with its key. Compare:

groupOn    :: Eq b => (a -> k) -> [a] -> [    [a]]
groupOnKey :: Eq b => (a -> k) -> [a] -> [(k, [a])]

Why

Wanting access to the key while processing the group is a very common use case. The idea is to emulate the ergonomics of python's itertools.groupby

for key, group in groupby(collection, keyfunc):
  print(f"{key}:")
  for item in group:
    print(f"  {item}")
for_ (groupOnKey keyFunc collection) $ \(key, group) -> do
  putStrLn $ show key <> ":"
  for_ group (putStrLn . ("  "<>) . show)

-- Existing solution using groupOn
for_ (groupOn keyFunc collection) $ \group -> do
  -- this is very common boilerplate.
  -- it feels kind of ugly to have to manually pull the key out of the first element of each group
  -- the partial head call is unfortunate, even though it's safe here
  let key = keyFunc $ head group
  putStrLn $ show key <> ":"
  for_ group (putStrLn . ("  "<>) . show)

I'm not wedded to the name. What do you think?

ndmitchell commented 2 years ago

Sounds very reasonable - usually the key computation is cheap (since you have to apply it a relatively large amount of times), but this saves you from having to do the nasty head to retrieve it. PR welcome!