{-# LANGUAGE Safe #-}

{-|
Module      : Data.List.Group
Description : A module to group elements of a list together in sequences.
Maintainer  : hapytexeu+gh@gmail.com
Stability   : experimental
Portability : POSIX

A module that defines functions that groups a list of elements into subsequences.
-}

module Data.List.Group (
  -- * Grouping with the key
    groupWith, groupWith'
  ) where

import Data.List.NonEmpty(NonEmpty((:|)))

-- | Group the given list of items to a list of 'NonEmpty' 2-tuples with the
-- "key" and the value for a given mapping function and equavalence relation.
groupWith'
  :: (b -> b -> Bool)  -- ^ The given equivalence relation.
  -> (a -> b)  -- ^ The given mapping function.
  -> [a]  -- ^ The list of items to group into non-empty sequences.
  -> [(b, NonEmpty a)]  -- ^ The list of groups.
groupWith' :: (b -> b -> Bool) -> (a -> b) -> [a] -> [(b, NonEmpty a)]
groupWith' eq :: b -> b -> Bool
eq f :: a -> b
f = [a] -> [(b, NonEmpty a)]
go
    where go :: [a] -> [(b, NonEmpty a)]
go [] = []
          go ~(x :: a
x:xs :: [a]
xs) = (b
fx, a
x a -> [a] -> NonEmpty a
forall a. a -> [a] -> NonEmpty a
:| [a]
ys) (b, NonEmpty a) -> [(b, NonEmpty a)] -> [(b, NonEmpty a)]
forall a. a -> [a] -> [a]
: [a] -> [(b, NonEmpty a)]
go [a]
zs
              where ~(ys :: [a]
ys, zs :: [a]
zs) = (a -> Bool) -> [a] -> ([a], [a])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span (b -> b -> Bool
eq b
fx (b -> Bool) -> (a -> b) -> a -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> b
f) [a]
xs
                    fx :: b
fx = a -> b
f a
x

-- | Group the given list of items to a list of 'NonEmpty' 2-tuples with the
-- "key" and the value for a given mapping function.
groupWith :: Eq b
  => (a -> b)  -- ^ The given mapping function.
  -> [a]  -- ^ The list of items to group into non-empty sequences.
  -> [(b, NonEmpty a)]  -- ^ The list of groups.
groupWith :: (a -> b) -> [a] -> [(b, NonEmpty a)]
groupWith = (b -> b -> Bool) -> (a -> b) -> [a] -> [(b, NonEmpty a)]
forall b a.
(b -> b -> Bool) -> (a -> b) -> [a] -> [(b, NonEmpty a)]
groupWith' b -> b -> Bool
forall a. Eq a => a -> a -> Bool
(==)