Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added sliding-window functions sliding and slidingWithSizeAndStep #212

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions src/Data/Array.purs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ module Data.Array
, toUnfoldable
, singleton
, (..), range
, rangeWithStep
, rangeWithStep'
, replicate
, some
, many
Expand Down Expand Up @@ -87,6 +89,8 @@ module Data.Array
, scanl
, scanr

, sliding
, slidingSizeStep
, sort
, sortBy
, sortWith
Expand Down Expand Up @@ -183,6 +187,7 @@ singleton :: forall a. a -> Array a
singleton a = [a]

-- | Create an array containing a range of integers, including both endpoints.
-- | If you want to control the step-size, see `rangeWithStep`
-- | ```purescript
-- | range 2 5 = [2, 3, 4, 5]
-- | ```
Expand Down Expand Up @@ -1289,3 +1294,82 @@ unsafeIndex :: forall a. Partial => Array a -> Int -> a
unsafeIndex = unsafeIndexImpl

foreign import unsafeIndexImpl :: forall a. Array a -> Int -> a

-- | Returns an array where each value represents a "sliding window" of the given array with a window of size 2 and a step size of 1.
-- |
-- | If you need more control over the size of the window and how far to step before making a new window, see `slidingSizeStep`
-- |
-- | ```purescript
-- | sliding [1,2,3,4,5] = [(Tuple 1 2),(Tuple 2 3),(Tuple 3 4),(Tuple 4 5)]
-- | ```
-- |
sliding :: forall a. Array a -> Array (Tuple a a)
sliding l = zip l (drop 1 l)

-- | Takes an arrays and returns an array of arrays as a "sliding window" view of this array.
-- | Illegal arguments result in an empty Array.
-- |
-- | As you can see in the example below, the last window might not get filled completely.
-- | Its size `s` will be `1 <= s <= size`.
-- |
-- | ```purescript
-- | > import Data.Array (range)
-- | > slidingSizeStep 3 2 [0,1,2,3,4,5,6,7,8,9,10] = [[0,1,2],[2,3,4],[4,5,6],[6,7,8],[8,9,10],[10]]
-- | > slidingSizeStep 3 3 [0,1,2,3,4,5,6,7,8,9,10] = [[0,1,2],[3,4,5],[6,7,8],[9,10]]
-- | ```
-- |
slidingSizeStep :: forall a. Int -> Int -> Array a -> Array (NonEmptyArray a)
slidingSizeStep size step array
| size <= 0 || step <= 0 = [] {- args not valid -}
| otherwise =
let
maxIndex = (length array) - 1
in
rangeWithStep' 0 maxIndex step (\i -> NonEmptyArray (slice i (i + size) array))


-- | Create an array containing a range of integers with a given step size, including both endpoints.
-- | Illegal arguments result in an empty Array.
-- |
-- | ```purescript
-- | > rangeWithStep 0 6 2 = [0,2,4,6]
-- | > rangeWithStep 0 (-6) (-2) = [0,-2,-4,-6]
-- | ```
-- |
rangeWithStep :: Int -> Int -> Int -> Array Int
rangeWithStep start end step = rangeWithStep' start end step identity

-- | Works just like rangeWithStep, but also uses a function to map each integer.
-- | `rangeWithStep' start end step f` is the same as `map f $ rangeWithStep start end step`,
-- | but without the extra intermediate array allocation.
-- |
-- | ```purescript
-- | > rangeWithStep' 0 6 2 identity = [0,2,4,6]
-- | > rangeWithStep' 0 6 2 (add 3) = [3,5,7,9]
-- | > rangeWithStep' 0 (-6) (-2) (add 3) = [3,1,-1,-3]
-- | ```
rangeWithStep' :: forall t. Int -> Int -> Int -> (Int -> t) -> Array t
rangeWithStep' start end step fn =
if not isValid
then []
else STA.run do
let
helper current acc =
if hasReachedEnd current then
pure acc
else do
void $ STA.push (fn current) acc
helper (current + step) acc
arr <- STA.new
_ <- helper start arr
pure arr
where
isValid =
step /= 0 &&
if end >= start
then step > 0
else step < 0
hasReachedEnd current =
if step > 0
then current > end
else current < end
22 changes: 22 additions & 0 deletions test/Test/Data/Array.purs
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,28 @@ testArray = do
, [4,0,0,1,25,36,458,5842,23757]
]

log "sliding"
assert $ A.sliding [ 1, 2, 3, 4, 5 ] == [ (Tuple 1 2), (Tuple 2 3), (Tuple 3 4), (Tuple 4 5) ]
assert $ A.sliding nil == []

log "slidingSizeStep"
assert $ A.slidingSizeStep 2 1 (A.range 0 4) == ([[0,1],[1,2],[2,3],[3,4],[4]] <#> nea)
assert $ A.slidingSizeStep 3 2 (A.range 0 10) == ([[0,1,2],[2,3,4],[4,5,6],[6,7,8],[8,9,10],[10]] <#> nea)
assert $ A.slidingSizeStep 3 3 (A.range 0 10) == ([[0,1,2],[3,4,5],[6,7,8],[9,10]] <#> nea)
assert $ A.slidingSizeStep 3 (-2) (A.range 0 10) == []
assert $ A.slidingSizeStep (-3) (2) (A.range 0 10) == []

log "rangeWithStep"
assert $ A.rangeWithStep 0 6 2 == [ 0, 2, 4, 6 ]
assert $ A.rangeWithStep 0 (-6) (-2) == [ 0, -2, -4, -6 ]
assert $ A.rangeWithStep 0 6 (-2) == []
assert $ A.rangeWithStep 0 (-6) (2) == []

log "rangeWithStep'"
assert $ A.rangeWithStep 0 6 2 == A.rangeWithStep' 0 6 2 identity
assert $ A.rangeWithStep' 0 6 2 (add 3) == [3, 5, 7, 9]
assert $ A.rangeWithStep' 0 (-6) (-2) (add 3) == [ 3, 1, -1, -3 ]

nea :: Array ~> NEA.NonEmptyArray
nea = unsafePartial fromJust <<< NEA.fromArray

Expand Down