diff --git a/src/List/Extra.elm b/src/List/Extra.elm index 875c0db..bf0a9cc 100644 --- a/src/List/Extra.elm +++ b/src/List/Extra.elm @@ -3,14 +3,14 @@ module List.Extra exposing , intercalate, transpose, subsequences, permutations, interweave, cartesianProduct , foldl1, foldr1, indexedFoldl, indexedFoldr , scanl, scanl1, scanr, scanr1, mapAccuml, mapAccumr, unfoldr, iterate, initialize, cycle - , splitAt, splitWhen, takeWhileRight, dropWhileRight, span, break, stripPrefix, group, groupWhile, inits, tails, select, selectSplit + , splitAt, splitWhen, takeWhileRight, dropWhileRight, span, break, stripPrefix, group, groupWhile, inits, tails, select, selectSplit, gatherEquals, gatherEqualsBy, gatherWith , isPrefixOf, isSuffixOf, isInfixOf, isSubsequenceOf, isPermutationOf , notMember, find, elemIndex, elemIndices, findIndex, findIndices, count , zip, zip3 , lift2, lift3, lift4 , groupsOf, groupsOfWithStep, groupsOfVarying, greedyGroupsOf, greedyGroupsOfWithStep ) - + {-| Convenience functions for working with List @@ -36,7 +36,7 @@ module List.Extra exposing # Sublists -@docs splitAt, splitWhen, takeWhileRight, dropWhileRight, span, break, stripPrefix, group, groupWhile, inits, tails, select, selectSplit +@docs splitAt, splitWhen, takeWhileRight, dropWhileRight, span, break, stripPrefix, group, groupWhile, inits, tails, select, selectSplit, gatherEquals, gatherEqualsBy, gatherWith # Predicates @@ -1857,3 +1857,54 @@ greedyGroupsOfWithStep size step xs = else [] + +{-| Group equal elements together. This is different from `group` as each sublist +will contain *all* equal elements of the original list. Elements will be grouped +in the same order as they appear in the original list. The same applies to elements +within each group. + + gatherEquals [1,2,1,3,2] + --> [(1,[1]),(2,[2]),(3,[])] +-} +gatherEquals : List a -> List (a, List a) +gatherEquals list = + gatherWith (==) list + + +{-| Group equal elements together. A function is applied to each element of the list +and then the equality check is performed against the results of that function evaluation. +Elements will be grouped in the same order as they appear in the original list. The +same applies to elements within each group. + + gatherEqualsBy .age [{age=25},{age=23},{age=25}] + --> [({age=25},[{age=25}]),({age=23},[])] +-} +gatherEqualsBy : (a -> b) -> List a -> List (a, List a) +gatherEqualsBy extract list = + gatherWith (\a b -> (extract a) == (extract b)) list + + +{-| Group equal elements together using a custom equality function. Elements will be +grouped in the same order as they appear in the original list. The same applies to +elements within each group. + + gatherWith (==) [1,2,1,3,2] + --> [(1,[1]),(2,[2]),(3,[])] +-} +gatherWith : (a -> a -> Bool) -> List a -> List (a, List a) +gatherWith testFn list = + let + helper : List a -> List (a,List a) -> List (a, List a) + helper scattered gathered = + case scattered of + [] -> + List.reverse gathered + + toGather :: population -> + let + ( gathering, remaining ) = + List.partition (testFn toGather) population + in + helper remaining <| (toGather, gathering) :: gathered + in + helper list [] diff --git a/tests/Tests.elm b/tests/Tests.elm index afd5edd..83bad75 100644 --- a/tests/Tests.elm +++ b/tests/Tests.elm @@ -685,4 +685,61 @@ all = \() -> Expect.equal (setIf (\x -> modBy 2 x == 0) 0 [ 17, 8, 2, 9 ]) [ 17, 0, 0, 9 ] ] + , describe "gatherEquals" + [ test "empty list" <| + \() -> + gatherEquals [] + |> Expect.equal [] + , test "single element" <| + \() -> + gatherEquals [ 1 ] + |> Expect.equal [ (1, []) ] + , test "proper test" <| + \() -> + gatherEquals [ 1, 2, 1, 2, 3, 4, 1 ] + |> Expect.equal + [ ( 1, [ 1, 1 ]) + , ( 2, [ 2 ]) + , ( 3, []) + , ( 4, []) + ] + ] + , describe "gatherEqualsBy" + [ test "empty list" <| + \() -> + gatherEqualsBy identity [] + |> Expect.equal [] + , test "single element" <| + \() -> + gatherEqualsBy identity [ 1 ] + |> Expect.equal [ (1, []) ] + , test "proper test" <| + \() -> + gatherEqualsBy identity [ 1, 2, 1, 2, 3, 4, 1 ] + |> Expect.equal + [ ( 1, [ 1, 1 ]) + , ( 2, [ 2 ]) + , ( 3, []) + , ( 4, []) + ] + ] + , describe "gatherWith" + [ test "empty list" <| + \() -> + gatherWith (==) [] + |> Expect.equal [] + , test "single element" <| + \() -> + gatherWith (==) [ 1 ] + |> Expect.equal [ (1, []) ] + , test "proper test" <| + \() -> + gatherWith (==) [ 1, 2, 1, 2, 3, 4, 1 ] + |> Expect.equal + [ ( 1, [ 1, 1 ]) + , ( 2, [ 2 ]) + , ( 3, []) + , ( 4, []) + ] + ] ]