Skip to content
Open
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
1,977 changes: 1,073 additions & 904 deletions spago.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions spago.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ package:
- filterable: ">=5.0.0 <6.0.0"
- foldable-traversable: ">=6.0.0 <7.0.0"
- foldable-traversable-extra: ">=0.0.5 <0.0.6"
- functions: ">=6.0.0 <7.0.0"
- maybe: ">=6.0.0 <7.0.0"
- ordered-collections: ">=3.2.0 <4.0.0"
- partial: ">=4.0.0 <5.0.0"
- prelude: ">=6.0.1 <7.0.0"
- tuples: ">=7.0.0 <8.0.0"
Expand All @@ -17,5 +19,5 @@ package:
githubOwner: flip111
githubRepo: purescript-arrays-extra
workspace:
package_set:
registry: 49.0.0
packageSet:
registry: 64.6.0
112 changes: 60 additions & 52 deletions src/Data/Array/Extra.purs
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,30 @@ module Data.Array.Extra
, interleave
, exactlyOne
, maybeToArray
, orderByArray
, orderByArrayOrd
, module Data.Foldable.Extra
, module Data.Semigroup.Foldable.Extra
, module Data.Traversable.Extra
) where

import Data.Foldable.Extra
import Data.Semigroup.Foldable.Extra
import Data.Traversable.Extra
import Data.Foldable.Extra (allPredicate, anyPredicate, groupMaybe, groupMaybeMap, mapEither, mapMaybeAny, occurrences, occurrencesMap, partitionMaybe, sameElements)
import Data.Semigroup.Foldable.Extra (mapAny)
import Data.Traversable.Extra (mapAll, mapMaybeWrite, mapMaybeWriteModify, mapModify)
import Prelude

import Control.Semigroupoid ((<<<))
import Data.Array (catMaybes, cons, head, length, sortBy, uncons, findIndex)
import Data.Array (catMaybes, cons, findIndex, head, length, mapWithIndex, sortBy, uncons)
import Data.Array.NonEmpty (NonEmptyArray)
import Data.Array.NonEmpty as NEA
import Data.Either (Either)
import Data.Eq (class Eq, (==))
import Data.Filterable (partitionMap)
import Data.Foldable (fold)
import Data.Function (on)
import Data.Functor (map)
import Data.HeytingAlgebra ((||))
import Data.Map as Map
import Data.Maybe (Maybe(..), fromJust)
import Data.Ord (class Ord, comparing, (<), (>))
import Data.Ordering (Ordering(..))
import Data.Ring ((-))
import Data.Semigroup ((<>))
import Data.Maybe (Maybe(..), fromJust, fromMaybe)
import Data.Tuple (Tuple(..), fst, snd)
import Partial.Unsafe (unsafePartial)


-- | Sort by multiple comparison functions.
-- | Similar to SQL where you can specificy multiple columns with ASC and DESC.
-- | When a comparison function gives EQ the next one is tried.
Expand Down Expand Up @@ -74,7 +68,7 @@ sortOn f = sortBy (comparing f)
-- | sortOn' (\x -> if x == "dog" then 2 else 1) ["apple", "dog", "kiwi"] = ["apple", "kiwi", "dog"]
-- | ```
sortOn' :: forall a b. Ord b => (a -> b) -> Array a -> Array a
sortOn' f = map snd <<< sortBy (comparing fst) <<< map (\x -> let y = f x in Tuple y x)
sortOn' f = map snd <<< sortBy (comparing fst) <<< map (\x -> let y = f x in Tuple y x)

-- | Sort a list by a projection and sort by a given comparison function.
-- |
Expand Down Expand Up @@ -102,11 +96,13 @@ sortOnBy' f comp = map snd <<< sortBy (comp `on` fst) <<< map (\x -> let y = f x
-- Implementers note: this could be simplified with https://github.com/purescript/purescript-prelude/issues/310
sortOnByMaybe :: forall a b. (a -> Maybe b) -> (b -> b -> Ordering) -> Array a -> Array a
sortOnByMaybe f comp xs =
let g Nothing Nothing = EQ
g (Just _) Nothing = GT
g Nothing (Just _) = LT
g (Just a) (Just b) = comp a b
in sortOnBy f g xs
let
g Nothing Nothing = EQ
g (Just _) Nothing = GT
g Nothing (Just _) = LT
g (Just a) (Just b) = comp a b
in
sortOnBy f g xs

-- | Sort a list by a projection and sort by a given comparison function.
-- | When the projection returns Nothing those items will be placed last. Useful if your Array contains items with missing data to sort on
Expand All @@ -117,11 +113,13 @@ sortOnByMaybe f comp xs =
-- | ```
sortOnByMaybe' :: forall a b. (a -> Maybe b) -> (b -> b -> Ordering) -> Array a -> Array a
sortOnByMaybe' f comp xs =
let g Nothing Nothing = EQ
g (Just _) Nothing = GT
g Nothing (Just _) = LT
g (Just a) (Just b) = comp a b
in sortOnBy' f g xs
let
g Nothing Nothing = EQ
g (Just _) Nothing = GT
g Nothing (Just _) = LT
g (Just a) (Just b) = comp a b
in
sortOnBy' f g xs

-- | Partitions an array of Either into two arrays. All the Left elements are put, in order, into the left field of the output record. Similarly the Right elements are put into the right field of the output record.
-- | Note that this function is an alias for `partitionMap` from `Data.Filterable`.
Expand All @@ -130,7 +128,7 @@ sortOnByMaybe' f comp xs =
-- | ```purescript
-- | partitionEithers (\a -> if a > 2 then Left a else Right a) [1,2,3,4] == {left: [1,2], right: [3,4]}
-- | ```
partitionEithers :: forall a l r. (a -> Either l r) -> Array a -> {left :: Array l, right :: Array r}
partitionEithers :: forall a l r. (a -> Either l r) -> Array a -> { left :: Array l, right :: Array r }
partitionEithers = partitionMap

-- -- | Like unionBy between array A and array B. With elements left out from B being included when they match the predicate.
Expand Down Expand Up @@ -165,14 +163,16 @@ partitionEithers = partitionMap
-- | ```
combinations :: forall a. Int -> Array a -> Maybe (Array (NonEmptyArray a))
combinations n xs =
let f 0 _ = [[]]
f nn xs' = case uncons xs' of
Nothing -> []
Just {head, tail} -> map (cons head) (f (nn - 1) tail) <> f n tail
in if n < 1 || n > length xs then
Nothing
else
Just (map (\x -> unsafePartial (fromJust (NEA.fromArray x))) (f n xs))
let
f 0 _ = [ [] ]
f nn xs' = case uncons xs' of
Nothing -> []
Just { head, tail } -> map (cons head) (f (nn - 1) tail) <> f n tail
in
if n < 1 || n > length xs then
Nothing
else
Just (map (\x -> unsafePartial (fromJust (NEA.fromArray x))) (f n xs))

-- | Takes an element from each array in turn to produce a new array.
-- |
Expand All @@ -182,12 +182,16 @@ combinations n xs =
-- | ```
interleave :: forall a. Array (Array a) -> Array a
interleave xss =
let f :: Tuple (Array (Array a)) (Array a) -> Tuple (Array (Array a)) (Array a)
f (Tuple [] acc) = Tuple [] acc
f (Tuple xss' acc) =
let heads_tails = catMaybes (map uncons xss')
in f (Tuple (map (_.tail) heads_tails) (map (_.head) heads_tails <> acc))
in snd (f (Tuple xss []))
let
f :: Tuple (Array (Array a)) (Array a) -> Tuple (Array (Array a)) (Array a)
f (Tuple [] acc) = Tuple [] acc
f (Tuple xss' acc) =
let
heads_tails = catMaybes (map uncons xss')
in
f (Tuple (map (_.tail) heads_tails) (map (_.head) heads_tails <> acc))
in
snd (f (Tuple xss []))

-- | When the array has only one element, return this element.
-- | This function is the opposite from `singleton`.
Expand All @@ -212,8 +216,8 @@ exactlyOne xs = case length xs of
-- | maybeToArray (Just 2) == [2]
-- | ```
maybeToArray :: forall a. Maybe a -> Array a
maybeToArray Nothing = []
maybeToArray (Just value) = [value]
maybeToArray Nothing = []
maybeToArray (Just value) = [ value ]

-- | Given two arrays, sort the second one by the order of the first one
-- | The elements of each array will be compared to each other with their own projection functions.
Expand All @@ -225,14 +229,16 @@ maybeToArray (Just value) = [value]
-- | ```
orderByArray :: forall x y a. Eq a => (x -> a) -> (y -> a) -> Array x -> Array y -> Array y
orderByArray proj_x proj_y xs ys =
let findIndexY y = findIndex (\x -> proj_x x == proj_y y) xs
compareIndices y1 y2 =
case Tuple (findIndexY y1) (findIndexY y2) of
Tuple (Just i1) (Just i2) -> compare i1 i2
Tuple (Just _) Nothing -> LT
Tuple Nothing (Just _) -> GT
Tuple Nothing Nothing -> EQ
in sortBy compareIndices ys
let
findIndexY y = findIndex (\x -> proj_x x == proj_y y) xs
compareIndices y1 y2 =
case Tuple (findIndexY y1) (findIndexY y2) of
Tuple (Just i1) (Just i2) -> compare i1 i2
Tuple (Just _) Nothing -> LT
Tuple Nothing (Just _) -> GT
Tuple Nothing Nothing -> EQ
in
sortBy compareIndices ys

-- | Given two arrays, sort the second one by the order of the first one
-- | The elements of each array will be compared to each other with their own projection functions.
Expand All @@ -241,8 +247,10 @@ orderByArray proj_x proj_y xs ys =
-- | Same as `orderByArray` but potentially faster due to using a Map internally. You will have to benchmark your specific situation to find out whether `orderByArray` or `orderByArrayOrd'` is faster.
orderByArrayOrd :: forall x y a. Ord a => (x -> a) -> (y -> a) -> Array x -> Array y -> Array y
orderByArrayOrd proj_x proj_y xs ys =
let indexMap = Map.fromFoldable (mapWithIndex (\i x -> Tuple (proj_x x) i) xs)
in sortBy (comparing (\y -> fromMaybe (length xs) $ Map.lookup (proj_y y) indexMap)) ys
let
indexMap = Map.fromFoldable (mapWithIndex (\i x -> Tuple (proj_x x) i) xs)
in
sortBy (comparing (\y -> fromMaybe (length xs) $ Map.lookup (proj_y y) indexMap)) ys

-- zipOn :: forall a b c d. (a -> Maybe d) (b -> Maybe d) (a -> b -> c) -> Array a -> Array b -> Array c
-- zipOn f_a f_b c xs ys -- start searching the smallest array first
11 changes: 7 additions & 4 deletions src/Data/Array/Extra/All.purs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ findIndices f xs = map snd (filter (\(Tuple x _) -> f x) (zip xs (range 0 (lengt
-- | ```
updateAllWith :: forall a. (a -> Boolean) -> a -> Array a -> Array a
updateAllWith f a xs = foldl go xs (findIndices f xs)
where go arr idx = unsafePartial (unsafeUpdateAt idx a arr)
where
go arr idx = unsafePartial (unsafeUpdateAt idx a arr)

-- | Find an element by a predicate and return an array with the element replaced by an array.
-- |
Expand All @@ -33,7 +34,8 @@ updateAllWith f a xs = foldl go xs (findIndices f xs)
-- | ```
updateAllArrayWith :: forall a. Partial => (a -> Boolean) -> Array a -> Array a -> Array a
updateAllArrayWith f xs ys = foldl go ys (findIndices f ys)
where go arr idx = unsafePartial (unsafeInsertArray idx xs arr)
where
go arr idx = unsafePartial (unsafeInsertArray idx xs arr)

-- | Find all elements matching a predicate and modify each element found.
-- |
Expand All @@ -42,7 +44,8 @@ updateAllArrayWith f xs ys = foldl go ys (findIndices f ys)
-- | ```
modifyAllWith :: forall a. (a -> Boolean) -> (a -> a) -> Array a -> Array a
modifyAllWith f modifier xs = foldl go xs (findIndices f xs)
where go arr idx = unsafePartial (unsafeModifyAt idx modifier arr)
where
go arr idx = unsafePartial (unsafeModifyAt idx modifier arr)

-- | Find an element and return an array without that element when it was found.
-- |
Expand Down Expand Up @@ -70,4 +73,4 @@ difference :: forall a. Eq a => Array a -> Array a -> Array a
difference xs ys = foldl (\xss y -> deleteWith (\x -> x == y) xss) xs ys
-- todo: using Set is probably faster (see haskell code)

-- todo: differenceBy
-- todo: differenceBy
34 changes: 18 additions & 16 deletions src/Data/Array/Extra/First.purs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import Partial.Unsafe (unsafePartial)
-- | ```purescript
-- | pick (_ == 2) [1,2,3] == Just {yes: 2, no: [1,3]}
-- | ```
pick :: forall a. (a -> Boolean) -> Array a -> Maybe {yes :: a, no :: Array a}
pick :: forall a. (a -> Boolean) -> Array a -> Maybe { yes :: a, no :: Array a }
pick f xs = case findIndex f xs of
Nothing -> Nothing
Nothing -> Nothing
Just idx ->
let elem = unsafePartial (unsafeIndex xs idx)
rest = unsafePartial (unsafeDeleteAt idx xs)
in Just {yes: elem, no: rest}
let
elem = unsafePartial (unsafeIndex xs idx)
rest = unsafePartial (unsafeDeleteAt idx xs)
in
Just { yes: elem, no: rest }

-- | Find an element and return an array without that element when it was found.
-- |
Expand Down Expand Up @@ -73,7 +75,7 @@ modifyWith f modifier xs = map (\idx -> unsafePartial (unsafeModifyAt idx modifi
-- | ```
modifyOrSnoc :: forall a. (a -> Boolean) -> (a -> a) -> Array a -> a -> Array a
modifyOrSnoc f modifier xs x = case findIndex f xs of
Nothing -> snoc xs x
Nothing -> snoc xs x
Just idx -> unsafePartial (unsafeModifyAt idx modifier xs)

-- | Modify an element when it was found by the predicate or push a new element to the front of the array.
Expand All @@ -84,7 +86,7 @@ modifyOrSnoc f modifier xs x = case findIndex f xs of
-- | ```
modifyOrCons :: forall a. (a -> Boolean) -> (a -> a) -> a -> Array a -> Array a
modifyOrCons f modifier x xs = case findIndex f xs of
Nothing -> cons x xs
Nothing -> cons x xs
Just idx -> unsafePartial (unsafeModifyAt idx modifier xs)

-- | Update an element when it was found by the predicate or append a new element to the end of the array.
Expand All @@ -95,7 +97,7 @@ modifyOrCons f modifier x xs = case findIndex f xs of
-- | ```
updateOrSnoc :: forall a. (a -> Boolean) -> Array a -> a -> Array a
updateOrSnoc f xs x = case findIndex f xs of
Nothing -> snoc xs x
Nothing -> snoc xs x
Just idx -> unsafePartial (unsafeUpdateAt idx x xs)

-- | Update an element when it was found by the predicate or push a new element to the front of the array.
Expand All @@ -106,18 +108,18 @@ updateOrSnoc f xs x = case findIndex f xs of
-- | ```
updateOrCons :: forall a. (a -> Boolean) -> a -> Array a -> Array a
updateOrCons f x xs = case findIndex f xs of
Nothing -> cons x xs
Nothing -> cons x xs
Just idx -> unsafePartial (unsafeUpdateAt idx x xs)

-- | Finds a single element and returns it together with elements before and after.
-- |
-- | ```purescript
-- | partitionSides (_ == 3) [1,2,3,4,5] == Just {before: [1,2], found: 3, after: [4,5]}
-- | ```
splitOn :: forall a. (a -> Boolean) -> Array a -> Maybe {before :: Array a, found :: a, after :: Array a}
splitOn :: forall a. (a -> Boolean) -> Array a -> Maybe { before :: Array a, found :: a, after :: Array a }
splitOn f xs = case findIndex f xs of
Nothing -> Nothing
Just idx -> Just {before: take idx xs, found: unsafePartial (unsafeIndex xs idx), after: drop (idx + 1) xs}
Nothing -> Nothing
Just idx -> Just { before: take idx xs, found: unsafePartial (unsafeIndex xs idx), after: drop (idx + 1) xs }

-- | Find an element and place an element before it.
-- | Could also be thought of as placing the element in the place of the found element and moving al later elements.
Expand All @@ -127,7 +129,7 @@ splitOn f xs = case findIndex f xs of
-- | ```
insertBefore :: forall a. (a -> Boolean) -> a -> Array a -> Maybe (Array a)
insertBefore f x xs = case findIndex f xs of
Nothing -> Nothing
Nothing -> Nothing
Just idx -> insertAt idx x xs

-- | Find an element and place an element after it.
Expand All @@ -137,7 +139,7 @@ insertBefore f x xs = case findIndex f xs of
-- | ```
insertAfter :: forall a. (a -> Boolean) -> a -> Array a -> Maybe (Array a)
insertAfter f x xs = case findIndex f xs of
Nothing -> Nothing
Nothing -> Nothing
Just idx -> insertAt (idx + 1) x xs

-- | Find an element which could be projected into another value.
Expand All @@ -148,10 +150,10 @@ insertAfter f x xs = case findIndex f xs of
-- | ```
findMaybe :: forall a b. (a -> Maybe b) -> Array a -> Maybe b
findMaybe f xs = case uncons xs of
Nothing -> Nothing
Nothing -> Nothing
Just { head, tail } -> case f head of
Just projection -> Just projection
Nothing -> findMaybe f tail
Nothing -> findMaybe f tail

-- | Re-export of `difference` from `Data.Array`
-- |
Expand Down
Loading