├── benchmarks ├── firefox.pdf ├── chromium.pdf ├── src │ ├── Application │ │ ├── NegAbs.elm │ │ └── Sum.elm │ ├── Array │ │ └── Extra │ │ │ ├── MapToList.elm │ │ │ ├── Reverse.elm │ │ │ ├── Member.elm │ │ │ ├── IndexedMapToList.elm │ │ │ ├── FilterMap.elm │ │ │ ├── Unzip.elm │ │ │ ├── All.elm │ │ │ ├── Any.elm │ │ │ ├── Intersperse.elm │ │ │ └── Map2.elm │ └── Benchmarks.elm └── elm.json ├── tests ├── elm-verify-examples.json └── Tests.elm ├── .gitignore ├── elm-tooling.json ├── ChangeLog.md ├── package.json ├── README.md ├── elm.json ├── .github └── workflows │ └── unittests.yml ├── LICENSE ├── review ├── elm.json └── src │ └── ReviewConfig.elm └── src └── Array └── Extra.elm /benchmarks/firefox.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/array-extra/master/benchmarks/firefox.pdf -------------------------------------------------------------------------------- /tests/elm-verify-examples.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "../src", 3 | "tests": [ 4 | "Array.Extra" 5 | ] 6 | } -------------------------------------------------------------------------------- /benchmarks/chromium.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elm-community/array-extra/master/benchmarks/chromium.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build or dist files 2 | elm-stuff 3 | node_modules 4 | index.html 5 | 6 | # Ignore Generated files 7 | /tests/VerifyExamples/ 8 | -------------------------------------------------------------------------------- /elm-tooling.json: -------------------------------------------------------------------------------- 1 | { 2 | "tools": { 3 | "elm": "0.19.1", 4 | "elm-format": "0.8.5", 5 | "elm-json": "0.2.10" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # changelog 2 | 3 | ### 2.6.0 4 | 5 | - `member` add 6 | 7 | ### 2.5.0 8 | 9 | - `interweave` add 10 | 11 | ### 2.4.0 12 | 13 | - `all`, `any` add 14 | - `intersperse` add 15 | - `reverse` add 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "array-extra", 4 | "scripts": { 5 | "test": "npx --no-install elm-test", 6 | "postinstall": "elm-tooling install" 7 | }, 8 | "devDependencies": { 9 | "elm-test": "^0.19.1-revision7", 10 | "elm-tooling": "^1.3.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # convenience functions for working with `Array` 2 | 3 | Some people like to import under the same namespace as `Array`: 4 | 5 | ```elm 6 | import Array exposing (Array) 7 | import Array.Extra as Array 8 | 9 | firstFive : Array a -> Array a 10 | firstFive = 11 | Array.sliceUntil 5 12 | ``` 13 | 14 | Note that this API is experimental and likely to go through many more iterations. 15 | 16 | Feedback and contributions are very welcome. 17 | -------------------------------------------------------------------------------- /benchmarks/src/Application/NegAbs.elm: -------------------------------------------------------------------------------- 1 | module Application.NegAbs exposing (composeR, declarationArgumentPipeline, lambdaPipeComposeR, lambdaPipeline) 2 | 3 | 4 | declarationArgumentPipeline : number -> number 5 | declarationArgumentPipeline n = 6 | n |> abs |> negate 7 | 8 | 9 | lambdaPipeComposeR : number -> number 10 | lambdaPipeComposeR = 11 | \n -> n |> abs >> negate 12 | 13 | 14 | lambdaPipeline : number -> number 15 | lambdaPipeline = 16 | \n -> n |> abs |> negate 17 | 18 | 19 | composeR : number -> number 20 | composeR = 21 | abs >> negate 22 | -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/MapToList.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.MapToList exposing (withFoldr, withListMap) 2 | 3 | import Array exposing (Array) 4 | 5 | 6 | withFoldr : (a -> b) -> Array a -> List b 7 | withFoldr alter = 8 | \array -> 9 | array 10 | |> Array.foldr 11 | (\element soFar -> 12 | soFar |> (::) (element |> alter) 13 | ) 14 | [] 15 | 16 | 17 | withListMap : (a -> b) -> Array a -> List b 18 | withListMap alter = 19 | \array -> 20 | array |> Array.toList |> List.map alter 21 | -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/Reverse.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.Reverse exposing (withCons, withListReverse, withPush) 2 | 3 | import Array exposing (Array) 4 | 5 | 6 | withCons : Array a -> Array a 7 | withCons = 8 | \array -> 9 | array |> Array.foldl (::) [] |> Array.fromList 10 | 11 | 12 | withPush : Array a -> Array a 13 | withPush = 14 | \array -> 15 | array |> Array.foldl Array.push Array.empty 16 | 17 | 18 | withListReverse : Array a -> Array a 19 | withListReverse = 20 | \array -> 21 | array |> Array.toList |> List.reverse |> Array.fromList 22 | -------------------------------------------------------------------------------- /elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "package", 3 | "name": "elm-community/array-extra", 4 | "summary": "Convenience functions for working with Array", 5 | "license": "MIT", 6 | "version": "2.6.0", 7 | "exposed-modules": [ 8 | "Array.Extra" 9 | ], 10 | "elm-version": "0.19.0 <= v < 0.20.0", 11 | "dependencies": { 12 | "elm/core": "1.0.0 <= v < 2.0.0" 13 | }, 14 | "test-dependencies": { 15 | "elm/html": "1.0.0 <= v < 2.0.0", 16 | "elm-explorations/test": "1.2.2 <= v < 2.0.0", 17 | "elm/random": "1.0.0 <= v < 2.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /benchmarks/src/Application/Sum.elm: -------------------------------------------------------------------------------- 1 | module Application.Sum exposing (lambdaFullyAppliedCurried, lambdaNestedFullyAppliedCurried, nameOnlyCurried, partiallyCurried, pipe) 2 | 3 | import Array exposing (Array) 4 | 5 | 6 | nameOnlyCurried : Array number -> number 7 | nameOnlyCurried = 8 | \array -> 9 | array |> Array.foldl (+) 0 10 | 11 | 12 | partiallyCurried : Array number -> number 13 | partiallyCurried = 14 | \array -> 15 | array |> Array.foldl (\element -> (+) element) 0 16 | 17 | 18 | pipe : Array number -> number 19 | pipe = 20 | \array -> 21 | array |> Array.foldl (\element soFar -> soFar |> (+) element) 0 22 | 23 | 24 | lambdaFullyAppliedCurried : Array number -> number 25 | lambdaFullyAppliedCurried = 26 | \array -> 27 | array |> Array.foldl (\element soFar -> soFar + element) 0 28 | 29 | 30 | lambdaNestedFullyAppliedCurried : Array number -> number 31 | lambdaNestedFullyAppliedCurried = 32 | \array -> 33 | array |> Array.foldl (\element -> \soFar -> soFar + element) 0 34 | -------------------------------------------------------------------------------- /.github/workflows/unittests.yml: -------------------------------------------------------------------------------- 1 | name: Unit tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - "master" 7 | pull_request: 8 | 9 | jobs: 10 | main: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: 15.x 19 | 20 | - name: Cache node_modules 21 | id: cache-node_modules 22 | uses: actions/cache@v2 23 | with: 24 | path: node_modules 25 | key: node_modules-${{ hashFiles('package-lock.json') }} 26 | 27 | - name: Cache ~/.elm 28 | uses: actions/cache@v2 29 | with: 30 | path: ~/.elm 31 | key: elm-${{ hashFiles('./elm.json', './elm-tooling.json', './review/elm.json') }} 32 | 33 | - name: npm ci 34 | if: steps.cache-node_modules.outputs.cache-hit != 'true' 35 | run: npm ci 36 | 37 | - name: elm-tooling install 38 | run: npx --no-install elm-tooling install 39 | 40 | - name: elm-test 41 | run: npx --no-install elm-test 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 CircuitHub, elm-community 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /benchmarks/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src", 5 | "../src" 6 | ], 7 | "elm-version": "0.19.1", 8 | "dependencies": { 9 | "direct": { 10 | "elm/browser": "1.0.2", 11 | "elm/core": "1.0.5", 12 | "elm/html": "1.0.0", 13 | "elm-explorations/benchmark": "1.0.2", 14 | "lue-bird/elm-alternative-benchmark-runner": "1.0.0" 15 | }, 16 | "indirect": { 17 | "BrianHicks/elm-trend": "2.1.3", 18 | "avh4/elm-color": "1.0.0", 19 | "elm/json": "1.1.3", 20 | "elm/regex": "1.0.0", 21 | "elm/time": "1.0.0", 22 | "elm/url": "1.0.0", 23 | "elm/virtual-dom": "1.0.3", 24 | "mdgriffith/elm-ui": "1.1.8", 25 | "mdgriffith/style-elements": "5.0.2", 26 | "miniBill/elm-ui-with-context": "1.1.0", 27 | "robinheghan/murmur3": "1.0.0" 28 | } 29 | }, 30 | "test-dependencies": { 31 | "direct": { 32 | "elm-explorations/test": "1.2.2" 33 | }, 34 | "indirect": { 35 | "elm/random": "1.0.0" 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/Member.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.Member exposing (recursive, recursiveFromIndex, withAny, withFold, withList) 2 | 3 | import Array exposing (Array) 4 | import Array.Extra 5 | 6 | 7 | withFold : a -> Array a -> Bool 8 | withFold needle = 9 | \array -> 10 | array 11 | |> Array.foldl (\el soFar -> soFar || needle == el) False 12 | 13 | 14 | recursive : a -> Array a -> Bool 15 | recursive needle = 16 | \array -> 17 | array |> recursiveFromIndex 0 needle 18 | 19 | 20 | recursiveFromIndex : Int -> a -> Array a -> Bool 21 | recursiveFromIndex index needle = 22 | \array -> 23 | case array |> Array.get index of 24 | Just atIndex -> 25 | if atIndex == needle then 26 | True 27 | 28 | else 29 | array |> recursiveFromIndex (index + 1) needle 30 | 31 | Nothing -> 32 | False 33 | 34 | 35 | withList : a -> Array a -> Bool 36 | withList needle = 37 | \array -> 38 | array |> Array.toList |> List.member needle 39 | 40 | 41 | withAny : a -> Array a -> Bool 42 | withAny needle = 43 | \array -> 44 | array |> Array.Extra.any (\element -> element == needle) 45 | -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/IndexedMapToList.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.IndexedMapToList exposing (withArrayIndexedMap, withFoldr, withListIndexedMap, withToIndexedList) 2 | 3 | import Array exposing (Array) 4 | 5 | 6 | withFoldr : (Int -> a -> b) -> Array a -> List b 7 | withFoldr mapIndexAndElement = 8 | \array -> 9 | Array.foldr 10 | (\element ( i, soFar ) -> 11 | ( i - 1 12 | , soFar |> (::) (mapIndexAndElement i element) 13 | ) 14 | ) 15 | ( Array.length array - 1, [] ) 16 | array 17 | |> Tuple.second 18 | 19 | 20 | withToIndexedList : (Int -> b -> a) -> Array b -> List a 21 | withToIndexedList mapIndexAndValue = 22 | \array -> 23 | array 24 | |> Array.toIndexedList 25 | |> List.map (\( i, v ) -> mapIndexAndValue i v) 26 | 27 | 28 | withListIndexedMap : (Int -> a -> b) -> Array a -> List b 29 | withListIndexedMap mapIndexAndValue = 30 | \array -> 31 | array 32 | |> Array.toList 33 | |> List.indexedMap mapIndexAndValue 34 | 35 | 36 | withArrayIndexedMap : (Int -> a -> b) -> Array a -> List b 37 | withArrayIndexedMap mapIndexAndValue = 38 | \array -> 39 | array 40 | |> Array.indexedMap mapIndexAndValue 41 | |> Array.toList 42 | -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/FilterMap.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.FilterMap exposing (withCons, withListFilterMap, withPush) 2 | 3 | import Array exposing (Array) 4 | 5 | 6 | withPush : (a -> Maybe b) -> Array a -> Array b 7 | withPush elementParse = 8 | \array -> 9 | Array.foldl 10 | (\element soFar -> 11 | soFar |> pushTry (element |> elementParse) 12 | ) 13 | Array.empty 14 | array 15 | 16 | 17 | pushTry : Maybe a -> Array a -> Array a 18 | pushTry maybe = 19 | case maybe of 20 | Just value -> 21 | \array -> array |> Array.push value 22 | 23 | Nothing -> 24 | identity 25 | 26 | 27 | withListFilterMap : (a -> Maybe mapped) -> Array a -> Array mapped 28 | withListFilterMap tryMap = 29 | \array -> 30 | array 31 | |> Array.toList 32 | |> List.filterMap tryMap 33 | |> Array.fromList 34 | 35 | 36 | withCons : (a -> Maybe mapped) -> Array a -> Array mapped 37 | withCons tryChange = 38 | \array -> 39 | array 40 | |> Array.foldr 41 | (\el soFar -> soFar |> consTry (el |> tryChange)) 42 | [] 43 | |> Array.fromList 44 | 45 | 46 | consTry : Maybe a -> List a -> List a 47 | consTry maybeNewHead = 48 | \list -> 49 | case maybeNewHead of 50 | Just newHead -> 51 | newHead :: list 52 | 53 | Nothing -> 54 | list 55 | -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/Unzip.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.Unzip exposing (withListUnzip, withMaps, wthCons, wthPush) 2 | 3 | import Array exposing (Array) 4 | 5 | 6 | withMaps : Array ( a, b ) -> ( Array a, Array b ) 7 | withMaps = 8 | \arrayOfTuple -> 9 | ( arrayOfTuple |> Array.map Tuple.first 10 | , arrayOfTuple |> Array.map Tuple.second 11 | ) 12 | 13 | 14 | withListUnzip : Array ( a, b ) -> ( Array a, Array b ) 15 | withListUnzip = 16 | \arrayOfTuple -> 17 | arrayOfTuple 18 | |> Array.toList 19 | |> List.unzip 20 | |> Tuple.mapBoth Array.fromList Array.fromList 21 | 22 | 23 | wthPush : Array ( a, b ) -> ( Array a, Array b ) 24 | wthPush = 25 | \arrayOfTuple -> 26 | arrayOfTuple 27 | |> Array.foldl 28 | (\( a, b ) ( arrayOfASoFar, arrayOfBSoFar ) -> 29 | ( arrayOfASoFar |> Array.push a 30 | , arrayOfBSoFar |> Array.push b 31 | ) 32 | ) 33 | ( Array.empty, Array.empty ) 34 | 35 | 36 | wthCons : Array ( a, b ) -> ( Array a, Array b ) 37 | wthCons = 38 | \arrayOfTuple -> 39 | arrayOfTuple 40 | |> Array.foldr 41 | (\( a, b ) ( arrayOfASoFar, arrayOfBSoFar ) -> 42 | ( arrayOfASoFar |> (::) a 43 | , arrayOfBSoFar |> (::) b 44 | ) 45 | ) 46 | ( [], [] ) 47 | |> Tuple.mapBoth Array.fromList Array.fromList 48 | -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/All.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.All exposing (recursiveGet, recursiveLast, withFold, withListAll) 2 | 3 | import Array exposing (Array) 4 | import Array.Extra 5 | 6 | 7 | recursiveLast : (a -> Bool) -> Array a -> Bool 8 | recursiveLast isOkay = 9 | \array -> 10 | case array |> Array.get ((array |> Array.length) - 1) of 11 | Nothing -> 12 | True 13 | 14 | Just last -> 15 | if isOkay last then 16 | recursiveLast isOkay (Array.Extra.pop array) 17 | 18 | else 19 | False 20 | 21 | 22 | withListAll : (a -> Bool) -> Array a -> Bool 23 | withListAll isOkay = 24 | \array -> 25 | array 26 | |> Array.toList 27 | |> List.all isOkay 28 | 29 | 30 | withFold : (a -> Bool) -> Array a -> Bool 31 | withFold isOkay = 32 | \array -> 33 | array 34 | |> Array.foldl 35 | (\element soFar -> soFar && (element |> isOkay)) 36 | True 37 | 38 | 39 | recursiveGet : (a -> Bool) -> Array a -> Bool 40 | recursiveGet isOkay array = 41 | recursiveGetFromIndex 0 isOkay array 42 | 43 | 44 | recursiveGetFromIndex : Int -> (a -> Bool) -> Array a -> Bool 45 | recursiveGetFromIndex index isOkay array = 46 | case Array.get index array |> Maybe.map isOkay of 47 | Just False -> 48 | False 49 | 50 | Just True -> 51 | recursiveGetFromIndex (index + 1) isOkay array 52 | 53 | Nothing -> 54 | True 55 | -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/Any.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.Any exposing (recursiveGet, recursiveLast, withFold, withList) 2 | 3 | import Array exposing (Array) 4 | import Array.Extra 5 | 6 | 7 | withList : (a -> Bool) -> Array a -> Bool 8 | withList isOkay = 9 | \array -> 10 | array 11 | |> Array.toList 12 | |> List.any isOkay 13 | 14 | 15 | withFold : (a -> Bool) -> Array a -> Bool 16 | withFold isOkay = 17 | \array -> 18 | array 19 | |> Array.foldl 20 | (\element soFar -> soFar || (element |> isOkay)) 21 | False 22 | 23 | 24 | recursiveLast : (a -> Bool) -> Array a -> Bool 25 | recursiveLast isOkay = 26 | \array -> 27 | -- read & write is faster on the last element 28 | case array |> Array.get ((array |> Array.length) - 1) of 29 | Nothing -> 30 | False 31 | 32 | Just last -> 33 | if last |> isOkay then 34 | True 35 | 36 | else 37 | array |> Array.Extra.pop |> recursiveLast isOkay 38 | 39 | 40 | recursiveGet : (a -> Bool) -> Array a -> Bool 41 | recursiveGet isOkay array = 42 | recursiveGetFromIndex 0 isOkay array 43 | 44 | 45 | recursiveGetFromIndex : Int -> (a -> Bool) -> Array a -> Bool 46 | recursiveGetFromIndex index isOkay array = 47 | case Array.get index array |> Maybe.map isOkay of 48 | Just True -> 49 | True 50 | 51 | Just False -> 52 | recursiveGetFromIndex (index + 1) isOkay array 53 | 54 | Nothing -> 55 | False 56 | -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/Intersperse.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.Intersperse exposing (withCons, withListIntersperse, withPush) 2 | 3 | import Array exposing (Array) 4 | import Array.Extra 5 | 6 | 7 | withPush : a -> Array a -> Array a 8 | withPush separator = 9 | \array -> 10 | case array |> Array.get ((array |> Array.length) - 1) of 11 | Nothing -> 12 | Array.empty 13 | 14 | Just last -> 15 | let 16 | step element = 17 | \beforeStep -> 18 | beforeStep 19 | |> Array.push element 20 | |> Array.push separator 21 | 22 | beforeLastInterspersed = 23 | array 24 | |> Array.Extra.pop 25 | |> Array.foldr step Array.empty 26 | in 27 | beforeLastInterspersed |> Array.push last 28 | 29 | 30 | withCons : a -> Array a -> Array a 31 | withCons separator = 32 | \array -> 33 | case array |> Array.get ((array |> Array.length) - 1) of 34 | Nothing -> 35 | Array.empty 36 | 37 | Just last -> 38 | let 39 | step element = 40 | \beforeStep -> 41 | beforeStep 42 | |> (::) element 43 | |> (::) separator 44 | 45 | beforeLastInterspersed = 46 | array 47 | |> Array.Extra.pop 48 | |> Array.foldl step [] 49 | in 50 | beforeLastInterspersed |> (::) last |> List.reverse |> Array.fromList 51 | 52 | 53 | withListIntersperse : a -> Array a -> Array a 54 | withListIntersperse separator = 55 | \array -> 56 | array 57 | |> Array.toList 58 | |> List.intersperse separator 59 | |> Array.fromList 60 | -------------------------------------------------------------------------------- /benchmarks/src/Array/Extra/Map2.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra.Map2 exposing (withGet, withListMap2, withUncons) 2 | 3 | import Array exposing (Array) 4 | import Array.Extra 5 | import Html exposing (b) 6 | 7 | 8 | withListMap2 : (a -> b -> c) -> Array a -> Array b -> Array c 9 | withListMap2 combine aArray bArray = 10 | List.map2 combine 11 | (aArray |> Array.toList) 12 | (bArray |> Array.toList) 13 | |> Array.fromList 14 | 15 | 16 | withGet : (a -> b -> c) -> Array a -> Array b -> Array c 17 | withGet combine aArray bArray = 18 | aArray 19 | |> Array.foldl 20 | (\aElement state -> 21 | if state.bsExhausted then 22 | state 23 | 24 | else 25 | case bArray |> Array.get state.index of 26 | Just bElement -> 27 | { state 28 | | combined = 29 | state.combined 30 | |> (::) (combine aElement bElement) 31 | , index = state.index + 1 32 | } 33 | 34 | Nothing -> 35 | { state | bsExhausted = True } 36 | ) 37 | { bsExhausted = False 38 | , index = 0 39 | , combined = [] 40 | } 41 | |> .combined 42 | |> List.reverse 43 | |> Array.fromList 44 | 45 | 46 | withUncons : (a -> b -> c) -> Array a -> Array b -> Array c 47 | withUncons combine aArray bArray = 48 | aArray 49 | |> Array.foldl 50 | (\aElement state -> 51 | case state.bsRemaining of 52 | [] -> 53 | state 54 | 55 | bsRemainingHead :: bsRemainingTail -> 56 | { combined = 57 | state.combined 58 | |> (::) (combine aElement bsRemainingHead) 59 | , bsRemaining = bsRemainingTail 60 | } 61 | ) 62 | { bsRemaining = bArray |> Array.toList 63 | , combined = [] 64 | } 65 | |> .combined 66 | |> List.reverse 67 | |> Array.fromList 68 | -------------------------------------------------------------------------------- /review/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src" 5 | ], 6 | "elm-version": "0.19.1", 7 | "dependencies": { 8 | "direct": { 9 | "NeoVier/elm-review-no-function-outside-of-modules": "2.0.0", 10 | "SiriusStarr/elm-review-no-single-pattern-case": "2.0.1", 11 | "SiriusStarr/elm-review-no-unsorted": "1.1.2", 12 | "SiriusStarr/elm-review-pipeline-styles": "1.3.3", 13 | "dillonkearns/elm-review-no-primitive-type-alias": "1.0.0", 14 | "elm/core": "1.0.5", 15 | "jfmengels/elm-review": "2.9.0", 16 | "jfmengels/elm-review-common": "1.2.3", 17 | "jfmengels/elm-review-debug": "1.0.6", 18 | "jfmengels/elm-review-documentation": "2.0.2", 19 | "jfmengels/elm-review-simplify": "2.0.21", 20 | "jfmengels/elm-review-unused": "1.1.25", 21 | "lue-bird/elm-review-record-alias-constructor": "1.1.0", 22 | "lue-bird/elm-review-single-use-type-vars-end-with-underscore": "2.0.2", 23 | "sparksp/elm-review-always": "1.0.5", 24 | "sparksp/elm-review-forbidden-words": "1.0.1", 25 | "truqu/elm-review-nobooleancase": "1.0.0" 26 | }, 27 | "indirect": { 28 | "Chadtech/elm-bool-extra": "2.4.2", 29 | "avh4/elm-fifo": "1.0.4", 30 | "elm/html": "1.0.0", 31 | "elm/json": "1.1.3", 32 | "elm/parser": "1.1.0", 33 | "elm/project-metadata-utils": "1.0.2", 34 | "elm/random": "1.0.0", 35 | "elm/regex": "1.0.0", 36 | "elm/time": "1.0.0", 37 | "elm/virtual-dom": "1.0.3", 38 | "elm-community/basics-extra": "4.1.0", 39 | "elm-community/dict-extra": "2.4.0", 40 | "elm-community/graph": "6.0.0", 41 | "elm-community/intdict": "3.0.0", 42 | "elm-community/list-extra": "8.6.0", 43 | "elm-community/maybe-extra": "5.3.0", 44 | "elm-community/result-extra": "2.4.0", 45 | "elm-community/string-extra": "4.0.1", 46 | "elm-explorations/test": "1.2.2", 47 | "miniBill/elm-unicode": "1.0.2", 48 | "pzp1997/assoc-list": "1.0.0", 49 | "rtfeldman/elm-hex": "1.0.0", 50 | "stil4m/elm-syntax": "7.2.9", 51 | "stil4m/structured-writer": "1.0.3", 52 | "the-sett/elm-pretty-printer": "3.0.0", 53 | "the-sett/elm-syntax-dsl": "6.0.2", 54 | "turboMaCk/non-empty-list-alias": "1.3.0" 55 | } 56 | }, 57 | "test-dependencies": { 58 | "direct": {}, 59 | "indirect": {} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /benchmarks/src/Benchmarks.elm: -------------------------------------------------------------------------------- 1 | module Benchmarks exposing (main) 2 | 3 | import Application.NegAbs 4 | import Application.Sum 5 | import Array exposing (Array) 6 | import Array.Extra as Array 7 | import Array.Extra.All 8 | import Array.Extra.Any 9 | import Array.Extra.FilterMap 10 | import Array.Extra.IndexedMapToList 11 | import Array.Extra.Intersperse 12 | import Array.Extra.Map2 13 | import Array.Extra.MapToList 14 | import Array.Extra.Member 15 | import Array.Extra.Reverse 16 | import Array.Extra.Unzip 17 | import Benchmark exposing (Benchmark, describe) 18 | import Benchmark.Alternative exposing (rank) 19 | import Benchmark.Runner.Alternative as BenchmarkRunner 20 | 21 | 22 | main : BenchmarkRunner.Program 23 | main = 24 | describe "for array-extra" 25 | [ application 26 | , array 27 | , arrayExtra 28 | ] 29 | |> BenchmarkRunner.program 30 | 31 | 32 | application : Benchmark 33 | application = 34 | describe "application" 35 | [ rank "curry" 36 | (\sum -> ints1To100 |> sum) 37 | [ ( "name only", Application.Sum.nameOnlyCurried ) 38 | , ( "partially curried/applied", Application.Sum.partiallyCurried ) 39 | , ( "lambda, piping", Application.Sum.pipe ) 40 | , ( "lambda, fully applied", Application.Sum.lambdaFullyAppliedCurried ) 41 | , ( "lambda nested, fully applied", Application.Sum.lambdaNestedFullyAppliedCurried ) 42 | ] 43 | , rank "chain" 44 | (\negAbs -> ints1To100 |> Array.map negAbs) 45 | [ ( "declaration argument, |> |>", Application.NegAbs.declarationArgumentPipeline ) 46 | , ( "lambda, |> |>", Application.NegAbs.lambdaPipeline ) 47 | , ( "lambda, |> >>", Application.NegAbs.lambdaPipeComposeR ) 48 | , ( ">>", Application.NegAbs.composeR ) 49 | ] 50 | ] 51 | 52 | 53 | array : Benchmark 54 | array = 55 | describe "Array" 56 | [ rank "Array.fold" 57 | (\fold -> ints1To100 |> fold (+) 0) 58 | [ ( "foldl", Array.foldl ) 59 | , ( "foldr", Array.foldr ) 60 | ] 61 | ] 62 | 63 | 64 | arrayExtra : Benchmark 65 | arrayExtra = 66 | describe "Array.Extra" 67 | [ rank "mapToList" 68 | (\mapToList -> ints1To100 |> mapToList negate) 69 | [ ( "with foldr", Array.Extra.MapToList.withFoldr ) 70 | , ( "with Array.toIndexedList", Array.Extra.MapToList.withListMap ) 71 | ] 72 | , rank "indexedMapToList" 73 | (\indexedMapToList -> 74 | ints1To100 |> indexedMapToList Tuple.pair 75 | ) 76 | [ ( "with Array.foldr", Array.Extra.IndexedMapToList.withFoldr ) 77 | , ( "with toIndexedList" 78 | , Array.Extra.IndexedMapToList.withToIndexedList 79 | ) 80 | , ( "with Array.indexedMap" 81 | , Array.Extra.IndexedMapToList.withArrayIndexedMap 82 | ) 83 | , ( "with List.indexedMap" 84 | , Array.Extra.IndexedMapToList.withListIndexedMap 85 | ) 86 | ] 87 | , rank "reverse" 88 | (\reverse -> reverse ints1To100) 89 | [ ( "with cons", Array.Extra.Reverse.withCons ) 90 | , ( "with List.reverse", Array.Extra.Reverse.withListReverse ) 91 | , ( "with push", Array.Extra.Reverse.withPush ) 92 | ] 93 | , let 94 | zipped = 95 | Array.zip ints1To100 ints1To100 96 | in 97 | rank "unzip" 98 | (\unzip -> zipped |> unzip) 99 | [ ( "with maps", Array.Extra.Unzip.withMaps ) 100 | , ( "with List.unzip", Array.Extra.Unzip.withListUnzip ) 101 | , ( "with push", Array.Extra.Unzip.wthPush ) 102 | , ( "with cons", Array.Extra.Unzip.wthCons ) 103 | ] 104 | , rank "map2" 105 | (\map2 -> 106 | map2 Tuple.pair ints1To100 ints1To100 107 | ) 108 | [ ( "with List.map2", Array.Extra.Map2.withListMap2 ) 109 | , ( "with get", Array.Extra.Map2.withGet ) 110 | , ( "with un-cons", Array.Extra.Map2.withUncons ) 111 | ] 112 | , let 113 | maybeInts = 114 | Array.initialize 100 115 | (\x -> 116 | if (x |> modBy 3) == 0 then 117 | Nothing 118 | 119 | else 120 | Just x 121 | ) 122 | in 123 | rank "filterMap" 124 | (\filterMap -> maybeInts |> filterMap identity) 125 | [ ( "with List.filterMap", Array.Extra.FilterMap.withListFilterMap ) 126 | , ( "with push", Array.Extra.FilterMap.withPush ) 127 | , ( "with cons", Array.Extra.FilterMap.withCons ) 128 | ] 129 | , let 130 | allTrue = 131 | Array.repeat 100 True 132 | in 133 | rank "all" 134 | (\all -> allTrue |> all identity) 135 | [ ( "recursive last", Array.Extra.All.recursiveLast ) 136 | , ( "recursive get", Array.Extra.All.recursiveGet ) 137 | , ( "with List.all", Array.Extra.All.withListAll ) 138 | , ( "with fold", Array.Extra.All.withFold ) 139 | ] 140 | , let 141 | allFalse = 142 | Array.repeat 100 False 143 | in 144 | rank "any" 145 | (\any -> allFalse |> any identity) 146 | [ ( "recursive last", Array.Extra.Any.recursiveLast ) 147 | , ( "recursive get", Array.Extra.Any.recursiveGet ) 148 | , ( "with List.any", Array.Extra.Any.withList ) 149 | , ( "with fold", Array.Extra.Any.withFold ) 150 | ] 151 | , rank "intersperse" 152 | (\intersperse -> ints1To100 |> intersperse 0) 153 | [ ( "with push", Array.Extra.Intersperse.withPush ) 154 | , ( "with cons", Array.Extra.Intersperse.withCons ) 155 | , ( "with List.intersperse", Array.Extra.Intersperse.withListIntersperse ) 156 | ] 157 | , rank "member" 158 | (\member -> member 50 ints1To100) 159 | [ ( "with fold", Array.Extra.Member.withFold ) 160 | , ( "recursive", Array.Extra.Member.recursive ) 161 | , ( "with List.member", Array.Extra.Member.withList ) 162 | , ( "with any", Array.Extra.Member.withAny ) 163 | ] 164 | ] 165 | 166 | 167 | ints1To100 : Array Int 168 | ints1To100 = 169 | Array.fromList (List.range 1 100) 170 | -------------------------------------------------------------------------------- /review/src/ReviewConfig.elm: -------------------------------------------------------------------------------- 1 | module ReviewConfig exposing (config) 2 | 3 | {-| Do not rename the ReviewConfig module or the config function, because 4 | `elm-review` will look for these. 5 | 6 | To add packages that contain rules, add them to this review project using 7 | 8 | `elm install author/packagename` 9 | 10 | when inside the directory containing this file. 11 | 12 | 13 | ## on radar 14 | 15 | - [`jfmengels/elm-review-cognitive-complexity`](https://dark.elm.dmy.fr/packages/jfmengels/elm-review-cognitive-complexity/latest/CognitiveComplexity) 16 | - I currently fear that there will be 17 | exceptions where we can't 18 | abstract/simplify functions further 19 | 20 | 21 | ## rejected 22 | 23 | Pipeline.parentheticalApplicationPipelines 24 | |> Pipeline.forbid 25 | |> Pipeline.that 26 | (PipelinePredicate.haveAnyNonInputStepThatIs 27 | PipelinePredicate.aSemanticallyInfixFunction 28 | ) 29 | |> Pipeline.andTryToFixThemBy PipelineFix.convertingToRightPizza 30 | |> Pipeline.andCallThem "parenthetical application of a semantically-infix function" 31 | 32 | because `aSemanticallyInfixFunction` covers `atLeast`/`atMost`/... which can be used in for example: `Morph.atLeast n3 AToZ.char` 33 | 34 | Pipeline.rightCompositionPipelines 35 | |> Pipeline.forbid 36 | |> Pipeline.andReportCustomError 37 | ">> pipeline" 38 | [ "Forbidding `g >> f` for reasons of simplicity, consistency:" 39 | , [ "Establish a subject: `List.map (\\user -> user |> User.badgeAdd ... |> User.levelIncrease)`" 40 | , " for easier readability and scalability (maybe even creating a separate function)" 41 | , " when chaining multiple operations" 42 | ] 43 | |> String.concat 44 | ] 45 | 46 | because they can improve food scoping 47 | 48 | - [`truqu/elm-review-nobooleancase`](https://dark.elm.dmy.fr/packages/truqu/elm-review-nobooleancase/latest/) 49 | preferably, I'd completely remove `Bool`s and with it `if ... then ... else ...` 50 | - completely covered by elm-review-simplify 51 | 52 | -} 53 | 54 | import Docs.NoMissing 55 | import Docs.ReviewAtDocs 56 | import Docs.ReviewLinksAndSections 57 | import NoAlways 58 | import NoDebug.Log 59 | import NoDebug.TodoOrToString 60 | import NoExposingEverything 61 | import NoForbiddenWords 62 | import NoFunctionOutsideOfModules 63 | import NoImportingEverything 64 | import NoMissingTypeAnnotation 65 | import NoMissingTypeExpose 66 | import NoPrimitiveTypeAlias 67 | import NoRecordAliasConstructor 68 | import NoSinglePatternCase 69 | import NoUnsortedLetDeclarations 70 | import NoUnsortedTopLevelDeclarations 71 | import NoUnused.CustomTypeConstructorArgs 72 | import NoUnused.CustomTypeConstructors 73 | import NoUnused.Dependencies 74 | import NoUnused.Exports 75 | import NoUnused.Modules 76 | import NoUnused.Parameters 77 | import NoUnused.Patterns 78 | import NoUnused.Variables 79 | import OnlyAllSingleUseTypeVarsEndWith_ 80 | import Review.Rule as Rule exposing (Rule) 81 | import ReviewPipelineStyles as Pipeline 82 | import ReviewPipelineStyles.Fixes as PipelineFix 83 | import ReviewPipelineStyles.Predicates as PipelinePredicate 84 | import ReviewPipelineStyles.Premade as Pipeline 85 | import Simplify 86 | 87 | 88 | config : List Rule 89 | config = 90 | [ -- ## documentation 91 | Docs.ReviewLinksAndSections.rule 92 | , Docs.ReviewAtDocs.rule 93 | , Docs.NoMissing.rule 94 | { document = Docs.NoMissing.onlyExposed 95 | , from = Docs.NoMissing.exposedModules 96 | } 97 | 98 | -- ## simplify 99 | , NoUnused.CustomTypeConstructors.rule [] 100 | , NoUnused.CustomTypeConstructorArgs.rule 101 | , NoUnused.Dependencies.rule 102 | , NoUnused.Exports.rule 103 | , NoUnused.Modules.rule 104 | , NoUnused.Parameters.rule 105 | , NoUnused.Patterns.rule 106 | , NoUnused.Variables.rule 107 | , Simplify.rule Simplify.defaults 108 | , NoSinglePatternCase.rule 109 | (NoSinglePatternCase.fixInArgument 110 | |> NoSinglePatternCase.ifAsPatternRequired 111 | (NoSinglePatternCase.fixInLetInstead 112 | |> NoSinglePatternCase.andIfNoLetExists 113 | NoSinglePatternCase.createNewLet 114 | ) 115 | ) 116 | 117 | -- ## limit 118 | , [ [ [ Pipeline.rightPizzaPipelines 119 | |> Pipeline.forbid 120 | |> Pipeline.that 121 | (PipelinePredicate.haveAnyStepThatIs 122 | PipelinePredicate.aConfusingNonCommutativeFunction 123 | ) 124 | |> Pipeline.andCallThem 125 | "|> pipeline with confusing non-commutative function" 126 | , Pipeline.parentheticalApplicationPipelines 127 | |> Pipeline.forbid 128 | |> Pipeline.that 129 | (PipelinePredicate.haveAnyStepThatIs 130 | PipelinePredicate.aConfusingNonCommutativePrefixOperator 131 | ) 132 | |> Pipeline.andCallThem 133 | "parenthetical application with confusing non-commutative prefix operator" 134 | ] 135 | ] 136 | |> List.concat 137 | , [ Pipeline.leftPizzaPipelines 138 | |> Pipeline.forbid 139 | |> Pipeline.andTryToFixThemBy 140 | PipelineFix.convertingToParentheticalApplication 141 | |> Pipeline.andReportCustomError 142 | "<| pipeline" 143 | [ "Forbidding `f <| a s` for reasons of simplicity, consistency:" 144 | , " - Pipe data before the function: `food |> op ...`" 145 | , " - Feed arguments after the function: `... |> opWith (a ...) (b ...)`" 146 | , "Use the application style `f (a s)` instead" 147 | ] 148 | , Pipeline.leftCompositionPipelines 149 | |> Pipeline.forbid 150 | |> Pipeline.andReportCustomError 151 | "<< pipeline" 152 | [ "Forbidding `g << f` for reasons of simplicity, readability, consistency:" 153 | , " - Keep the order data comes from before and gets piped through functions after: `... |> opF |> opG`" 154 | , [ "Establish a subject: `List.map (\\user -> user |> User.badgeAdd ... |> User.levelIncrease)`" 155 | , " for easier readability and scalability (maybe even creating a separate function)" 156 | , " when chaining multiple operations" 157 | ] 158 | |> String.concat 159 | ] 160 | ] 161 | ] 162 | |> List.concat 163 | |> Pipeline.rule 164 | , NoPrimitiveTypeAlias.rule 165 | , OnlyAllSingleUseTypeVarsEndWith_.rule 166 | , NoRecordAliasConstructor.rule 167 | , NoDebug.TodoOrToString.rule 168 | |> Rule.ignoreErrorsForDirectories [ "tests/" ] 169 | , NoExposingEverything.rule 170 | , NoForbiddenWords.rule forbiddenWords 171 | , NoImportingEverything.rule [] 172 | , NoMissingTypeAnnotation.rule 173 | , NoMissingTypeExpose.rule 174 | , NoUnsortedTopLevelDeclarations.rule 175 | (NoUnsortedTopLevelDeclarations.sortTopLevelDeclarations 176 | |> NoUnsortedTopLevelDeclarations.glueHelpersAfter 177 | |> NoUnsortedTopLevelDeclarations.glueDependenciesBeforeFirstDependent 178 | ) 179 | , NoUnsortedLetDeclarations.rule 180 | (NoUnsortedLetDeclarations.sortLetDeclarations 181 | |> NoUnsortedLetDeclarations.glueDependenciesBeforeFirstDependent 182 | ) 183 | , NoFunctionOutsideOfModules.rule 184 | [ ( forbiddenFunctionOrValues, [] ) ] 185 | , NoAlways.rule 186 | , NoDebug.Log.rule 187 | ] 188 | 189 | 190 | forbiddenFunctionOrValues : List String 191 | forbiddenFunctionOrValues = 192 | [ -- use tuple destructuring instead 193 | -- for improved descriptiveness 194 | "Tuple.first" 195 | , "Tuple.second" 196 | , -- use `mapFirst |> mapSecond` instead 197 | "Tuple.mapBoth" 198 | , -- use `String.indexes` instead 199 | "String.indices" 200 | , -- use a `case` instead 201 | "String.isEmpty" 202 | , "List.isEmpty" 203 | , "List.tail" 204 | 205 | -- use a `Set`, `Dict` or `List.sortWith` 206 | , "List.sort" 207 | , "List.sortBy" 208 | ] 209 | 210 | 211 | forbiddenWords : List String 212 | forbiddenWords = 213 | [ [ "REPLACEME", "replaceme", "replace-me", "ReplaceMe" ] 214 | , [ "ToReplace", "TOREPLACE", "to-replace" ] 215 | , [ "TODO", "todo", "Todo", "to-do", "ToDo" ] 216 | , [ "- []" ] 217 | , [ "ToCheck", "to-check" ] 218 | , [ "ToFix", "TOFIX" ] 219 | , [ "FIXME", "fixme", "FixMe", "Fixme" ] 220 | ] 221 | |> List.concat 222 | -------------------------------------------------------------------------------- /src/Array/Extra.elm: -------------------------------------------------------------------------------- 1 | module Array.Extra exposing 2 | ( all, any, member 3 | , reverse, intersperse 4 | , update, pop, removeAt, insertAt 5 | , removeWhen, filterMap 6 | , sliceFrom, sliceUntil, splitAt, unzip 7 | , interweave, apply, map2, map3, map4, map5, zip, zip3 8 | , resizelRepeat, resizerRepeat, resizelIndexed, resizerIndexed 9 | , mapToList, indexedMapToList 10 | ) 11 | 12 | {-| Convenience functions for working with `Array` 13 | 14 | 15 | # observe 16 | 17 | @docs all, any, member 18 | 19 | 20 | # alter 21 | 22 | @docs reverse, intersperse 23 | @docs update, pop, removeAt, insertAt 24 | 25 | 26 | ## filter 27 | 28 | @docs removeWhen, filterMap 29 | 30 | 31 | ## part 32 | 33 | @docs sliceFrom, sliceUntil, splitAt, unzip 34 | 35 | 36 | ## combine 37 | 38 | @docs interweave, apply, map2, map3, map4, map5, zip, zip3 39 | 40 | 41 | ## resize 42 | 43 | @docs resizelRepeat, resizerRepeat, resizelIndexed, resizerIndexed 44 | 45 | 46 | # transform 47 | 48 | @docs mapToList, indexedMapToList 49 | 50 | -} 51 | 52 | import Array exposing (Array, append, empty, initialize, length, repeat, slice) 53 | 54 | 55 | {-| Update the element at a given index based on its current value. 56 | If the index is out of bounds, nothing is changed. 57 | 58 | import Array exposing (fromList) 59 | 60 | fromList [ 1, 2, 3 ] |> update 1 (\n -> n + 10) 61 | --> fromList [ 1, 12, 3 ] 62 | 63 | fromList [ 1, 2, 3 ] |> update 4 (\n -> n + 10) 64 | --> fromList [ 1, 2, 3 ] 65 | 66 | fromList [ 1, 2, 3 ] |> update -1 (\n -> n + 10) 67 | --> fromList [ 1, 2, 3 ] 68 | 69 | -} 70 | update : 71 | Int 72 | -> (element -> element) 73 | -> (Array element -> Array element) 74 | update index alter = 75 | \array -> 76 | case array |> Array.get index of 77 | Nothing -> 78 | array 79 | 80 | Just element -> 81 | array |> Array.set index (alter element) 82 | 83 | 84 | {-| Drop a given number of elements from the start. 85 | In other words, slice the `Array` from an index until the very end. 86 | Given a negative argument, count the end of the slice from the end. 87 | 88 | import Array exposing (fromList) 89 | 90 | fromList (List.range 0 6) |> sliceFrom 3 91 | --> fromList [ 3, 4, 5, 6 ] 92 | 93 | fromList (List.range 0 6) |> sliceFrom -3 94 | --> fromList [ 4, 5, 6 ] 95 | 96 | -} 97 | sliceFrom : Int -> (Array element -> Array element) 98 | sliceFrom lengthDropped = 99 | \array -> 100 | array |> slice lengthDropped (array |> length) 101 | 102 | 103 | {-| Take a number of elements from the start. 104 | In other words, slice the `Array` from the very beginning until not including the index. 105 | Given a negative argument, count the beginning of the slice from the end. 106 | 107 | import Array exposing (fromList) 108 | 109 | fromList (List.range 0 6) |> sliceUntil 3 110 | --> fromList [ 0, 1, 2 ] 111 | 112 | fromList (List.range 0 6) |> sliceUntil -3 113 | --> fromList [ 0, 1, 2, 3 ] 114 | 115 | -} 116 | sliceUntil : Int -> (Array element -> Array element) 117 | sliceUntil lengthNew = 118 | \array -> 119 | array 120 | |> slice 0 121 | (if lengthNew >= 0 then 122 | lengthNew 123 | 124 | else 125 | (array |> length) + lengthNew 126 | ) 127 | 128 | 129 | {-| Remove the last element. 130 | 131 | import Array exposing (fromList, empty) 132 | 133 | fromList [ 1, 2, 3 ] |> pop 134 | --> fromList [ 1, 2 ] 135 | 136 | empty |> pop 137 | --> empty 138 | 139 | -} 140 | pop : Array element -> Array element 141 | pop = 142 | \array -> array |> slice 0 -1 143 | 144 | 145 | {-| Place a value between all elements. 146 | 147 | import Array exposing (fromList) 148 | 149 | fromList [ "turtles", "turtles", "turtles" ] 150 | |> intersperse "on" 151 | --> fromList 152 | --> [ "turtles", "on", "turtles", "on", "turtles" ] 153 | 154 | To interlace an `Array`, [`interweave`](#interweave). 155 | 156 | -} 157 | intersperse : element -> (Array element -> Array element) 158 | intersperse separator = 159 | \array -> 160 | array 161 | |> Array.toList 162 | |> List.intersperse separator 163 | |> Array.fromList 164 | 165 | 166 | {-| Try transforming all elements but only keep the successes. 167 | 168 | import Array exposing (fromList) 169 | 170 | fromList [ "3", "4.0", "5", "hats" ] 171 | |> filterMap String.toInt 172 | --> fromList [ 3, 5 ] 173 | 174 | -} 175 | filterMap : 176 | (element -> Maybe narrowElement) 177 | -> (Array element -> Array narrowElement) 178 | filterMap tryMap = 179 | \array -> 180 | array 181 | |> Array.foldr 182 | (\el soFar -> soFar |> consTry (el |> tryMap)) 183 | [] 184 | |> Array.fromList 185 | 186 | 187 | consTry : Maybe a -> List a -> List a 188 | consTry maybeNewHead = 189 | \list -> 190 | case maybeNewHead of 191 | Just newHead -> 192 | newHead :: list 193 | 194 | Nothing -> 195 | list 196 | 197 | 198 | {-| Apply a given `Array` of changes to all elements. 199 | If one `Array` is longer, its extra elements are not used. 200 | 201 | import Array exposing (fromList, repeat) 202 | 203 | repeat 5 100 204 | |> apply 205 | (fromList 206 | [ \x -> -x, identity, (+) 10 ] 207 | ) 208 | --> fromList [ -100, 100, 110 ] 209 | 210 | -} 211 | apply : 212 | Array (element -> mappedElement) 213 | -> (Array element -> Array mappedElement) 214 | apply changes = 215 | \array -> 216 | array |> map2 (\map element -> map element) changes 217 | 218 | 219 | {-| Apply a function to the elements in the array and collect the result in a List. 220 | 221 | import Array exposing (fromList) 222 | import Html 223 | 224 | fromList [ "a", "b", "c" ] 225 | |> mapToList Html.text 226 | --> [ Html.text "a", Html.text "b", Html.text "c" ] 227 | 228 | -} 229 | mapToList : 230 | (element -> mappedElement) 231 | -> (Array element -> List mappedElement) 232 | mapToList elementChange = 233 | \array -> 234 | array 235 | |> Array.foldr 236 | (\element soFar -> 237 | soFar |> (::) (element |> elementChange) 238 | ) 239 | [] 240 | 241 | 242 | {-| Transform all elements with their indexes as the first argument 243 | and collect the result in a `List`. 244 | 245 | import Array exposing (Array, fromList) 246 | import Html exposing (Html) 247 | 248 | type alias Exercise = 249 | { name : String } 250 | 251 | exerciseRender : Int -> Exercise -> Html msg 252 | exerciseRender index = 253 | \exercise -> 254 | String.concat 255 | [ "Exercise #" 256 | , String.fromInt (index + 1) 257 | , " - " 258 | , exercise.name 259 | ] 260 | |> Html.text 261 | 262 | exercisesRender : Array Exercise -> Html msg 263 | exercisesRender = 264 | indexedMapToList renderExercise 265 | >> Html.div [] 266 | 267 | -} 268 | indexedMapToList : 269 | (Int -> element -> mappedElement) 270 | -> (Array element -> List mappedElement) 271 | indexedMapToList mapIndexedElement = 272 | \array -> 273 | array 274 | |> Array.foldr 275 | (\element ( i, listSoFar ) -> 276 | ( i - 1 277 | , listSoFar |> (::) (mapIndexedElement i element) 278 | ) 279 | ) 280 | ( (array |> length) - 1, [] ) 281 | |> Tuple.second 282 | 283 | 284 | {-| Combine the elements of two `Array`s with a given function. 285 | If one `Array` is longer, its extra elements are not used. 286 | 287 | import Array exposing (fromList) 288 | 289 | map2 (\a b -> a + b) 290 | (fromList [ 1, 2, 3 ]) 291 | (fromList [ 1, 2, 3, 4 ]) 292 | --> fromList [ 2, 4, 6 ] 293 | 294 | map2 Tuple.pair 295 | (fromList [ 1, 2, 3 ]) 296 | (fromList [ 'a', 'b' ]) 297 | --> fromList [ ( 1, 'a' ), ( 2, 'b' ) ] 298 | 299 | Note: [`zip`](Array-Extra#zip) can be used instead of `map2 Tuple.pair`. 300 | 301 | -} 302 | map2 : 303 | (a -> b -> combined) 304 | -> Array a 305 | -> Array b 306 | -> Array combined 307 | map2 elementsCombine aArray bArray = 308 | List.map2 elementsCombine 309 | (aArray |> Array.toList) 310 | (bArray |> Array.toList) 311 | |> Array.fromList 312 | 313 | 314 | {-| Combine the elements of three `Array`s with the given function. See [`map2`](Array-Extra#map2). 315 | 316 | Note: [`zip3`](Array-Extra#zip3) can be used instead of `map3 (\a b c -> ( a, b, c ))`. 317 | 318 | -} 319 | map3 : 320 | (a -> b -> c -> combined) 321 | -> Array a 322 | -> Array b 323 | -> Array c 324 | -> Array combined 325 | map3 elementsCombine aArray bArray cArray = 326 | apply (map2 elementsCombine aArray bArray) cArray 327 | 328 | 329 | {-| Combine the elements of four `Array`s with the given function. See [`map2`](Array-Extra#map2). 330 | -} 331 | map4 : 332 | (a -> b -> c -> d -> combined) 333 | -> Array a 334 | -> Array b 335 | -> Array c 336 | -> Array d 337 | -> Array combined 338 | map4 elementsCombine aArray bArray cArray dArray = 339 | apply (map3 elementsCombine aArray bArray cArray) dArray 340 | 341 | 342 | {-| Combine the elements of five `Array`s with the given function. See [`map2`](Array-Extra#map2). 343 | -} 344 | map5 : 345 | (a -> b -> c -> d -> e -> combined) 346 | -> Array a 347 | -> Array b 348 | -> Array c 349 | -> Array d 350 | -> Array e 351 | -> Array combined 352 | map5 elementsCombine aArray bArray cArray dArray eArray = 353 | apply (map4 elementsCombine aArray bArray cArray dArray) eArray 354 | 355 | 356 | {-| Combine the elements of two `Array`s into tuples. 357 | If one is longer, its extra elements are not used. 358 | 359 | import Array exposing (fromList) 360 | 361 | zip 362 | (fromList [ 1, 2, 3 ]) 363 | (fromList [ 'a', 'b' ]) 364 | --> fromList [ ( 1, 'a' ), ( 2, 'b' ) ] 365 | 366 | -} 367 | zip : 368 | Array firstElement 369 | -> Array secondElement 370 | -> Array ( firstElement, secondElement ) 371 | zip firstArray secondArray = 372 | map2 Tuple.pair firstArray secondArray 373 | 374 | 375 | {-| Zip the elements of three `Array`s into 3-tuples. 376 | Only the indexes of the shortest `Array` are used. 377 | 378 | import Array exposing (fromList) 379 | 380 | zip3 381 | (fromList [ 1, 2, 3 ]) 382 | (fromList [ 'a', 'b' ]) 383 | (fromList [ "a", "b", "c", "d" ]) 384 | --> fromList 385 | --> [ ( 1, 'a', "a" ) 386 | --> , ( 2, 'b', "b" ) 387 | --> ] 388 | 389 | -} 390 | zip3 : 391 | Array firstElement 392 | -> Array secondElement 393 | -> Array thirdElement 394 | -> Array ( firstElement, secondElement, thirdElement ) 395 | zip3 = 396 | map3 (\a b c -> ( a, b, c )) 397 | 398 | 399 | {-| Split all tuple elements into a tuple of one `Array` with the first and one with the second values. 400 | 401 | import Array exposing (fromList) 402 | 403 | unzip 404 | (fromList 405 | [ ( 1, 'a' ), ( 2, 'b' ), ( 3, 'c' ) ] 406 | ) 407 | --> ( fromList [ 1, 2, 3 ] 408 | --> , fromList [ 'a', 'b', 'c' ] 409 | --> ) 410 | 411 | -} 412 | unzip : 413 | Array ( elementFirst, elementSecond ) 414 | -> ( Array elementFirst, Array elementSecond ) 415 | unzip = 416 | \arrayOfTuple -> 417 | ( arrayOfTuple |> Array.map Tuple.first 418 | , arrayOfTuple |> Array.map Tuple.second 419 | ) 420 | 421 | 422 | {-| Only keep elements which fail to satisfy a given predicate. 423 | This is equivalent to `Array.filter (not << predicate)`. 424 | 425 | import Array exposing (fromList) 426 | 427 | fromList [ -1, 92, 0, 14, -3 ] 428 | |> removeWhen (\x -> x < 0) 429 | --> fromList [ 92, 0, 14 ] 430 | 431 | -} 432 | removeWhen : (element -> Bool) -> (Array element -> Array element) 433 | removeWhen isBad = 434 | \array -> 435 | array 436 | |> Array.filter (\element -> element |> isBad |> not) 437 | 438 | 439 | {-| Resize from the left, padding the right-hand side with a given value. 440 | 441 | import Array exposing (fromList, empty) 442 | 443 | fromList [ 1, 2 ] |> resizelRepeat 4 0 444 | --> fromList [ 1, 2, 0, 0 ] 445 | 446 | fromList [ 1, 2, 3 ] |> resizelRepeat 2 0 447 | --> fromList [ 1, 2 ] 448 | 449 | fromList [ 1, 2 ] |> resizelRepeat -1 0 450 | --> empty 451 | 452 | -} 453 | resizelRepeat : Int -> element -> (Array element -> Array element) 454 | resizelRepeat lengthNew padding = 455 | if lengthNew <= 0 then 456 | \_ -> Array.empty 457 | 458 | else 459 | \array -> 460 | let 461 | arrayLength = 462 | array |> length 463 | in 464 | case compare arrayLength lengthNew of 465 | GT -> 466 | array |> sliceUntil lengthNew 467 | 468 | LT -> 469 | append array (repeat (lengthNew - arrayLength) padding) 470 | 471 | EQ -> 472 | array 473 | 474 | 475 | {-| Resize from the right, padding the left-hand side with a given value. 476 | 477 | import Array exposing (fromList, empty) 478 | 479 | fromList [ 1, 2 ] |> resizerRepeat 4 0 480 | --> fromList [ 0, 0, 1, 2 ] 481 | 482 | fromList [ 1, 2, 3 ] |> resizerRepeat 2 0 483 | --> fromList [ 2, 3 ] 484 | 485 | fromList [ 1, 2 ] |> resizerRepeat -1 0 486 | --> empty 487 | 488 | -} 489 | resizerRepeat : Int -> element -> (Array element -> Array element) 490 | resizerRepeat lengthNew defaultValue = 491 | \array -> 492 | let 493 | arrayLength = 494 | array |> length 495 | in 496 | case compare arrayLength lengthNew of 497 | GT -> 498 | array |> slice (arrayLength - lengthNew) arrayLength 499 | 500 | LT -> 501 | append 502 | (repeat (lengthNew - arrayLength) defaultValue) 503 | array 504 | 505 | EQ -> 506 | array 507 | 508 | 509 | {-| Resize from the left, padding the right-hand side with a given value based on index. 510 | 511 | import Array exposing (fromList, empty) 512 | 513 | fromList [ 'a', 'b', 'c' ] 514 | |> resizelIndexed 5 toLetterInAlphabet 515 | --> fromList [ 'a', 'b', 'c', 'd', 'e' ] 516 | 517 | fromList [ 'a', 'b', 'c' ] 518 | |> resizelIndexed 2 toLetterInAlphabet 519 | --> fromList [ 'a', 'b' ] 520 | 521 | fromList [ 'a', 'b', 'c' ] 522 | |> resizelIndexed -1 toLetterInAlphabet 523 | --> empty 524 | 525 | toLetterInAlphabet : Int -> Char 526 | toLetterInAlphabet inAlphabet = 527 | ('a' |> Char.toCode) + inAlphabet 528 | |> Char.fromCode 529 | 530 | -} 531 | resizelIndexed : 532 | Int 533 | -> (Int -> element) 534 | -> (Array element -> Array element) 535 | resizelIndexed lengthNew paddingElementForIndex = 536 | \array -> 537 | if lengthNew <= 0 then 538 | Array.empty 539 | 540 | else 541 | let 542 | arrayLength = 543 | array |> length 544 | in 545 | case compare arrayLength lengthNew of 546 | GT -> 547 | array |> sliceUntil lengthNew 548 | 549 | LT -> 550 | append array 551 | (initialize (lengthNew - arrayLength) 552 | (\padIndex -> 553 | (arrayLength + padIndex) 554 | |> paddingElementForIndex 555 | ) 556 | ) 557 | 558 | EQ -> 559 | array 560 | 561 | 562 | {-| Resize from the right, padding the left-hand side with a given value based on index. 563 | 564 | import Array exposing (fromList, empty) 565 | 566 | fromList [ 10, 25, 36 ] 567 | |> resizerIndexed 5 (\n -> n * 5) 568 | --> fromList [ 0, 5, 10, 25, 36 ] 569 | 570 | fromList [ 10, 25, 36 ] 571 | |> resizerIndexed 2 (\n -> n * 5) 572 | --> fromList [ 25, 36 ] 573 | 574 | fromList [ 10, 25, 36 ] 575 | |> resizerIndexed -1 (\n -> n * 5) 576 | --> empty 577 | 578 | -} 579 | resizerIndexed : 580 | Int 581 | -> (Int -> element) 582 | -> (Array element -> Array element) 583 | resizerIndexed lengthNew paddingAtIndex = 584 | \array -> 585 | let 586 | arrayLength = 587 | array |> length 588 | in 589 | case compare arrayLength lengthNew of 590 | GT -> 591 | array |> slice (arrayLength - lengthNew) arrayLength 592 | 593 | LT -> 594 | append 595 | (initialize (lengthNew - arrayLength) paddingAtIndex) 596 | array 597 | 598 | EQ -> 599 | array 600 | 601 | 602 | {-| Flip the element order. 603 | 604 | import Array exposing (fromList) 605 | 606 | fromList [ 1, 2, 3, 4 ] |> reverse 607 | --> fromList [ 4, 3, 2, 1 ] 608 | 609 | -} 610 | reverse : Array element -> Array element 611 | reverse = 612 | \array -> 613 | array 614 | |> reverseToList 615 | |> Array.fromList 616 | 617 | 618 | reverseToList : Array element -> List element 619 | reverseToList = 620 | \array -> array |> Array.foldl (::) [] 621 | 622 | 623 | {-| Split into two `Array`s, the first ending before and the second starting with a given index. 624 | 625 | import Array exposing (fromList, empty) 626 | 627 | fromList [ 1, 2, 3, 4 ] |> splitAt 2 628 | --> ( fromList [ 1, 2 ], fromList [ 3, 4 ] ) 629 | 630 | fromList [ 1, 2, 3, 4 ] |> splitAt 100 631 | --> ( fromList [ 1, 2, 3, 4 ], empty ) 632 | 633 | fromList [ 1, 2, 3, 4 ] |> splitAt -1 634 | --> ( empty, fromList [ 1, 2, 3, 4 ] ) 635 | 636 | -} 637 | splitAt : Int -> Array element -> ( Array element, Array element ) 638 | splitAt index = 639 | \array -> 640 | if index >= 1 then 641 | ( array |> sliceUntil index 642 | , array |> sliceFrom index 643 | ) 644 | 645 | else 646 | ( empty, array ) 647 | 648 | 649 | {-| Remove the element at a given index. 650 | If the index is out of bounds, nothing is changed. 651 | 652 | import Array exposing (fromList) 653 | 654 | fromList [ 1, 2, 3, 4 ] |> removeAt 2 655 | --> fromList [ 1, 2, 4 ] 656 | 657 | fromList [ 1, 2, 3, 4 ] |> removeAt -1 658 | --> fromList [ 1, 2, 3, 4 ] 659 | 660 | fromList [ 1, 2, 3, 4 ] |> removeAt 100 661 | --> fromList [ 1, 2, 3, 4 ] 662 | 663 | -} 664 | removeAt : Int -> (Array element -> Array element) 665 | removeAt index = 666 | \array -> 667 | if index >= 0 then 668 | let 669 | ( beforeIndex, startingAtIndex ) = 670 | array |> splitAt index 671 | 672 | lengthStartingAtIndex = 673 | length startingAtIndex 674 | in 675 | if lengthStartingAtIndex == 0 then 676 | beforeIndex 677 | 678 | else 679 | append beforeIndex 680 | (slice 1 lengthStartingAtIndex startingAtIndex) 681 | 682 | else 683 | array 684 | 685 | 686 | {-| Insert an element at a given index. 687 | If the index is out of bounds, nothing is changed. 688 | 689 | import Array exposing (fromList) 690 | 691 | fromList [ 'a', 'c' ] |> insertAt 1 'b' 692 | --> fromList [ 'a', 'b', 'c' ] 693 | 694 | fromList [ 'a', 'c' ] |> insertAt -1 'b' 695 | --> fromList [ 'a', 'c' ] 696 | 697 | fromList [ 'a', 'c' ] |> insertAt 100 'b' 698 | --> fromList [ 'a', 'c' ] 699 | 700 | -} 701 | insertAt : Int -> element -> (Array element -> Array element) 702 | insertAt index elementToInsert = 703 | \array -> 704 | if index >= 0 then 705 | let 706 | arrayLength = 707 | array |> length 708 | in 709 | if index <= arrayLength then 710 | let 711 | before = 712 | array |> Array.slice 0 index 713 | 714 | after = 715 | array |> Array.slice index arrayLength 716 | in 717 | Array.append (before |> Array.push elementToInsert) after 718 | 719 | else 720 | array 721 | 722 | else 723 | array 724 | 725 | 726 | {-| Whether all elements satisfy a given test. 727 | 728 | import Array exposing (fromList, empty) 729 | 730 | fromList [ 2, 4 ] |> all (\x -> x < 5) 731 | --> True 732 | 733 | fromList [ 4, 16 ] |> all (\x -> x < 5) 734 | --> False 735 | 736 | empty |> all (\x -> x < 5) 737 | --> True 738 | 739 | -} 740 | all : (element -> Bool) -> (Array element -> Bool) 741 | all isOkay = 742 | \array -> 743 | array 744 | |> Array.foldl 745 | (\element soFar -> soFar && isOkay element) 746 | True 747 | 748 | 749 | {-| Whether at least some elements satisfy a given test. 750 | 751 | import Array exposing (fromList, empty) 752 | 753 | fromList [ 6, 3 ] |> any (\x -> x < 5) 754 | --> True 755 | 756 | fromList [ 12, 33 ] |> any (\x -> x < 5) 757 | --> False 758 | 759 | empty |> any (\x -> x < 5) 760 | --> False 761 | 762 | -} 763 | any : (element -> Bool) -> (Array element -> Bool) 764 | any isOkay = 765 | \array -> 766 | array 767 | |> Array.foldl 768 | (\element soFar -> soFar || isOkay element) 769 | False 770 | 771 | 772 | {-| Whether a given value is contained. 773 | 774 | import Array exposing (fromList) 775 | 776 | fromList [ "Leonardo", "Michelangelo", "Donatello", "Raphael" ] 777 | |> member "Donatello" 778 | --> True 779 | 780 | fromList [ "Leonardo", "Michelangelo" ] 781 | |> member "Raphael" 782 | --> False 783 | 784 | For checking if some aspect is present, use [`any`](#any). 785 | 786 | -} 787 | member : element -> (Array element -> Bool) 788 | member needle = 789 | \array -> 790 | array |> any (\element -> element == needle) 791 | 792 | 793 | {-| Place all elements of a given `Array` between all current elements. 794 | Extra elements of either `Array` are glued to the end without anything in between. 795 | 796 | import Array exposing (fromList, repeat) 797 | 798 | fromList [ "turtles", "turtles", "turtles" ] 799 | |> interweave (repeat 2 "on") 800 | --> fromList [ "turtles", "on", "turtles", "on", "turtles" ] 801 | 802 | fromList [ "turtles", "turtles", "turtles" ] 803 | |> interweave (repeat 5 "on") 804 | --> fromList [ "turtles", "on", "turtles", "on", "turtles", "on", "on", "on" ] 805 | 806 | fromList [ "turtles", "turtles", "turtles" ] 807 | |> interweave (repeat 1 "on") 808 | --> fromList [ "turtles", "on", "turtles", "turtles" ] 809 | 810 | -} 811 | interweave : Array element -> (Array element -> Array element) 812 | interweave toInterweave = 813 | \array -> 814 | let 815 | untilArrayEnd = 816 | array 817 | |> Array.foldl 818 | (\element soFar -> 819 | case soFar.toInterweave of 820 | [] -> 821 | { interwoven = 822 | soFar.interwoven |> (::) element 823 | , toInterweave = [] 824 | } 825 | 826 | toInterweaveHead :: toInterweaveTail -> 827 | { interwoven = 828 | soFar.interwoven 829 | |> (::) element 830 | |> (::) toInterweaveHead 831 | , toInterweave = toInterweaveTail 832 | } 833 | ) 834 | { interwoven = [] 835 | , toInterweave = toInterweave |> Array.toList 836 | } 837 | in 838 | (untilArrayEnd.interwoven 839 | |> List.reverse 840 | ) 841 | ++ untilArrayEnd.toInterweave 842 | |> Array.fromList 843 | -------------------------------------------------------------------------------- /tests/Tests.elm: -------------------------------------------------------------------------------- 1 | module Tests exposing (suite) 2 | 3 | {-| Even though most implementations seem robust as they are now, 4 | the tests are here to allow confident refactoring & changing. 5 | -} 6 | 7 | import Array exposing (Array) 8 | import Array.Extra as Array 9 | import Expect exposing (Expectation) 10 | import Fuzz 11 | import Random 12 | import Test exposing (Test, test) 13 | 14 | 15 | suite : Test 16 | suite = 17 | Test.describe "Array.Extra" 18 | [ Test.describe "all" 19 | [ Test.describe "True" 20 | [ Test.fuzz 21 | (Fuzz.array Fuzz.int) 22 | "filter test |> all test" 23 | (\array -> 24 | array 25 | |> Array.filter isEven 26 | |> Array.all isEven 27 | |> Expect.equal 28 | True 29 | ) 30 | , test "example" 31 | (\() -> 32 | Array.fromList [ 2, 4 ] 33 | |> Array.all isEven 34 | |> Expect.equal 35 | True 36 | ) 37 | ] 38 | , Test.describe "False" 39 | [ Test.fuzz 40 | (Fuzz.constant 41 | (\before after -> { before = before, after = after }) 42 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 43 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 44 | ) 45 | "1 failing element included" 46 | (\{ before, after } -> 47 | Array.append 48 | (before |> Array.push 1) 49 | after 50 | |> Array.all isEven 51 | |> Expect.equal 52 | False 53 | ) 54 | , test "example" 55 | (\() -> 56 | Array.fromList [ 2, 3 ] 57 | |> Array.all isEven 58 | |> Expect.equal 59 | False 60 | ) 61 | ] 62 | ] 63 | , Test.describe "any" 64 | [ Test.describe "True" 65 | [ Test.fuzz 66 | (Fuzz.constant 67 | (\before after -> { before = before, after = after }) 68 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 69 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 70 | ) 71 | "1 passing element included" 72 | (\{ before, after } -> 73 | Array.append 74 | (before |> Array.push 2) 75 | after 76 | |> Array.any isEven 77 | |> Expect.equal 78 | True 79 | ) 80 | , test "example" 81 | (\() -> 82 | Array.fromList [ 1, 2 ] 83 | |> Array.any isEven 84 | |> Expect.equal 85 | True 86 | ) 87 | ] 88 | , Test.describe "False" 89 | [ Test.fuzz 90 | (Fuzz.array Fuzz.int) 91 | "removeWhen test |> any test" 92 | (\array -> 93 | array 94 | |> Array.removeWhen isEven 95 | |> Array.any isEven 96 | |> Expect.equal 97 | False 98 | ) 99 | , test "example" 100 | (\() -> 101 | Array.fromList [ 1, 3 ] 102 | |> Array.any isEven 103 | |> Expect.equal 104 | False 105 | ) 106 | ] 107 | ] 108 | , Test.describe "member" 109 | [ Test.fuzz 110 | (Fuzz.constant 111 | (\before after -> 112 | { before = before, after = after } 113 | ) 114 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 115 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 116 | ) 117 | "included → True" 118 | (\{ before, after } -> 119 | Array.append 120 | (before |> Array.push 123456) 121 | after 122 | |> Array.member 123456 123 | |> Expect.equal 124 | True 125 | ) 126 | , Test.fuzz 127 | (Fuzz.array Fuzz.int) 128 | "all removed → False" 129 | (\array -> 130 | array 131 | |> Array.removeWhen (\element -> element == 123456) 132 | |> Array.member 123456 133 | |> Expect.equal 134 | False 135 | ) 136 | ] 137 | , Test.describe "update" 138 | [ test "index valid" 139 | (\() -> 140 | Array.fromList [ 1, 2, 3 ] 141 | |> Array.update 1 (\n -> n + 10) 142 | |> expectEqualArrays 143 | (Array.fromList [ 1, 12, 3 ]) 144 | ) 145 | , Test.fuzz 146 | (Fuzz.constant 147 | (\array index -> { array = array, index = index }) 148 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 149 | |> Fuzz.andMap (Fuzz.intRange Random.minInt -1) 150 | ) 151 | "index negative" 152 | (\{ array, index } -> 153 | array 154 | |> Array.update index (\n -> n + 10) 155 | |> expectEqualArrays 156 | array 157 | ) 158 | , Test.fuzz 159 | (Fuzz.constant 160 | (\array above -> { array = array, above = above }) 161 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 162 | |> Fuzz.andMap (Fuzz.intRange 0 Random.maxInt) 163 | ) 164 | "index too high" 165 | (\{ array, above } -> 166 | array 167 | |> Array.update ((array |> Array.length) + above) (\n -> n + 10) 168 | |> expectEqualArrays 169 | array 170 | ) 171 | ] 172 | , Test.describe "pop" 173 | [ test "empty → Array.empty" 174 | (\() -> 175 | Array.empty 176 | |> Array.pop 177 | |> expectEqualArrays 178 | Array.empty 179 | ) 180 | , Test.fuzz 181 | (Fuzz.constant 182 | (\beforeLast last -> 183 | { beforeLast = beforeLast, last = last } 184 | ) 185 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 186 | |> Fuzz.andMap Fuzz.int 187 | ) 188 | "push |> pop → no change" 189 | (\{ beforeLast, last } -> 190 | beforeLast 191 | |> Array.push last 192 | |> Array.pop 193 | |> expectEqualArrays 194 | beforeLast 195 | ) 196 | ] 197 | , Test.describe "splitAt" 198 | [ test "index valid" 199 | (\() -> 200 | Array.fromList [ 1, 2, 3, 4 ] 201 | |> Array.splitAt 2 202 | |> Expect.equal 203 | ( Array.fromList [ 1, 2 ], Array.fromList [ 3, 4 ] ) 204 | ) 205 | , Test.fuzz 206 | (Fuzz.constant 207 | (\array index -> { array = array, index = index }) 208 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 209 | |> Fuzz.andMap (Fuzz.intRange Random.minInt 0) 210 | ) 211 | "index not positive" 212 | (\{ array, index } -> 213 | array 214 | |> Array.splitAt index 215 | |> Expect.equal 216 | ( Array.empty, array ) 217 | ) 218 | , Test.fuzz 219 | (Fuzz.constant 220 | (\array above -> { array = array, above = above }) 221 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 222 | |> Fuzz.andMap (Fuzz.intRange 0 Random.maxInt) 223 | ) 224 | "index too high" 225 | (\{ array, above } -> 226 | array 227 | |> Array.splitAt ((array |> Array.length) + above) 228 | |> Expect.equal 229 | ( array, Array.empty ) 230 | ) 231 | ] 232 | , Test.describe "removeAt" 233 | [ test "index valid" 234 | (\() -> 235 | Array.fromList [ 1, 2, 3, 4 ] 236 | |> Array.removeAt 2 237 | |> expectEqualArrays 238 | (Array.fromList [ 1, 2, 4 ]) 239 | ) 240 | , Test.fuzz 241 | (Fuzz.constant 242 | (\array index -> { array = array, index = index }) 243 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 244 | |> Fuzz.andMap (Fuzz.intRange Random.minInt -1) 245 | ) 246 | "index negative" 247 | (\{ array, index } -> 248 | array 249 | |> Array.removeAt index 250 | |> expectEqualArrays 251 | array 252 | ) 253 | , Test.fuzz 254 | (Fuzz.constant 255 | (\array above -> { array = array, above = above }) 256 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 257 | |> Fuzz.andMap (Fuzz.intRange 0 Random.maxInt) 258 | ) 259 | "index too high" 260 | (\{ array, above } -> 261 | array 262 | |> Array.removeAt ((array |> Array.length) + above) 263 | |> expectEqualArrays 264 | array 265 | ) 266 | ] 267 | , Test.describe "insertAt" 268 | [ test "index valid" 269 | (\() -> 270 | Array.fromList [ 'a', 'c' ] 271 | |> Array.insertAt 1 'b' 272 | |> expectEqualArrays 273 | (Array.fromList [ 'a', 'b', 'c' ]) 274 | ) 275 | , Test.fuzz 276 | (Fuzz.constant 277 | (\array index -> { array = array, index = index }) 278 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 279 | |> Fuzz.andMap (Fuzz.intRange Random.minInt -1) 280 | ) 281 | "index negative" 282 | (\{ array, index } -> 283 | array 284 | |> Array.insertAt index 12345 285 | |> expectEqualArrays 286 | array 287 | ) 288 | , Test.fuzz 289 | (Fuzz.constant 290 | (\array above -> { array = array, above = above }) 291 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 292 | |> Fuzz.andMap (Fuzz.intRange 1 Random.maxInt) 293 | ) 294 | "index too high" 295 | (\{ array, above } -> 296 | array 297 | |> Array.insertAt ((array |> Array.length) + above) 12345 298 | |> expectEqualArrays 299 | array 300 | ) 301 | ] 302 | , Test.describe "sliceFrom" 303 | [ test "index positive valid" 304 | (\() -> 305 | Array.fromList (List.range 0 6) 306 | |> Array.sliceFrom 3 307 | |> expectEqualArrays 308 | (Array.fromList [ 3, 4, 5, 6 ]) 309 | ) 310 | , test "index negative valid" 311 | (\() -> 312 | Array.fromList (List.range 0 6) 313 | |> Array.sliceFrom -3 314 | |> expectEqualArrays 315 | (Array.fromList [ 4, 5, 6 ]) 316 | ) 317 | , Test.fuzz 318 | (Fuzz.constant 319 | (\array above -> { array = array, above = above }) 320 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 321 | |> Fuzz.andMap (Fuzz.intRange 0 Random.maxInt) 322 | ) 323 | "index positive too high" 324 | (\{ array, above } -> 325 | array 326 | |> Array.sliceFrom ((array |> Array.length) + above) 327 | |> expectEqualArrays 328 | Array.empty 329 | ) 330 | , Test.fuzz 331 | (Fuzz.constant 332 | (\array below -> { array = array, below = below }) 333 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 334 | |> Fuzz.andMap (Fuzz.intRange 0 Random.maxInt) 335 | ) 336 | "index negative too low" 337 | (\{ array, below } -> 338 | array 339 | |> Array.sliceFrom (-(array |> Array.length) - below) 340 | |> expectEqualArrays 341 | array 342 | ) 343 | ] 344 | , Test.describe "sliceUntil" 345 | [ test "index positive valid" 346 | (\() -> 347 | Array.fromList (List.range 0 6) 348 | |> Array.sliceUntil 3 349 | |> expectEqualArrays 350 | (Array.fromList [ 0, 1, 2 ]) 351 | ) 352 | , test "index negative valid" 353 | (\() -> 354 | Array.fromList (List.range 0 6) 355 | |> Array.sliceUntil -3 356 | |> expectEqualArrays 357 | (Array.fromList [ 0, 1, 2, 3 ]) 358 | ) 359 | , Test.fuzz 360 | (Fuzz.array Fuzz.int) 361 | "index 0" 362 | (\array -> 363 | array 364 | |> Array.sliceUntil 0 365 | |> expectEqualArrays 366 | Array.empty 367 | ) 368 | , Test.fuzz 369 | (Fuzz.constant 370 | (\array above -> { array = array, above = above }) 371 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 372 | |> Fuzz.andMap (Fuzz.intRange 0 Random.maxInt) 373 | ) 374 | "index positive too high" 375 | (\{ array, above } -> 376 | array 377 | |> Array.sliceUntil ((array |> Array.length) + above) 378 | |> expectEqualArrays 379 | array 380 | ) 381 | , Test.fuzz 382 | (Fuzz.constant 383 | (\array below -> { array = array, below = below }) 384 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 385 | |> Fuzz.andMap (Fuzz.intRange 0 Random.maxInt) 386 | ) 387 | "index negative too low" 388 | (\{ array, below } -> 389 | array 390 | |> Array.sliceUntil (-(array |> Array.length) - below) 391 | |> expectEqualArrays 392 | Array.empty 393 | ) 394 | ] 395 | , Test.describe "filterMap" 396 | [ Test.fuzz 397 | (Fuzz.array Fuzz.int) 398 | "all Just" 399 | (\array -> 400 | array 401 | |> Array.map Just 402 | |> Array.filterMap identity 403 | |> expectEqualArrays 404 | array 405 | ) 406 | , Test.fuzz 407 | (Fuzz.array (Fuzz.constant Nothing)) 408 | "all Nothing" 409 | (\arrayOfNothing -> 410 | arrayOfNothing 411 | |> Array.filterMap identity 412 | |> expectEqualArrays 413 | Array.empty 414 | ) 415 | , test "some Nothing" 416 | (\() -> 417 | Array.fromList [ Just 3, Nothing, Just 5, Nothing ] 418 | |> Array.filterMap identity 419 | |> expectEqualArrays 420 | (Array.fromList [ 3, 5 ]) 421 | ) 422 | ] 423 | , Test.describe "apply" 424 | [ Test.fuzz 425 | (Fuzz.array Fuzz.int) 426 | "more elements than functions" 427 | (\after4 -> 428 | Array.append (Array.repeat 4 100) after4 429 | |> Array.apply 430 | (Array.fromList 431 | [ negate 432 | , identity 433 | , \n -> n + 10 434 | , \_ -> 0 435 | ] 436 | ) 437 | |> expectEqualArrays 438 | (Array.fromList [ -100, 100, 110, 0 ]) 439 | ) 440 | , Test.fuzz 441 | (Fuzz.array (Fuzz.constant (\_ -> 0))) 442 | "more functions than elements" 443 | (\after3 -> 444 | Array.repeat 3 100 445 | |> Array.apply 446 | (Array.append 447 | (Array.fromList [ negate, identity, \n -> n + 10 ]) 448 | after3 449 | ) 450 | |> expectEqualArrays 451 | (Array.fromList [ -100, 100, 110 ]) 452 | ) 453 | ] 454 | , Test.fuzz 455 | (Fuzz.array Fuzz.int) 456 | "mapToList" 457 | (\array -> 458 | array 459 | |> Array.mapToList String.fromInt 460 | |> Expect.equalLists 461 | (array 462 | |> Array.map String.fromInt 463 | |> Array.toList 464 | ) 465 | ) 466 | , Test.fuzz 467 | (Fuzz.array Fuzz.string) 468 | "indexedMapToList" 469 | (\array -> 470 | array 471 | |> Array.indexedMapToList (\i el -> ( i, el )) 472 | |> Expect.equalLists 473 | (array 474 | |> Array.toIndexedList 475 | ) 476 | ) 477 | , Test.describe "map2" 478 | -- `zip` will probably always be implemented with `map2`. 479 | -- No need to test both 480 | [ Test.fuzz 481 | (Fuzz.constant 482 | (\first second -> { first = first, second = second }) 483 | |> Fuzz.andMap (Fuzz.list Fuzz.int) 484 | |> Fuzz.andMap (Fuzz.list Fuzz.int) 485 | ) 486 | "behaves like List.map2" 487 | (\{ first, second } -> 488 | Array.map2 (\a b -> a + b) 489 | (first |> Array.fromList) 490 | (second |> Array.fromList) 491 | |> expectEqualArrays 492 | (List.map2 (\a b -> a + b) first second 493 | |> Array.fromList 494 | ) 495 | ) 496 | , test "first array shorter than the last example" 497 | (\() -> 498 | Array.map2 Tuple.pair 499 | (Array.fromList [ 1, 2, 3, 4 ]) 500 | (Array.fromList [ 'a', 'b', 'c', 'd', 'e' ]) 501 | |> expectEqualArrays 502 | (Array.fromList 503 | [ ( 1, 'a' ) 504 | , ( 2, 'b' ) 505 | , ( 3, 'c' ) 506 | , ( 4, 'd' ) 507 | ] 508 | ) 509 | ) 510 | , test "first array longer than the last example" 511 | (\() -> 512 | Array.map2 Tuple.pair 513 | (Array.fromList [ 'a', 'b', 'c', 'd', 'e' ]) 514 | (Array.fromList [ 1, 2, 3, 4 ]) 515 | |> expectEqualArrays 516 | (Array.fromList 517 | [ ( 'a', 1 ) 518 | , ( 'b', 2 ) 519 | , ( 'c', 3 ) 520 | , ( 'd', 4 ) 521 | ] 522 | ) 523 | ) 524 | ] 525 | , -- `zip3` will probably always be implemented using `map3`. 526 | -- No need to test both 527 | Test.describe "map3" 528 | [ Test.fuzz 529 | (Fuzz.constant 530 | (\first second third -> 531 | { first = first, second = second, third = third } 532 | ) 533 | |> Fuzz.andMap (Fuzz.list Fuzz.int) 534 | |> Fuzz.andMap (Fuzz.list Fuzz.int) 535 | |> Fuzz.andMap (Fuzz.list Fuzz.int) 536 | ) 537 | "behaves like List.map3" 538 | (\{ first, second, third } -> 539 | Array.map3 (\a b c -> a + b + c) 540 | (first |> Array.fromList) 541 | (second |> Array.fromList) 542 | (third |> Array.fromList) 543 | |> expectEqualArrays 544 | (List.map3 (\a b c -> a + b + c) first second third 545 | |> Array.fromList 546 | ) 547 | ) 548 | , test "first array the shortest example" 549 | (\() -> 550 | Array.map3 (\a b c -> ( a, b, c )) 551 | (Array.fromList [ "a", "b", "c" ]) 552 | (Array.fromList [ 'a', 'b', 'c', 'd', 'e' ]) 553 | (Array.fromList [ 1, 2, 3, 4 ]) 554 | |> expectEqualArrays 555 | (Array.fromList 556 | [ ( "a", 'a', 1 ) 557 | , ( "b", 'b', 2 ) 558 | , ( "c", 'c', 3 ) 559 | ] 560 | ) 561 | ) 562 | , test "second array the shortest example" 563 | (\() -> 564 | Array.map3 (\a b c -> ( a, b, c )) 565 | (Array.fromList [ 'a', 'b', 'c', 'd', 'e' ]) 566 | (Array.fromList [ "a", "b", "c" ]) 567 | (Array.fromList [ 1, 2, 3, 4 ]) 568 | |> expectEqualArrays 569 | (Array.fromList 570 | [ ( 'a', "a", 1 ) 571 | , ( 'b', "b", 2 ) 572 | , ( 'c', "c", 3 ) 573 | ] 574 | ) 575 | ) 576 | , test "third array the shortest example" 577 | (\() -> 578 | Array.map3 (\a b c -> ( a, b, c )) 579 | (Array.fromList [ 'a', 'b', 'c', 'd', 'e' ]) 580 | (Array.fromList [ 1, 2, 3, 4 ]) 581 | (Array.fromList [ "a", "b", "c" ]) 582 | |> expectEqualArrays 583 | (Array.fromList 584 | [ ( 'a', 1, "a" ) 585 | , ( 'b', 2, "b" ) 586 | , ( 'c', 3, "c" ) 587 | ] 588 | ) 589 | ) 590 | ] 591 | , Test.describe "removeWhen" 592 | [ test "example" 593 | (\() -> 594 | Array.fromList [ 1, 2, 3, 4 ] 595 | |> Array.removeWhen isEven 596 | |> expectEqualArrays 597 | (Array.fromList [ 1, 3 ]) 598 | ) 599 | , Test.fuzz 600 | (Fuzz.array Fuzz.int) 601 | "filter is |> removeWhen is → Array.empty" 602 | (\array -> 603 | array 604 | |> Array.filter isEven 605 | |> Array.removeWhen isEven 606 | |> expectEqualArrays 607 | Array.empty 608 | ) 609 | , Test.fuzz 610 | (Fuzz.array Fuzz.int) 611 | "removeWhen is |> filter is → Array.empty" 612 | (\array -> 613 | array 614 | |> Array.removeWhen isEven 615 | |> Array.filter isEven 616 | |> expectEqualArrays 617 | Array.empty 618 | ) 619 | ] 620 | , Test.describe "unzip" 621 | [ Test.fuzz 622 | (Fuzz.list (Fuzz.tuple ( Fuzz.int, Fuzz.char ))) 623 | "behaves like List.unzip" 624 | (\listOfTuple -> 625 | listOfTuple 626 | |> Array.fromList 627 | |> Array.unzip 628 | |> Expect.equal 629 | (listOfTuple 630 | |> List.unzip 631 | |> Tuple.mapBoth Array.fromList Array.fromList 632 | ) 633 | ) 634 | , Test.fuzz 635 | (Fuzz.constant (\first second -> { first = first, second = second }) 636 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 637 | |> Fuzz.andMap (Fuzz.array Fuzz.char) 638 | ) 639 | "restores what was before zip with the same length taken" 640 | (\{ first, second } -> 641 | let 642 | minLength = 643 | Basics.min (first |> Array.length) (second |> Array.length) 644 | in 645 | Array.zip first second 646 | |> Array.unzip 647 | |> Expect.equal 648 | ( first |> Array.sliceUntil minLength 649 | , second |> Array.sliceUntil minLength 650 | ) 651 | ) 652 | , test "example" 653 | (\() -> 654 | Array.fromList [ ( 1, 'a' ), ( 2, 'b' ), ( 3, 'c' ) ] 655 | |> Array.unzip 656 | |> Expect.equal 657 | ( Array.fromList [ 1, 2, 3 ] 658 | , Array.fromList [ 'a', 'b', 'c' ] 659 | ) 660 | ) 661 | ] 662 | , Test.describe "reverse" 663 | [ Test.fuzz 664 | (Fuzz.list Fuzz.int) 665 | "like List.reverse" 666 | (\list -> 667 | list 668 | |> Array.fromList 669 | |> Array.reverse 670 | |> expectEqualArrays 671 | (list |> List.reverse |> Array.fromList) 672 | ) 673 | , test "example" 674 | (\() -> 675 | Array.fromList [ 1, 2, 3, 4 ] 676 | |> Array.reverse 677 | |> expectEqualArrays 678 | (Array.fromList [ 4, 3, 2, 1 ]) 679 | ) 680 | ] 681 | , Test.describe "resizelRepeat" 682 | [ test "length less than current" 683 | (\() -> 684 | Array.fromList [ 1, 2, 3, 4 ] 685 | |> Array.resizelRepeat 3 0 686 | |> expectEqualArrays 687 | (Array.fromList [ 1, 2, 3 ]) 688 | ) 689 | , test "length greater than current" 690 | (\() -> 691 | Array.fromList [ 1, 2, 3, 4 ] 692 | |> Array.resizelRepeat 6 0 693 | |> expectEqualArrays 694 | (Array.fromList [ 1, 2, 3, 4, 0, 0 ]) 695 | ) 696 | , Test.fuzz 697 | (Fuzz.constant 698 | (\array length -> { array = array, length = length }) 699 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 700 | |> Fuzz.andMap (Fuzz.intRange Random.minInt 0) 701 | ) 702 | "length not positive → Array.empty" 703 | (\{ array, length } -> 704 | array 705 | |> Array.resizelRepeat length 0 706 | |> expectEqualArrays 707 | Array.empty 708 | ) 709 | ] 710 | , Test.describe "resizerRepeat" 711 | [ test "length less than current" 712 | (\() -> 713 | Array.fromList [ 1, 2, 3, 4 ] 714 | |> Array.resizerRepeat 3 0 715 | |> expectEqualArrays 716 | (Array.fromList [ 2, 3, 4 ]) 717 | ) 718 | , test "length greater than current" 719 | (\() -> 720 | Array.fromList [ 1, 2, 3, 4 ] 721 | |> Array.resizerRepeat 6 0 722 | |> expectEqualArrays 723 | (Array.fromList [ 0, 0, 1, 2, 3, 4 ]) 724 | ) 725 | , Test.fuzz 726 | (Fuzz.constant 727 | (\array length -> { array = array, length = length }) 728 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 729 | |> Fuzz.andMap (Fuzz.intRange Random.minInt 0) 730 | ) 731 | "length not positive → Array.empty" 732 | (\{ array, length } -> 733 | array 734 | |> Array.resizelRepeat length 0 735 | |> expectEqualArrays 736 | Array.empty 737 | ) 738 | ] 739 | , Test.describe "resizelIndexed" 740 | [ test "length less than current" 741 | (\() -> 742 | Array.fromList [ "a", "b", "c" ] 743 | |> Array.resizelIndexed 2 String.fromInt 744 | |> expectEqualArrays 745 | (Array.fromList [ "a", "b" ]) 746 | ) 747 | , test "length greater than current" 748 | (\() -> 749 | Array.fromList [ "a", "b", "c" ] 750 | |> Array.resizelIndexed 5 String.fromInt 751 | |> expectEqualArrays 752 | (Array.fromList [ "a", "b", "c", "3", "4" ]) 753 | ) 754 | , Test.fuzz 755 | (Fuzz.constant 756 | (\array length -> { array = array, length = length }) 757 | |> Fuzz.andMap (Fuzz.array Fuzz.string) 758 | |> Fuzz.andMap (Fuzz.intRange Random.minInt 0) 759 | ) 760 | "length not positive → Array.empty" 761 | (\{ array, length } -> 762 | array 763 | |> Array.resizelIndexed length String.fromInt 764 | |> expectEqualArrays 765 | Array.empty 766 | ) 767 | ] 768 | , Test.describe "resizerIndexed" 769 | [ test "length less than current" 770 | (\() -> 771 | Array.fromList [ "a", "b", "c" ] 772 | |> Array.resizerIndexed 2 String.fromInt 773 | |> expectEqualArrays 774 | (Array.fromList [ "b", "c" ]) 775 | ) 776 | , test "length greater than current" 777 | (\() -> 778 | Array.fromList [ "a", "b", "c" ] 779 | |> Array.resizerIndexed 5 String.fromInt 780 | |> expectEqualArrays 781 | (Array.fromList [ "0", "1", "a", "b", "c" ]) 782 | ) 783 | , Test.fuzz 784 | (Fuzz.constant 785 | (\array index -> { array = array, index = index }) 786 | |> Fuzz.andMap (Fuzz.array Fuzz.string) 787 | |> Fuzz.andMap (Fuzz.intRange Random.minInt -1) 788 | ) 789 | "negative length → Array.empty" 790 | (\{ array, index } -> 791 | array 792 | |> Array.resizerIndexed index String.fromInt 793 | |> expectEqualArrays 794 | Array.empty 795 | ) 796 | ] 797 | , Test.describe "intersperse" 798 | [ Test.fuzz Fuzz.int 799 | "empty → Array.empty" 800 | (\separator -> 801 | Array.empty 802 | |> Array.intersperse separator 803 | |> expectEqualArrays 804 | Array.empty 805 | ) 806 | , Test.fuzz 807 | (Fuzz.constant 808 | (\onlyElement separator -> 809 | { onlyElement = onlyElement, separator = separator } 810 | ) 811 | |> Fuzz.andMap Fuzz.int 812 | |> Fuzz.andMap Fuzz.int 813 | ) 814 | "one → one" 815 | (\{ onlyElement, separator } -> 816 | Array.empty 817 | |> Array.push onlyElement 818 | |> Array.intersperse separator 819 | |> expectEqualArrays 820 | (Array.empty 821 | |> Array.push onlyElement 822 | ) 823 | ) 824 | , Test.fuzz 825 | (Fuzz.constant 826 | (\base separator -> 827 | { base = base, separator = separator } 828 | ) 829 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 830 | |> Fuzz.andMap Fuzz.int 831 | ) 832 | "combined length" 833 | (\{ base, separator } -> 834 | base 835 | |> Array.intersperse separator 836 | |> Array.length 837 | |> Expect.equal 838 | (((base |> Array.length) * 2 - 1) 839 | |> max 0 840 | ) 841 | ) 842 | , test "multiple" 843 | (\() -> 844 | Array.fromList [ "turtles", "turtles", "turtles" ] 845 | |> Array.intersperse "on" 846 | |> expectEqualArrays 847 | (Array.fromList 848 | [ "turtles", "on", "turtles", "on", "turtles" ] 849 | ) 850 | ) 851 | ] 852 | , Test.describe "interweave" 853 | [ Test.fuzz 854 | (Fuzz.constant 855 | (\base toInterweave -> 856 | { base = base, toInterweave = toInterweave } 857 | ) 858 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 859 | |> Fuzz.andMap (Fuzz.array Fuzz.int) 860 | ) 861 | "lengths add up" 862 | (\{ base, toInterweave } -> 863 | base 864 | |> Array.interweave toInterweave 865 | |> Array.length 866 | |> Expect.equal 867 | ((base |> Array.length) 868 | + (toInterweave |> Array.length) 869 | ) 870 | ) 871 | , test "less to interweave" 872 | (\() -> 873 | Array.fromList [ "a0", "a1", "a2" ] 874 | |> Array.interweave 875 | (Array.fromList [ "b0" ]) 876 | |> expectEqualArrays 877 | (Array.fromList 878 | [ "a0", "b0", "a1", "a2" ] 879 | ) 880 | ) 881 | , test "more to interweave" 882 | (\() -> 883 | Array.fromList [ "a0", "a1", "a2" ] 884 | |> Array.interweave 885 | (Array.fromList [ "b0", "b1", "b2", "b3", "b4" ]) 886 | |> expectEqualArrays 887 | (Array.fromList 888 | [ "a0", "b0", "a1", "b1", "a2", "b2", "b3", "b4" ] 889 | ) 890 | ) 891 | ] 892 | ] 893 | 894 | 895 | isEven : Int -> Bool 896 | isEven = 897 | \int -> (int |> modBy 2) == 0 898 | 899 | 900 | expectEqualArrays : Array a -> Array a -> Expectation 901 | expectEqualArrays expected actual = 902 | Expect.equalLists 903 | (expected |> Array.toList) 904 | (actual |> Array.toList) 905 | --------------------------------------------------------------------------------