diff --git a/src/Data/Array.js b/src/Data/Array.js index b9792582..8f0d63d3 100644 --- a/src/Data/Array.js +++ b/src/Data/Array.js @@ -107,6 +107,20 @@ exports.indexImpl = function (just) { }; }; +exports.findMapImpl = function (nothing) { + return function (isJust) { + return function (f) { + return function (xs) { + for (var i = 0; i < xs.length; i++) { + var result = f(xs[i]); + if (isJust(result)) return result; + } + return nothing; + }; + }; + }; +}; + exports.findIndexImpl = function (just) { return function (nothing) { return function (f) { @@ -222,6 +236,36 @@ exports.partition = function (f) { }; }; +exports.scanl = function (f) { + return function (b) { + return function (xs) { + var len = xs.length; + var acc = b; + var out = new Array(len); + for (var i = 0; i < len; i++) { + acc = f(acc)(xs[i]); + out[i] = acc; + } + return out; + }; + }; +}; + +exports.scanr = function (f) { + return function (b) { + return function (xs) { + var len = xs.length; + var acc = b; + var out = new Array(len); + for (var i = len - 1; i >= 0; i--) { + acc = f(xs[i])(acc); + out[i] = acc; + } + return out; + }; + }; +}; + //------------------------------------------------------------------------------ // Sorting --------------------------------------------------------------------- //------------------------------------------------------------------------------ diff --git a/src/Data/Array.purs b/src/Data/Array.purs index cb1dee91..b208a6fd 100644 --- a/src/Data/Array.purs +++ b/src/Data/Array.purs @@ -52,8 +52,12 @@ module Data.Array , unsnoc , (!!), index + , elem + , notElem , elemIndex , elemLastIndex + , find + , findMap , findIndex , findLastIndex , insertAt @@ -74,6 +78,8 @@ module Data.Array , mapMaybe , catMaybes , mapWithIndex + , scanl + , scanr , sort , sortBy @@ -127,9 +133,8 @@ import Data.Array.NonEmpty.Internal (NonEmptyArray(..)) import Data.Array.ST as STA import Data.Array.ST.Iterator as STAI import Data.Foldable (class Foldable, foldl, foldr, traverse_) -import Data.Foldable (foldl, foldr, foldMap, fold, intercalate, elem, notElem, find, findMap, any, all) as Exports -import Data.Maybe (Maybe(..), maybe, isJust, fromJust) -import Data.Traversable (scanl, scanr) as Exports +import Data.Foldable (foldl, foldr, foldMap, fold, intercalate, any, all) as Exports +import Data.Maybe (Maybe(..), maybe, isJust, fromJust, isNothing) import Data.Traversable (sequence, traverse) import Data.Tuple (Tuple(..), fst, snd) import Data.Unfoldable (class Unfoldable, unfoldr) @@ -398,6 +403,14 @@ foreign import indexImpl -- | infixl 8 index as !! +-- | Returns true if the array has the given element. +elem :: forall a. Eq a => a -> Array a -> Boolean +elem a arr = isJust $ elemIndex a arr + +-- | Returns true if the array does not have the given element. +notElem :: forall a. Eq a => a -> Array a -> Boolean +notElem a arr = isNothing $ elemIndex a arr + -- | Find the index of the first element equal to the specified element. -- | -- | ```purescript @@ -418,6 +431,28 @@ elemIndex x = findIndex (_ == x) elemLastIndex :: forall a. Eq a => a -> Array a -> Maybe Int elemLastIndex x = findLastIndex (_ == x) +-- | Find the first element for which a predicate holds. +-- | +-- | ```purescript +-- | findIndex (contains $ Pattern "b") ["a", "bb", "b", "d"] = Just "bb" +-- | findIndex (contains $ Pattern "x") ["a", "bb", "b", "d"] = Nothing +-- | ``` +find :: forall a. (a -> Boolean) -> Array a -> Maybe a +find f xs = unsafePartial (unsafeIndex xs) <$> findIndex f xs + +-- | Find the first element in a data structure which satisfies +-- | a predicate mapping. +findMap :: forall a b. (a -> Maybe b) -> Array a -> Maybe b +findMap = findMapImpl Nothing isJust + +foreign import findMapImpl + :: forall a b + . (forall c. Maybe c) + -> (forall c. Maybe c -> Boolean) + -> (a -> Maybe b) + -> Array a + -> Maybe b + -- | Find the first index for which a predicate holds. -- | -- | ```purescript @@ -698,6 +733,26 @@ modifyAtIndices :: forall t a. Foldable t => t Int -> (a -> a) -> Array a -> Arr modifyAtIndices is f xs = ST.run (STA.withArray (\res -> traverse_ (\i -> STA.modify i f res) is) xs) +-- | Fold a data structure from the left, keeping all intermediate results +-- | instead of only the final result. Note that the initial value does not +-- | appear in the result (unlike Haskell's `Prelude.scanl`). +-- | +-- | ``` +-- | scanl (+) 0 [1,2,3] = [1,3,6] +-- | scanl (-) 10 [1,2,3] = [9,7,4] +-- | ``` +foreign import scanl :: forall a b. (b -> a -> b) -> b -> Array a -> Array b + +-- | Fold a data structure from the right, keeping all intermediate results +-- | instead of only the final result. Note that the initial value does not +-- | appear in the result (unlike Haskell's `Prelude.scanr`). +-- | +-- | ``` +-- | scanr (+) 0 [1,2,3] = [6,5,3] +-- | scanr (flip (-)) 10 [1,2,3] = [4,5,7] +-- | ``` +foreign import scanr :: forall a b. (a -> b -> b) -> b -> Array a -> Array b + -------------------------------------------------------------------------------- -- Sorting --------------------------------------------------------------------- -------------------------------------------------------------------------------- diff --git a/test/Test/Data/Array.purs b/test/Test/Data/Array.purs index 39ff57a8..7cdbd3b2 100644 --- a/test/Test/Data/Array.purs +++ b/test/Test/Data/Array.purs @@ -7,6 +7,7 @@ import Data.Array as A import Data.Array.NonEmpty as NEA import Data.Const (Const(..)) import Data.Foldable (for_, foldMapDefaultR, class Foldable, all, traverse_) +import Data.Traversable (scanl, scanr) import Data.Maybe (Maybe(..), isNothing, fromJust) import Data.Tuple (Tuple(..)) import Data.Unfoldable (replicateA) @@ -131,6 +132,14 @@ testArray = do assert $ [1, 2, 3] !! 6 == Nothing assert $ [1, 2, 3] !! (-1) == Nothing + log "elem should return true if the array contains the given element at least once" + assert $ (A.elem 1 [1, 2, 1]) == true + assert $ (A.elem 4 [1, 2, 1]) == false + + log "notElem should return true if the array does not contain the given element" + assert $ (A.notElem 1 [1, 2, 1]) == false + assert $ (A.notElem 4 [1, 2, 1]) == true + log "elemIndex should return the index of an item that a predicate returns true for in an array" assert $ (A.elemIndex 1 [1, 2, 1]) == Just 0 assert $ (A.elemIndex 4 [1, 2, 1]) == Nothing @@ -139,6 +148,15 @@ testArray = do assert $ (A.elemLastIndex 1 [1, 2, 1]) == Just 2 assert $ (A.elemLastIndex 4 [1, 2, 1]) == Nothing + log "find should return the first element for which a predicate returns true in an array" + assert $ (A.find (_ /= 1) [1, 2, 1]) == Just 2 + assert $ (A.find (_ == 3) [1, 2, 1]) == Nothing + + log "findMap should return the mapping of the first element that satisfies the given predicate" + assert $ (A.findMap (\x -> if x > 3 then Just x else Nothing) [1, 2, 4]) == Just 4 + assert $ (A.findMap (\x -> if x > 3 then Just x else Nothing) [1, 2, 1]) == Nothing + assert $ (A.findMap (\x -> if x > 3 then Just x else Nothing) [4, 1, 5]) == Just 4 + log "findIndex should return the index of an item that a predicate returns true for in an array" assert $ (A.findIndex (_ /= 1) [1, 2, 1]) == Just 1 assert $ (A.findIndex (_ == 3) [1, 2, 1]) == Nothing @@ -237,6 +255,22 @@ testArray = do assert $ A.modifyAtIndices [0, 2, 8] not [true, true, true, true] == [false, true, false, true] + log "scanl should return an array that stores the accumulated value at each step" + assert $ A.scanl (+) 0 [1,2,3] == [1, 3, 6] + assert $ A.scanl (-) 10 [1,2,3] == [9, 7, 4] + + log "scanl should return the same results as its Foldable counterpart" + assert $ A.scanl (+) 0 [1,2,3] == scanl (+) 0 [1,2,3] + assert $ A.scanl (-) 10 [1,2,3] == scanl (-) 10 [1,2,3] + + log "scanr should return an array that stores the accumulated value at each step" + assert $ A.scanr (+) 0 [1,2,3] == [6,5,3] + assert $ A.scanr (flip (-)) 10 [1,2,3] == [4,5,7] + + log "scanr should return the same results as its Foldable counterpart" + assert $ A.scanr (+) 0 [1,2,3] == scanr (+) 0 [1,2,3] + assert $ A.scanr (flip (-)) 10 [1,2,3] == scanr (flip (-)) 10 [1,2,3] + log "sort should reorder a list into ascending order based on the result of compare" assert $ A.sort [1, 3, 2, 5, 6, 4] == [1, 2, 3, 4, 5, 6]