Skip to content

Commit 7d3664d

Browse files
Add missing members to Data.Array.NonEmpty (#201)
* Add missing members to Data.Array.NonEmpty * Export monomorphic versions of fold* and intercalate functions * Test find: find first element when multiple elements satisfy predicate Co-authored-by: Jordan Martinez <jordanalex.martinez@gmail.com>
1 parent a5afe3d commit 7d3664d

File tree

4 files changed

+121
-10
lines changed

4 files changed

+121
-10
lines changed

src/Data/Array.purs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ module Data.Array
7979
, mapMaybe
8080
, catMaybes
8181
, mapWithIndex
82+
, foldl
83+
, foldr
84+
, foldMap
85+
, fold
86+
, intercalate
8287
, scanl
8388
, scanr
8489

@@ -124,8 +129,6 @@ module Data.Array
124129
, foldRecM
125130

126131
, unsafeIndex
127-
128-
, module Exports
129132
) where
130133

131134
import Prelude
@@ -138,8 +141,8 @@ import Control.Monad.ST as ST
138141
import Data.Array.NonEmpty.Internal (NonEmptyArray(..))
139142
import Data.Array.ST as STA
140143
import Data.Array.ST.Iterator as STAI
141-
import Data.Foldable (class Foldable, foldl, foldr, traverse_)
142-
import Data.Foldable (foldl, foldr, foldMap, fold, intercalate) as Exports
144+
import Data.Foldable (class Foldable, traverse_)
145+
import Data.Foldable as F
143146
import Data.Maybe (Maybe(..), maybe, isJust, fromJust, isNothing)
144147
import Data.Traversable (sequence, traverse)
145148
import Data.Tuple (Tuple(..), fst, snd)
@@ -164,7 +167,7 @@ toUnfoldable xs = unfoldr f 0
164167
-- | ```
165168
-- |
166169
fromFoldable :: forall f. Foldable f => f ~> Array
167-
fromFoldable = fromFoldableImpl foldr
170+
fromFoldable = fromFoldableImpl F.foldr
168171

169172
foreign import fromFoldableImpl
170173
:: forall f a
@@ -765,6 +768,21 @@ modifyAtIndices :: forall t a. Foldable t => t Int -> (a -> a) -> Array a -> Arr
765768
modifyAtIndices is f xs =
766769
ST.run (STA.withArray (\res -> traverse_ (\i -> STA.modify i f res) is) xs)
767770

771+
foldl :: forall a b. (b -> a -> b) -> b -> Array a -> b
772+
foldl = F.foldl
773+
774+
foldr :: forall a b. (a -> b -> b) -> b -> Array a -> b
775+
foldr = F.foldr
776+
777+
foldMap :: forall a m. Monoid m => (a -> m) -> Array a -> m
778+
foldMap = F.foldMap
779+
780+
fold :: forall m. Monoid m => Array m -> m
781+
fold = F.fold
782+
783+
intercalate :: forall a. Monoid a => a -> Array a -> a
784+
intercalate = F.intercalate
785+
768786
-- | Fold a data structure from the left, keeping all intermediate results
769787
-- | instead of only the final result. Note that the initial value does not
770788
-- | appear in the result (unlike Haskell's `Prelude.scanl`).

src/Data/Array/NonEmpty.purs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,12 @@ module Data.Array.NonEmpty
3232
, unsnoc
3333

3434
, (!!), index
35+
, elem
36+
, notElem
3537
, elemIndex
3638
, elemLastIndex
39+
, find
40+
, findMap
3741
, findIndex
3842
, findLastIndex
3943
, insertAt
@@ -44,15 +48,24 @@ module Data.Array.NonEmpty
4448
, modifyAtIndices
4549
, alterAt
4650

51+
, intersperse
4752
, reverse
4853
, concat
4954
, concatMap
5055
, filter
51-
, splitAt
5256
, partition
57+
, splitAt
5358
, filterA
5459
, mapMaybe
5560
, catMaybes
61+
, mapWithIndex
62+
, foldl1
63+
, foldr1
64+
, foldMap1
65+
, fold1
66+
, intercalate
67+
, scanl
68+
, scanr
5669

5770
, sort
5871
, sortBy
@@ -116,6 +129,7 @@ import Data.Foldable (class Foldable)
116129
import Data.Maybe (Maybe(..), fromJust)
117130
import Data.NonEmpty (NonEmpty, (:|))
118131
import Data.Semigroup.Foldable (class Foldable1)
132+
import Data.Semigroup.Foldable as F
119133
import Data.Tuple (Tuple(..))
120134
import Data.Unfoldable (class Unfoldable)
121135
import Data.Unfoldable1 (class Unfoldable1, unfoldr1)
@@ -247,14 +261,26 @@ index = adaptAny A.index
247261

248262
infixl 8 index as !!
249263

264+
elem :: forall a. Eq a => a -> NonEmptyArray a -> Boolean
265+
elem x = adaptAny $ A.elem x
266+
267+
notElem :: forall a. Eq a => a -> NonEmptyArray a -> Boolean
268+
notElem x = adaptAny $ A.notElem x
269+
250270
elemIndex :: forall a. Eq a => a -> NonEmptyArray a -> Maybe Int
251271
elemIndex x = adaptAny $ A.elemIndex x
252272

253273
elemLastIndex :: forall a. Eq a => a -> NonEmptyArray a -> Maybe Int
254274
elemLastIndex x = adaptAny $ A.elemLastIndex x
255275

276+
find :: forall a. (a -> Boolean) -> NonEmptyArray a -> Maybe a
277+
find p = adaptAny $ A.find p
278+
279+
findMap :: forall a b. (a -> Maybe b) -> NonEmptyArray a -> Maybe b
280+
findMap p = adaptAny $ A.findMap p
281+
256282
findIndex :: forall a. (a -> Boolean) -> NonEmptyArray a -> Maybe Int
257-
findIndex x = adaptAny $ A.findIndex x
283+
findIndex p = adaptAny $ A.findIndex p
258284

259285
findLastIndex :: forall a. (a -> Boolean) -> NonEmptyArray a -> Maybe Int
260286
findLastIndex x = adaptAny $ A.findLastIndex x
@@ -280,6 +306,9 @@ modifyAtIndices is f = unsafeAdapt $ A.modifyAtIndices is f
280306
alterAt :: forall a. Int -> (a -> Maybe a) -> NonEmptyArray a -> Maybe (Array a)
281307
alterAt i f = A.alterAt i f <<< toArray
282308

309+
intersperse :: forall a. a -> NonEmptyArray a -> NonEmptyArray a
310+
intersperse x = unsafeAdapt $ A.intersperse x
311+
283312
reverse :: forall a. NonEmptyArray a -> NonEmptyArray a
284313
reverse = unsafeAdapt A.reverse
285314

@@ -316,6 +345,30 @@ mapMaybe f = adaptAny $ A.mapMaybe f
316345
catMaybes :: forall a. NonEmptyArray (Maybe a) -> Array a
317346
catMaybes = adaptAny A.catMaybes
318347

348+
mapWithIndex :: forall a b. (Int -> a -> b) -> NonEmptyArray a -> NonEmptyArray b
349+
mapWithIndex f = unsafeAdapt $ A.mapWithIndex f
350+
351+
foldl1 :: forall a. (a -> a -> a) -> NonEmptyArray a -> a
352+
foldl1 = F.foldl1
353+
354+
foldr1 :: forall a. (a -> a -> a) -> NonEmptyArray a -> a
355+
foldr1 = F.foldr1
356+
357+
foldMap1 :: forall a m. Semigroup m => (a -> m) -> NonEmptyArray a -> m
358+
foldMap1 = F.foldMap1
359+
360+
fold1 :: forall m. Semigroup m => NonEmptyArray m -> m
361+
fold1 = F.fold1
362+
363+
intercalate :: forall a. Semigroup a => a -> NonEmptyArray a -> a
364+
intercalate = F.intercalate
365+
366+
scanl :: forall a b. (b -> a -> b) -> b -> NonEmptyArray a -> NonEmptyArray b
367+
scanl f x = unsafeAdapt $ A.scanl f x
368+
369+
scanr :: forall a b. (a -> b -> b) -> b -> NonEmptyArray a -> NonEmptyArray b
370+
scanr f x = unsafeAdapt $ A.scanr f x
371+
319372
sort :: forall a. Ord a => NonEmptyArray a -> NonEmptyArray a
320373
sort = unsafeAdapt A.sort
321374

test/Test/Data/Array.purs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ testArray = do
270270
assert $ A.scanl (-) 10 [1,2,3] == [9, 7, 4]
271271

272272
log "scanl should return the same results as its Foldable counterpart"
273-
assert $ A.scanl (+) 0 [1,2,3] == scanl (+) 0 [1,2,3]
273+
assert $ A.scanl (+) 0 [1,2,3] == scanl (+) 0 [1,2,3]
274274
assert $ A.scanl (-) 10 [1,2,3] == scanl (-) 10 [1,2,3]
275275

276276
log "scanr should return an array that stores the accumulated value at each step"

test/Test/Data/Array/NonEmpty.purs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Data.Array as A
66
import Data.Array.NonEmpty as NEA
77
import Data.Const (Const(..))
88
import Data.Foldable (for_, sum, traverse_)
9-
import Data.FunctorWithIndex (mapWithIndex)
9+
import Data.Traversable (scanl, scanr)
1010
import Data.Maybe (Maybe(..), fromJust)
1111
import Data.Monoid.Additive (Additive(..))
1212
import Data.NonEmpty ((:|))
@@ -102,6 +102,14 @@ testNonEmptyArray = do
102102
assert $ NEA.index (fromArray [1, 2, 3]) 6 == Nothing
103103
assert $ NEA.index (fromArray [1, 2, 3]) (-1) == Nothing
104104

105+
log "elem should return true if the array contains the given element at least once"
106+
assert $ NEA.elem 1 (fromArray [1, 2, 1]) == true
107+
assert $ NEA.elem 4 (fromArray [1, 2, 1]) == false
108+
109+
log "notElem should return true if the array does not contain the given element"
110+
assert $ NEA.notElem 1 (fromArray [1, 2, 1]) == false
111+
assert $ NEA.notElem 4 (fromArray [1, 2, 1]) == true
112+
105113
log "elemIndex should return the index of an item that a predicate returns true for in an array"
106114
assert $ NEA.elemIndex 1 (fromArray [1, 2, 1]) == Just 0
107115
assert $ NEA.elemIndex 4 (fromArray [1, 2, 1]) == Nothing
@@ -110,6 +118,15 @@ testNonEmptyArray = do
110118
assert $ NEA.elemLastIndex 1 (fromArray [1, 2, 1]) == Just 2
111119
assert $ NEA.elemLastIndex 4 (fromArray [1, 2, 1]) == Nothing
112120

121+
log "find should return the first element for which a predicate returns true in an array"
122+
assert $ NEA.find (_ == 1) (fromArray [1, 2, 1]) == Just 1
123+
assert $ NEA.find (_ == 3) (fromArray [1, 2, 1]) == Nothing
124+
125+
log "findMap should return the mapping of the first element that satisfies the given predicate"
126+
assert $ NEA.findMap (\x -> if x > 3 then Just x else Nothing) (fromArray [1, 2, 4]) == Just 4
127+
assert $ NEA.findMap (\x -> if x > 3 then Just x else Nothing) (fromArray [1, 2, 1]) == Nothing
128+
assert $ NEA.findMap (\x -> if x > 3 then Just x else Nothing) (fromArray [4, 1, 5]) == Just 4
129+
113130
log "findIndex should return the index of an item that a predicate returns true for in an array"
114131
assert $ (NEA.findIndex (_ /= 1) (fromArray [1, 2, 1])) == Just 1
115132
assert $ (NEA.findIndex (_ == 3) (fromArray [1, 2, 1])) == Nothing
@@ -158,6 +175,13 @@ testNonEmptyArray = do
158175
log "alterAt should return Nothing if the index is out of NEA.range"
159176
assert $ NEA.alterAt 1 (Just <<< (_ + 1)) (NEA.singleton 1) == Nothing
160177

178+
log "intersperse should return the original array when given an array with one element"
179+
assert $ NEA.intersperse " " (NEA.singleton "a") == NEA.singleton "a"
180+
181+
log "intersperse should insert the given element in-between each element in an array with two or more elements"
182+
assert $ NEA.intersperse " " (fromArray ["a", "b"]) == fromArray ["a", " ", "b"]
183+
assert $ NEA.intersperse 0 (fromArray [ 1, 2, 3, 4, 5 ]) == fromArray [ 1, 0, 2, 0, 3, 0, 4, 0, 5 ]
184+
161185
log "reverse should reverse the order of items in an array"
162186
assert $ NEA.reverse (fromArray [1, 2, 3]) == fromArray [3, 2, 1]
163187
assert $ NEA.reverse (NEA.singleton 0) == NEA.singleton 0
@@ -193,7 +217,23 @@ testNonEmptyArray = do
193217
assert $ NEA.catMaybes (fromArray [Nothing, Just 2, Nothing, Just 4]) == [2, 4]
194218

195219
log "mapWithIndex applies a function with an index for every element"
196-
assert $ mapWithIndex (\i x -> x - i) (fromArray [9,8,7,6,5]) == fromArray [9,7,5,3,1]
220+
assert $ NEA.mapWithIndex (\i x -> x - i) (fromArray [9,8,7,6,5]) == fromArray [9,7,5,3,1]
221+
222+
log "scanl should return an array that stores the accumulated value at each step"
223+
assert $ NEA.scanl (+) 0 (fromArray [1,2,3]) == fromArray [1, 3, 6]
224+
assert $ NEA.scanl (-) 10 (fromArray [1,2,3]) == fromArray [9, 7, 4]
225+
226+
log "scanl should return the same results as its Foldable counterpart"
227+
assert $ NEA.scanl (+) 0 (fromArray [1,2,3]) == scanl (+) 0 (fromArray [1,2,3])
228+
assert $ NEA.scanl (-) 10 (fromArray [1,2,3]) == scanl (-) 10 (fromArray [1,2,3])
229+
230+
log "scanr should return an array that stores the accumulated value at each step"
231+
assert $ NEA.scanr (+) 0 (fromArray [1,2,3]) == fromArray [6,5,3]
232+
assert $ NEA.scanr (flip (-)) 10 (fromArray [1,2,3]) == fromArray [4,5,7]
233+
234+
log "scanr should return the same results as its Foldable counterpart"
235+
assert $ NEA.scanr (+) 0 (fromArray [1,2,3]) == scanr (+) 0 (fromArray [1,2,3])
236+
assert $ NEA.scanr (flip (-)) 10 (fromArray [1,2,3]) == scanr (flip (-)) 10 (fromArray [1,2,3])
197237

198238
log "updateAtIndices changes the elements at specified indices"
199239
assert $ NEA.updateAtIndices

0 commit comments

Comments
 (0)