Skip to content

Commit d8486ad

Browse files
authored
Add List.Extra.insertAt (#99)
* Add List.Extra.insertAt * Use Lue's optimized version of List.Extra.insertAt * Count index down, one fewer argument
1 parent 820708d commit d8486ad

File tree

4 files changed

+227
-5
lines changed

4 files changed

+227
-5
lines changed

benchmarks/src/Benchmarks.elm

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import Benchmark.Runner.Alternative as BenchmarkRunner
2020
import List.Extra
2121
import List.Extra.DropRight
2222
import List.Extra.GroupsOf
23+
import List.Extra.InsertAt
2324
import List.Extra.Lift
2425
import List.Extra.NotMember
2526
import List.Extra.TakeRight
@@ -275,6 +276,30 @@ listExtra =
275276
, ( "reverse", List.Extra.TakeRight.takeRightReverse )
276277
, ( "length", List.Extra.TakeRight.takeRightLength )
277278
]
279+
, rank "insertAt negative index"
280+
(\insertAt -> insertAt -3 999 intList)
281+
[ ( "recursion", List.Extra.InsertAt.insertAtRecursion )
282+
, ( "takeDrop", List.Extra.InsertAt.insertAtTakeDrop )
283+
, ( "splitAt", List.Extra.InsertAt.insertAtSplitAt )
284+
, ( "recursion2", List.Extra.InsertAt.insertAtRecursion2 )
285+
, ( "recursion3", List.Extra.InsertAt.insertAtRecursion3 )
286+
]
287+
, rank "insertAt good positive index"
288+
(\insertAt -> insertAt 50 999 intList)
289+
[ ( "recursion", List.Extra.InsertAt.insertAtRecursion )
290+
, ( "takeDrop", List.Extra.InsertAt.insertAtTakeDrop )
291+
, ( "splitAt", List.Extra.InsertAt.insertAtSplitAt )
292+
, ( "recursion2", List.Extra.InsertAt.insertAtRecursion2 )
293+
, ( "recursion3", List.Extra.InsertAt.insertAtRecursion3 )
294+
]
295+
, rank "insertAt bad positive index"
296+
(\insertAt -> insertAt 150 999 intList)
297+
[ ( "recursion", List.Extra.InsertAt.insertAtRecursion )
298+
, ( "takeDrop", List.Extra.InsertAt.insertAtTakeDrop )
299+
, ( "splitAt", List.Extra.InsertAt.insertAtSplitAt )
300+
, ( "recursion2", List.Extra.InsertAt.insertAtRecursion2 )
301+
, ( "recursion3", List.Extra.InsertAt.insertAtRecursion3 )
302+
]
278303
]
279304
++ List.concatMap toComparisonsGroupsOfWithStep (List.range 1 4)
280305
)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
module List.Extra.InsertAt exposing
2+
( insertAtRecursion
3+
, insertAtRecursion2
4+
, insertAtRecursion3
5+
, insertAtSplitAt
6+
, insertAtTakeDrop
7+
)
8+
9+
import List.Extra
10+
11+
12+
insertAtRecursion : Int -> a -> List a -> List a
13+
insertAtRecursion index value list =
14+
if index <= -1 then
15+
list
16+
17+
else
18+
let
19+
go : Int -> List a -> List a -> List a
20+
go i rest acc =
21+
if i == index then
22+
List.reverse acc ++ (value :: rest)
23+
24+
else
25+
case rest of
26+
[] ->
27+
-- index > length list
28+
list
29+
30+
head :: newRest ->
31+
go (i + 1) newRest (head :: acc)
32+
in
33+
go 0 list []
34+
35+
36+
insertAtRecursion2Help : Int -> a -> List a -> Int -> List a -> List a -> List a
37+
insertAtRecursion2Help index value list i rest acc =
38+
if i == index then
39+
List.foldl (::) (value :: rest) acc
40+
41+
else
42+
case rest of
43+
[] ->
44+
-- index > length list
45+
list
46+
47+
head :: newRest ->
48+
insertAtRecursion2Help index value list (i + 1) newRest (head :: acc)
49+
50+
51+
insertAtRecursion2 : Int -> a -> List a -> List a
52+
insertAtRecursion2 index value list =
53+
if index <= -1 then
54+
list
55+
56+
else
57+
insertAtRecursion2Help index value list 0 list []
58+
59+
60+
insertAtTakeDrop : Int -> a -> List a -> List a
61+
insertAtTakeDrop index value list =
62+
if index <= -1 then
63+
list
64+
65+
else
66+
let
67+
length =
68+
List.length list
69+
in
70+
if length < index then
71+
list
72+
73+
else
74+
List.take index list ++ (value :: List.drop index list)
75+
76+
77+
insertAtSplitAt : Int -> a -> List a -> List a
78+
insertAtSplitAt index value list =
79+
if index <= -1 then
80+
list
81+
82+
else
83+
let
84+
( before, after ) =
85+
List.Extra.splitAt index list
86+
in
87+
if List.isEmpty after && List.length before < index then
88+
list
89+
90+
else
91+
before ++ (value :: after)
92+
93+
94+
insertAtRecursion3Help : a -> List a -> Int -> List a -> List a -> List a
95+
insertAtRecursion3Help value list i rest acc =
96+
if i == 0 then
97+
List.foldl (::) (value :: rest) acc
98+
99+
else
100+
case rest of
101+
[] ->
102+
-- index > length list
103+
list
104+
105+
head :: newRest ->
106+
insertAtRecursion3Help value list (i - 1) newRest (head :: acc)
107+
108+
109+
insertAtRecursion3 : Int -> a -> List a -> List a
110+
insertAtRecursion3 index value list =
111+
if index <= -1 then
112+
list
113+
114+
else
115+
insertAtRecursion3Help value list index list []

src/List/Extra.elm

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module List.Extra exposing
2-
( last, init, getAt, cons, uncons, unconsLast, push, appendTo, prependTo, maximumBy, maximumWith, minimumBy, minimumWith, andMap, andThen, reverseMap, takeWhile, dropWhile, unique, uniqueBy, allDifferent, allDifferentBy, setIf, setAt, remove, updateIf, updateAt, updateIfIndex, removeAt, removeIfIndex, removeWhen, swapAt, stableSortWith
2+
( last, init, getAt, cons, uncons, unconsLast, push, appendTo, prependTo, maximumBy, maximumWith, minimumBy, minimumWith, andMap, andThen, reverseMap, takeWhile, dropWhile, unique, uniqueBy, allDifferent, allDifferentBy, insertAt, setIf, setAt, remove, updateIf, updateAt, updateIfIndex, removeAt, removeIfIndex, removeWhen, swapAt, stableSortWith
33
, intercalate, transpose, subsequences, permutations, interweave, cartesianProduct, uniquePairs
44
, foldl1, foldr1, indexedFoldl, indexedFoldr, Step(..), stoppableFoldl
55
, scanl, scanl1, scanr, scanr1, mapAccuml, mapAccumr, unfoldr, iterate, initialize, cycle, reverseRange
@@ -17,7 +17,7 @@ module List.Extra exposing
1717
1818
# Basics
1919
20-
@docs last, init, getAt, cons, uncons, unconsLast, push, appendTo, prependTo, maximumBy, maximumWith, minimumBy, minimumWith, andMap, andThen, reverseMap, takeWhile, dropWhile, unique, uniqueBy, allDifferent, allDifferentBy, setIf, setAt, remove, updateIf, updateAt, updateIfIndex, removeAt, removeIfIndex, removeWhen, swapAt, stableSortWith
20+
@docs last, init, getAt, cons, uncons, unconsLast, push, appendTo, prependTo, maximumBy, maximumWith, minimumBy, minimumWith, andMap, andThen, reverseMap, takeWhile, dropWhile, unique, uniqueBy, allDifferent, allDifferentBy, insertAt, setIf, setAt, remove, updateIf, updateAt, updateIfIndex, removeAt, removeIfIndex, removeWhen, swapAt, stableSortWith
2121
2222
2323
# List transformations
@@ -831,6 +831,43 @@ count predicate =
831831
0
832832

833833

834+
{-| Insert an element at a given index.
835+
If the index is out of bounds, nothing is changed.
836+
837+
[ 'a', 'c' ] |> insertAt 1 'b'
838+
--> [ 'a', 'b', 'c' ]
839+
840+
[ 'a', 'c' ] |> insertAt -1 'b'
841+
--> [ 'a', 'c' ]
842+
843+
[ 'a', 'c' ] |> insertAt 100 'b'
844+
--> [ 'a', 'c' ]
845+
846+
-}
847+
insertAt : Int -> a -> List a -> List a
848+
insertAt index value list =
849+
if index <= -1 then
850+
list
851+
852+
else
853+
insertAtHelp value list index list []
854+
855+
856+
insertAtHelp : a -> List a -> Int -> List a -> List a -> List a
857+
insertAtHelp value list i rest acc =
858+
if i == 0 then
859+
List.foldl (::) (value :: rest) acc
860+
861+
else
862+
case rest of
863+
[] ->
864+
-- index > length list
865+
list
866+
867+
head :: newRest ->
868+
insertAtHelp value list (i - 1) newRest (head :: acc)
869+
870+
834871
{-| Replace all values that satisfy a predicate with a replacement value.
835872
-}
836873
setIf : (a -> Bool) -> a -> List a -> List a
@@ -1596,7 +1633,8 @@ splitWhen predicate list =
15961633

15971634
{-| Take the last _n_ members of a list.
15981635
1599-
takeRight 2 [ 1, 2, 3, 4, 5 ] == [ 4, 5 ]
1636+
takeRight 2 [ 1, 2, 3, 4, 5 ]
1637+
--> [ 4, 5 ]
16001638
16011639
-}
16021640
takeRight : Int -> List a -> List a
@@ -1609,7 +1647,8 @@ takeRight n lst =
16091647

16101648
{-| Drop the last _n_ members of a list.
16111649
1612-
dropRight 2 [ 1, 2, 3, 4, 5 ] == [ 1, 2, 3 ]
1650+
dropRight 2 [ 1, 2, 3, 4, 5 ]
1651+
--> [ 1, 2, 3 ]
16131652
16141653
-}
16151654
dropRight : Int -> List a -> List a

tests/ListTests.elm

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module ListTests exposing (all)
22

33
import Expect
4-
import Fuzz
4+
import Fuzz exposing (Fuzzer)
55
import List.Extra exposing (Step(..))
66
import Test exposing (Test, describe, fuzz, fuzz2, fuzz3, test)
77

@@ -1001,6 +1001,36 @@ all =
10011001
Expect.equal (List.Extra.minimumBy (\x -> x.val) [ { id = 1, val = 2 }, { id = 2, val = 1 }, { id = 3, val = 1 } ])
10021002
(Just { id = 2, val = 1 })
10031003
]
1004+
, describe "insertAt"
1005+
[ fuzz3
1006+
negativeIntFuzzer
1007+
Fuzz.int
1008+
(Fuzz.list Fuzz.int)
1009+
"negative index returns original list"
1010+
<|
1011+
\negativeIndex value list ->
1012+
List.Extra.insertAt negativeIndex value list
1013+
|> Expect.equalLists list
1014+
, fuzz2
1015+
(Fuzz.intRange 0 4)
1016+
(Fuzz.listOfLengthBetween 4 10 (Fuzz.intRange 0 10))
1017+
"index in bounds (0 <= index <= length) inserts in the right place in the list"
1018+
<|
1019+
\goodIndex list ->
1020+
-- -1 is guaranteed to not be in the fuzzed input list
1021+
List.Extra.insertAt goodIndex -1 list
1022+
|> List.Extra.removeAt goodIndex
1023+
|> Expect.equalLists list
1024+
, fuzz3
1025+
(Fuzz.intRange 5 10)
1026+
Fuzz.int
1027+
(Fuzz.listOfLengthBetween 0 4 Fuzz.int)
1028+
"index out of bounds returns original list"
1029+
<|
1030+
\badPositiveIndex value list ->
1031+
List.Extra.insertAt badPositiveIndex value list
1032+
|> Expect.equalLists list
1033+
]
10041034
, describe "setIf"
10051035
[ test "empty list" <|
10061036
\() ->
@@ -1204,3 +1234,16 @@ all =
12041234
|> Expect.equal 50
12051235
]
12061236
]
1237+
1238+
1239+
negativeIntFuzzer : Fuzzer Int
1240+
negativeIntFuzzer =
1241+
Fuzz.int
1242+
|> Fuzz.map
1243+
(\n ->
1244+
if n == 0 then
1245+
-1
1246+
1247+
else
1248+
-(abs n)
1249+
)

0 commit comments

Comments
 (0)