diff --git a/CHANGELOG.md b/CHANGELOG.md index ef197a9..4f3e1cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ New features: Bugfixes: Other improvements: +- Improve consistency and clarity of docs and tests for `group*` functions (#193) - Fix Lazy List docs where original list is returned instead of Nothing (#169) - Migrate to GitHub Actions (#177) - Change `foldM` type signature to more closely match `foldl` (#165) diff --git a/src/Data/List.purs b/src/Data/List.purs index 360b256..08ffdd3 100644 --- a/src/Data/List.purs +++ b/src/Data/List.purs @@ -584,25 +584,23 @@ span _ xs = { init: Nil, rest: xs } -- | Group equal, consecutive elements of a list into lists. -- | --- | For example, --- | -- | ```purescript --- | group (1 : 1 : 2 : 2 : 1 : Nil) == --- | (NonEmptyList (NonEmpty 1 (1 : Nil))) : (NonEmptyList (NonEmpty 2 (2 : Nil))) : (NonEmptyList (NonEmpty 1 Nil)) : Nil +-- | group (3 : 3 : 2 : 2 : 1 : 3 : Nil) == +-- | NonEmptyList (3 :| 3 : Nil) : NonEmptyList (2 :| 2 : Nil) : NonEmptyList (1 :| Nil) : NonEmptyList (3 :| Nil) : Nil -- | ``` -- | -- | Running time: `O(n)` group :: forall a. Eq a => List a -> List (NEL.NonEmptyList a) group = groupBy (==) --- | Group equal elements of a list into lists. --- | --- | For example, +-- | Sort, then group equal elements of a list into lists. -- | -- | ```purescript --- | groupAll (1 : 1 : 2 : 2 : 1 : Nil) == --- | (NonEmptyList (NonEmpty 1 (1 : 1 : Nil))) : (NonEmptyList (NonEmpty 2 (2 : Nil))) : Nil +-- | groupAll (3 : 3 : 2 : 2 : 1 : 3 : Nil) == +-- | NonEmptyList (1 :| Nil) : NonEmptyList (2 :| 2 : Nil) : NonEmptyList (3 :| 3 : 3 : Nil) : Nil -- | ``` +-- | +-- | Running time: `O(n log n)` groupAll :: forall a. Ord a => List a -> List (NEL.NonEmptyList a) groupAll = group <<< sort @@ -610,14 +608,12 @@ groupAll = group <<< sort group' :: forall a. Warn (Text "'group\'' is deprecated, use groupAll instead") => Ord a => List a -> List (NEL.NonEmptyList a) group' = groupAll --- | Group equal, consecutive elements of a list into lists, using the specified --- | equivalence relation to determine equality. --- | --- | For example, +-- | Group equal, consecutive elements of a list into lists, using the provided +-- | equivalence function to determine equality. -- | -- | ```purescript --- | groupBy (\a b -> odd a && odd b) (1 : 3 : 2 : 4 : 3 : 3 : Nil) == --- | (NonEmptyList (NonEmpty 1 (3 : Nil))) : (NonEmptyList (NonEmpty 2 Nil)) : (NonEmptyList (NonEmpty 4 Nil)) : (NonEmptyList (NonEmpty 3 (3 : Nil))) : Nil +-- | groupBy (eq `on` (_ `div` 10)) (32 : 31 : 21 : 22 : 11 : 33 : Nil) == +-- | NonEmptyList (32 :| 31 : Nil) : NonEmptyList (21 :| 22 : Nil) : NonEmptyList (11 :| Nil) : NonEmptyList (33 :| Nil) : Nil -- | ``` -- | -- | Running time: `O(n)` @@ -626,17 +622,16 @@ groupBy _ Nil = Nil groupBy eq (x : xs) = case span (eq x) xs of { init: ys, rest: zs } -> NEL.NonEmptyList (x :| ys) : groupBy eq zs --- | Group equal elements of a list into lists, using the specified --- | equivalence relation to determine equality. --- | --- | For example, +-- | Sort, then group equal elements of a list into lists, using the provided comparison function. -- | -- | ```purescript --- | groupAllBy (\a b -> odd a && odd b) (1 : 3 : 2 : 4 : 3 : 3 : Nil) == --- | (NonEmptyList (NonEmpty 1 Nil)) : (NonEmptyList (NonEmpty 2 Nil)) : (NonEmptyList (NonEmpty 3 (3 : 3 : Nil))) : (NonEmptyList (NonEmpty 4 Nil)) : Nil +-- | groupAllBy (compare `on` (_ `div` 10)) (32 : 31 : 21 : 22 : 11 : 33 : Nil) == +-- | NonEmptyList (11 :| Nil) : NonEmptyList (21 :| 22 : Nil) : NonEmptyList (32 :| 31 : 33) : Nil -- | ``` -groupAllBy :: forall a. Ord a => (a -> a -> Boolean) -> List a -> List (NEL.NonEmptyList a) -groupAllBy p = groupBy p <<< sort +-- | +-- | Running time: `O(n log n)` +groupAllBy :: forall a. (a -> a -> Ordering) -> List a -> List (NEL.NonEmptyList a) +groupAllBy p = groupBy (\x y -> p x y == EQ) <<< sortBy p -- | Returns a lists of elements which do and do not satisfy a predicate. -- | diff --git a/src/Data/List/NonEmpty.purs b/src/Data/List/NonEmpty.purs index 9b71508..9f7d357 100644 --- a/src/Data/List/NonEmpty.purs +++ b/src/Data/List/NonEmpty.purs @@ -272,7 +272,7 @@ group' = groupAll groupBy :: forall a. (a -> a -> Boolean) -> NonEmptyList a -> NonEmptyList (NonEmptyList a) groupBy = wrappedOperation "groupBy" <<< L.groupBy -groupAllBy :: forall a. Ord a => (a -> a -> Boolean) -> NonEmptyList a -> NonEmptyList (NonEmptyList a) +groupAllBy :: forall a. (a -> a -> Ordering) -> NonEmptyList a -> NonEmptyList (NonEmptyList a) groupAllBy = wrappedOperation "groupAllBy" <<< L.groupAllBy partition :: forall a. (a -> Boolean) -> NonEmptyList a -> { yes :: L.List a, no :: L.List a } diff --git a/test/Test/Data/List.purs b/test/Test/Data/List.purs index 02b4355..fd76b6d 100644 --- a/test/Test/Data/List.purs +++ b/test/Test/Data/List.purs @@ -2,8 +2,9 @@ module Test.Data.List (testList) where import Prelude -import Data.Foldable (foldMap, foldl) +import Data.Foldable (class Foldable, foldMap, foldl) import Data.FoldableWithIndex (foldMapWithIndex, foldlWithIndex, foldrWithIndex) +import Data.Function (on) import Data.List (List(..), (..), stripPrefix, Pattern(..), length, range, foldM, unzip, zip, zipWithA, zipWith, intersectBy, intersect, (\\), deleteBy, delete, unionBy, union, nubBy, nub, group, groupAll, groupBy, groupAllBy, partition, span, dropWhile, drop, dropEnd, takeWhile, take, takeEnd, sortBy, sort, catMaybes, mapMaybe, filterM, filter, concat, concatMap, reverse, alterAt, modifyAt, updateAt, deleteAt, insertAt, findLastIndex, findIndex, elemLastIndex, elemIndex, (!!), uncons, unsnoc, init, tail, last, head, insertBy, insert, snoc, null, singleton, fromFoldable, transpose, mapWithIndex, (:)) import Data.List.NonEmpty as NEL import Data.Maybe (Maybe(..), isNothing, fromJust) @@ -21,7 +22,11 @@ import Test.Assert (assert) testList :: Effect Unit testList = do - let l = fromFoldable + let + l = fromFoldable + + nel :: ∀ f a. Foldable f => a -> f a -> NEL.NonEmptyList a + nel x xs = NEL.NonEmptyList $ x :| fromFoldable xs log "strip prefix" assert $ stripPrefix (Pattern (1:Nil)) (1:2:Nil) == Just (2:Nil) @@ -265,16 +270,16 @@ testList = do assert $ spanResult.rest == l [4, 5, 6, 7] log "group should group consecutive equal elements into lists" - assert $ group (l [1, 2, 2, 3, 3, 3, 1]) == l [NEL.singleton 1, NEL.NonEmptyList (2 :| l [2]), NEL.NonEmptyList (3 :| l [3, 3]), NEL.singleton 1] + assert $ group (l [3, 3, 2, 2, 1, 3]) == l [nel 3 [3], nel 2 [2], nel 1 [], nel 3 []] - log "groupAll should group equal elements into lists" - assert $ groupAll (l [1, 2, 2, 3, 3, 3, 1]) == l [NEL.NonEmptyList (1 :| l [1]), NEL.NonEmptyList (2 :| l [2]), NEL.NonEmptyList (3 :| l [3, 3])] + log "groupAll should sort then group equal elements into lists" + assert $ groupAll (l [3, 3, 2, 2, 1, 3]) == l [nel 1 [], nel 2 [2], nel 3 [3, 3]] log "groupBy should group consecutive equal elements into lists based on an equivalence relation" - assert $ groupBy (\x y -> odd x && odd y) (l [1, 1, 2, 2, 3, 3]) == l [NEL.NonEmptyList (1 :| l [1]), NEL.singleton 2, NEL.singleton 2, NEL.NonEmptyList (3 :| l [3])] + assert $ groupBy (eq `on` (_ `div` 10)) (l [32, 31, 21, 22, 11, 33]) == l [nel 32 [31], nel 21 [22], nel 11 [], nel 33 []] - log "groupAllBy should group equal elements into lists based on an equivalence relation" - assert $ groupAllBy (\x y -> odd x && odd y) (l [1, 3, 2, 4, 3, 3]) == l [NEL.singleton 1, NEL.singleton 2, NEL.NonEmptyList (3 :| l [3, 3]), NEL.singleton 4] + log "groupAllBy should sort then group equal elements into lists based on a comparison function" + assert $ groupAllBy (compare `on` (_ `div` 10)) (l [32, 31, 21, 22, 11, 33]) == l [nel 11 [], nel 21 [22], nel 32 [31, 33]] log "partition should separate a list into a tuple of lists that do and do not satisfy a predicate" let partitioned = partition (_ > 2) (l [1, 5, 3, 2, 4]) diff --git a/test/Test/Data/List/NonEmpty.purs b/test/Test/Data/List/NonEmpty.purs index 3316456..2c94642 100644 --- a/test/Test/Data/List/NonEmpty.purs +++ b/test/Test/Data/List/NonEmpty.purs @@ -4,6 +4,7 @@ import Prelude import Data.Foldable (class Foldable, foldM, foldMap, foldl, length) import Data.FoldableWithIndex (foldlWithIndex, foldrWithIndex, foldMapWithIndex) +import Data.Function (on) import Data.List as L import Data.List.NonEmpty as NEL import Data.Maybe (Maybe(..)) @@ -166,16 +167,16 @@ testNonEmptyList = do assert $ spanResult.rest == l [4, 5, 6, 7] log "group should group consecutive equal elements into lists" - assert $ NEL.group (nel 1 [2, 2, 3, 3, 3, 1]) == nel (nel 1 []) [nel 2 [2], nel 3 [3, 3], nel 1 []] + assert $ NEL.group (nel 3 [3, 2, 2, 1, 3]) == nel (nel 3 [3]) [nel 2 [2], nel 1 [], nel 3 []] - log "groupAll should group equal elements into lists" - assert $ NEL.groupAll (nel 1 [2, 2, 3, 3, 3, 1]) == nel (nel 1 [1]) [nel 2 [2], nel 3 [3, 3]] + log "groupAll should sort then group equal elements into lists" + assert $ NEL.groupAll (nel 3 [3, 2, 2, 1, 3]) == nel (nel 1 []) [nel 2 [2], nel 3 [3, 3]] log "groupBy should group consecutive equal elements into lists based on an equivalence relation" - assert $ NEL.groupBy (\x y -> odd x && odd y) (nel 1 [1, 2, 2, 3, 3]) == nel (nel 1 [1]) [nel 2 [], nel 2 [], nel 3 [3]] + assert $ NEL.groupBy (eq `on` (_ `div` 10)) (nel 32 [31, 21, 22, 11, 33]) == nel (nel 32 [31]) [nel 21 [22], nel 11 [], nel 33 []] - log "groupAllBy should group equal elements into lists based on an equivalence relation" - assert $ NEL.groupAllBy (\x y -> odd x && odd y) (nel 1 [3, 2, 4, 3, 3]) == nel (nel 1 []) [nel 2 [], nel 3 [3, 3], nel 4 []] + log "groupAllBy should sort then group equal elements into lists based on a comparison function" + assert $ NEL.groupAllBy (compare `on` (_ `div` 10)) (nel 32 [31, 21, 22, 11, 33]) == nel (nel 11 []) [nel 21 [22], nel 32 [31, 33]] log "partition should separate a list into a tuple of lists that do and do not satisfy a predicate" let partitioned = NEL.partition (_ > 2) (nel 1 [5, 3, 2, 4])