From e3268cf85eb69c26e18aedf532b651d592b61a60 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 27 Sep 2020 00:23:08 -0400 Subject: [PATCH 1/3] Add `splitAt` --- src/Data/Array.purs | 11 +++++++++++ src/Data/Array/NonEmpty.purs | 4 ++++ test/Test/Data/Array.purs | 8 ++++++++ test/Test/Data/Array/NonEmpty.purs | 8 ++++++++ 4 files changed, 31 insertions(+) diff --git a/src/Data/Array.purs b/src/Data/Array.purs index 0409530e..af45ab7c 100644 --- a/src/Data/Array.purs +++ b/src/Data/Array.purs @@ -69,6 +69,7 @@ module Data.Array , concatMap , filter , partition + , splitAt , filterA , mapMaybe , catMaybes @@ -600,6 +601,16 @@ foreign import partition -> Array a -> { yes :: Array a, no :: Array a } +-- | Splits an array into two pieces, where the first array has `n` elements +-- | and the second array has the remaining elements. +-- | +-- | ```purescript +-- | splitAt 3 [1, 2, 3, 4, 5] == Tuple [1, 2, 3] [4, 5] +-- | ``` +splitAt :: forall a. Int -> Array a -> Tuple (Array a) (Array a) +splitAt n xs | n <= 0 = Tuple [] xs +splitAt n xs = Tuple (slice 0 n xs) (slice n (length xs) xs) + -- | Filter where the predicate returns a `Boolean` in some `Applicative`. -- | -- | ```purescript diff --git a/src/Data/Array/NonEmpty.purs b/src/Data/Array/NonEmpty.purs index d987fd8f..21150643 100644 --- a/src/Data/Array/NonEmpty.purs +++ b/src/Data/Array/NonEmpty.purs @@ -48,6 +48,7 @@ module Data.Array.NonEmpty , concat , concatMap , filter + , splitAt , partition , filterA , mapMaybe @@ -296,6 +297,9 @@ filterA -> f (Array a) filterA f = adaptAny $ A.filterA f +splitAt :: forall a. Int -> NonEmptyArray a -> Tuple (Array a) (Array a) +splitAt n xs = A.splitAt n $ toArray xs + mapMaybe :: forall a b. (a -> Maybe b) -> NonEmptyArray a -> Array b mapMaybe f = adaptAny $ A.mapMaybe f diff --git a/test/Test/Data/Array.purs b/test/Test/Data/Array.purs index d1c59f6a..7c84f97f 100644 --- a/test/Test/Data/Array.purs +++ b/test/Test/Data/Array.purs @@ -202,6 +202,14 @@ testArray = do log "filter should remove items that don't match a predicate" assert $ A.filter odd (A.range 0 10) == [1, 3, 5, 7, 9] + log "splitAt should split the array at the given number of elements" + assert $ A.splitAt 3 [1, 2, 3, 4, 5] == Tuple [1, 2, 3] [4, 5] + assert $ A.splitAt 1 [1, 2, 3] == Tuple [1] [2, 3] + assert $ A.splitAt 3 [1, 2, 3] == Tuple [1, 2, 3] [] + assert $ A.splitAt 4 [1, 2, 3] == Tuple [1, 2, 3] [] + assert $ A.splitAt 0 [1, 2, 3] == Tuple [] [1, 2, 3] + assert $ A.splitAt (-1) [1, 2, 3] == Tuple [] [1, 2, 3] + log "filterA should remove items that don't match a predicate while using an applicative behaviour" assert $ A.filterA (Just <<< odd) (A.range 0 10) == Just [1, 3, 5, 7, 9] assert $ A.filterA (const Nothing) (A.range 0 10) == Nothing diff --git a/test/Test/Data/Array/NonEmpty.purs b/test/Test/Data/Array/NonEmpty.purs index fbc576fa..b4260309 100644 --- a/test/Test/Data/Array/NonEmpty.purs +++ b/test/Test/Data/Array/NonEmpty.purs @@ -166,6 +166,14 @@ testNonEmptyArray = do log "filter should remove items that don't match a predicate" assert $ NEA.filter odd (NEA.range 0 10) == [1, 3, 5, 7, 9] + log "splitAt should split the array at the given number of elements" + assert $ NEA.splitAt 3 (fromArray [1, 2, 3, 4, 5]) == Tuple [1, 2, 3] [4, 5] + assert $ NEA.splitAt 1 (fromArray [1, 2, 3]) == Tuple [1] [2, 3] + assert $ NEA.splitAt 3 (fromArray [1, 2, 3]) == Tuple [1, 2, 3] [] + assert $ NEA.splitAt 4 (fromArray [1, 2, 3]) == Tuple [1, 2, 3] [] + assert $ NEA.splitAt 0 (fromArray [1, 2, 3]) == Tuple [] [1, 2, 3] + assert $ NEA.splitAt (-1) (fromArray [1, 2, 3]) == Tuple [] [1, 2, 3] + log "filterA should remove items that don't match a predicate while using an applicative behaviour" assert $ NEA.filterA (Just <<< odd) (NEA.range 0 10) == Just [1, 3, 5, 7, 9] assert $ NEA.filterA (const Nothing) (NEA.range 0 10) == Nothing From 8bc4588b504069eebe95e588ab7edc2da02ddcf2 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 27 Sep 2020 10:04:41 -0400 Subject: [PATCH 2/3] Make `splitAt` return a record instead of a tuple --- src/Data/Array.purs | 22 ++++++++++++++++------ src/Data/Array/NonEmpty.purs | 4 ++-- test/Test/Data/Array.purs | 12 ++++++------ test/Test/Data/Array/NonEmpty.purs | 12 ++++++------ 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/Data/Array.purs b/src/Data/Array.purs index af45ab7c..b86b4f91 100644 --- a/src/Data/Array.purs +++ b/src/Data/Array.purs @@ -601,15 +601,25 @@ foreign import partition -> Array a -> { yes :: Array a, no :: Array a } --- | Splits an array into two pieces, where the first array has `n` elements --- | and the second array has the remaining elements. +-- | Splits an array into two subarrays, where `before` contains the elements +-- | up to (but not including) the given index, and `after` contains the rest +-- | of the elements, from that index on. -- | -- | ```purescript --- | splitAt 3 [1, 2, 3, 4, 5] == Tuple [1, 2, 3] [4, 5] +-- | >>> splitAt 3 [1, 2, 3, 4, 5] +-- | { before: [1, 2, 3], after: [4, 5] } -- | ``` -splitAt :: forall a. Int -> Array a -> Tuple (Array a) (Array a) -splitAt n xs | n <= 0 = Tuple [] xs -splitAt n xs = Tuple (slice 0 n xs) (slice n (length xs) xs) +-- | +-- | Thus, the length of `(splitAt i arr).before` will equal either `i` or +-- | `length arr`, if that is shorter. (Or if `i` is negative the length will +-- | be 0.) +-- | +-- | ```purescript +-- | splitAt 3 [1, 2, 3, 4, 5] == { before: [1, 2, 3], after: [4, 5] } +-- | ``` +splitAt :: forall a. Int -> Array a -> { before :: Array a, after :: Array a } +splitAt i xs | i <= 0 = { before: [], after: xs } +splitAt i xs = { before: slice 0 i xs, after: slice i (length xs) xs } -- | Filter where the predicate returns a `Boolean` in some `Applicative`. -- | diff --git a/src/Data/Array/NonEmpty.purs b/src/Data/Array/NonEmpty.purs index 21150643..119b74a9 100644 --- a/src/Data/Array/NonEmpty.purs +++ b/src/Data/Array/NonEmpty.purs @@ -297,8 +297,8 @@ filterA -> f (Array a) filterA f = adaptAny $ A.filterA f -splitAt :: forall a. Int -> NonEmptyArray a -> Tuple (Array a) (Array a) -splitAt n xs = A.splitAt n $ toArray xs +splitAt :: forall a. Int -> NonEmptyArray a -> { before :: Array a, after :: Array a } +splitAt i xs = A.splitAt i $ toArray xs mapMaybe :: forall a b. (a -> Maybe b) -> NonEmptyArray a -> Array b mapMaybe f = adaptAny $ A.mapMaybe f diff --git a/test/Test/Data/Array.purs b/test/Test/Data/Array.purs index 7c84f97f..537ee9d7 100644 --- a/test/Test/Data/Array.purs +++ b/test/Test/Data/Array.purs @@ -203,12 +203,12 @@ testArray = do assert $ A.filter odd (A.range 0 10) == [1, 3, 5, 7, 9] log "splitAt should split the array at the given number of elements" - assert $ A.splitAt 3 [1, 2, 3, 4, 5] == Tuple [1, 2, 3] [4, 5] - assert $ A.splitAt 1 [1, 2, 3] == Tuple [1] [2, 3] - assert $ A.splitAt 3 [1, 2, 3] == Tuple [1, 2, 3] [] - assert $ A.splitAt 4 [1, 2, 3] == Tuple [1, 2, 3] [] - assert $ A.splitAt 0 [1, 2, 3] == Tuple [] [1, 2, 3] - assert $ A.splitAt (-1) [1, 2, 3] == Tuple [] [1, 2, 3] + assert $ A.splitAt 3 [1, 2, 3, 4, 5] == { before: [1, 2, 3], after: [4, 5] } + assert $ A.splitAt 1 [1, 2, 3] == { before: [1], after: [2, 3] } + assert $ A.splitAt 3 [1, 2, 3] == { before: [1, 2, 3], after: [] } + assert $ A.splitAt 4 [1, 2, 3] == { before: [1, 2, 3], after: [] } + assert $ A.splitAt 0 [1, 2, 3] == { before: [], after: [1, 2, 3] } + assert $ A.splitAt (-1) [1, 2, 3] == { before: [], after: [1, 2, 3] } log "filterA should remove items that don't match a predicate while using an applicative behaviour" assert $ A.filterA (Just <<< odd) (A.range 0 10) == Just [1, 3, 5, 7, 9] diff --git a/test/Test/Data/Array/NonEmpty.purs b/test/Test/Data/Array/NonEmpty.purs index b4260309..ba791e2b 100644 --- a/test/Test/Data/Array/NonEmpty.purs +++ b/test/Test/Data/Array/NonEmpty.purs @@ -167,12 +167,12 @@ testNonEmptyArray = do assert $ NEA.filter odd (NEA.range 0 10) == [1, 3, 5, 7, 9] log "splitAt should split the array at the given number of elements" - assert $ NEA.splitAt 3 (fromArray [1, 2, 3, 4, 5]) == Tuple [1, 2, 3] [4, 5] - assert $ NEA.splitAt 1 (fromArray [1, 2, 3]) == Tuple [1] [2, 3] - assert $ NEA.splitAt 3 (fromArray [1, 2, 3]) == Tuple [1, 2, 3] [] - assert $ NEA.splitAt 4 (fromArray [1, 2, 3]) == Tuple [1, 2, 3] [] - assert $ NEA.splitAt 0 (fromArray [1, 2, 3]) == Tuple [] [1, 2, 3] - assert $ NEA.splitAt (-1) (fromArray [1, 2, 3]) == Tuple [] [1, 2, 3] + assert $ NEA.splitAt 3 (fromArray [1, 2, 3, 4, 5]) == { before: [1, 2, 3], after: [4, 5] } + assert $ NEA.splitAt 1 (fromArray [1, 2, 3]) == { before: [1], after: [2, 3] } + assert $ NEA.splitAt 3 (fromArray [1, 2, 3]) == { before: [1, 2, 3], after: [] } + assert $ NEA.splitAt 4 (fromArray [1, 2, 3]) == { before: [1, 2, 3], after: [] } + assert $ NEA.splitAt 0 (fromArray [1, 2, 3]) == { before: [], after: [1, 2, 3] } + assert $ NEA.splitAt (-1) (fromArray [1, 2, 3]) == { before: [], after: [1, 2, 3] } log "filterA should remove items that don't match a predicate while using an applicative behaviour" assert $ NEA.filterA (Just <<< odd) (NEA.range 0 10) == Just [1, 3, 5, 7, 9] From 6f414d60001cae1dd7850b35cd8bfaccd6ee3db7 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Sun, 27 Sep 2020 10:08:31 -0400 Subject: [PATCH 3/3] Add test case and example for `splitAt` with an empty array --- src/Data/Array.purs | 1 + test/Test/Data/Array.purs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Data/Array.purs b/src/Data/Array.purs index b86b4f91..8a75dd19 100644 --- a/src/Data/Array.purs +++ b/src/Data/Array.purs @@ -615,6 +615,7 @@ foreign import partition -- | be 0.) -- | -- | ```purescript +-- | splitAt 2 ([] :: Array Int) == { before: [], after: [] } -- | splitAt 3 [1, 2, 3, 4, 5] == { before: [1, 2, 3], after: [4, 5] } -- | ``` splitAt :: forall a. Int -> Array a -> { before :: Array a, after :: Array a } diff --git a/test/Test/Data/Array.purs b/test/Test/Data/Array.purs index 537ee9d7..39ff57a8 100644 --- a/test/Test/Data/Array.purs +++ b/test/Test/Data/Array.purs @@ -203,6 +203,7 @@ testArray = do assert $ A.filter odd (A.range 0 10) == [1, 3, 5, 7, 9] log "splitAt should split the array at the given number of elements" + assert $ A.splitAt 2 ([] :: Array Int) == { before: [], after: [] } assert $ A.splitAt 3 [1, 2, 3, 4, 5] == { before: [1, 2, 3], after: [4, 5] } assert $ A.splitAt 1 [1, 2, 3] == { before: [1], after: [2, 3] } assert $ A.splitAt 3 [1, 2, 3] == { before: [1, 2, 3], after: [] }