Skip to content

Commit 59eb5d4

Browse files
authored
Merge pull request #14 from alasconnect/fsdv-nonemptylist-utils
Add utility functions to help with using NonEmptyLists [MBI-53]
2 parents 98ed022 + 394e2c7 commit 59eb5d4

File tree

1 file changed

+228
-10
lines changed

1 file changed

+228
-10
lines changed

src/FSharp.Data.Validation/NonEmptyList.fs

Lines changed: 228 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,242 @@ module NonEmptyList =
1111
let (><) y x = cons y (singleton x)
1212
let (>-) x xs = cons x xs
1313

14+
// Convert a NonEmptyList to a List
15+
let rec toList xs =
16+
match xs with
17+
| Single x -> [x]
18+
| Cons (x, xs') -> List.Cons (x, toList xs')
19+
20+
// Convert a List to NonEmptyList
1421
let rec ofList x xs =
1522
match xs with
16-
| [] -> Single x
17-
| x' :: xs' -> x >- (ofList x' xs')
23+
| [] -> Single x
24+
| h :: t -> x >- (ofList h t)
1825

26+
// Combine two NonEmptyLists, putting the first before the second
1927
let rec append xs ys =
2028
match xs with
21-
| Single x -> Cons (x,ys)
22-
| Cons (x,xs') -> Cons (x, append xs' ys)
29+
| Single x -> Cons (x, ys)
30+
| Cons (x, xs') -> Cons (x, append xs' ys)
2331

32+
// Combine a List and a NonEmptyList, putting the first before the second
33+
// Results in a NonEmptyList
2434
let appendList xs ys =
2535
match xs with
26-
| [] -> ys
27-
| h :: t -> append (ofList h t) ys
36+
| [] -> ys
37+
| h :: t -> append (ofList h t) ys
2838

29-
let rec toList xs =
39+
// Combine two NonEmptyLists, putting the second before the first
40+
let rec prepend xs ys =
41+
match ys with
42+
| Single y -> Cons (y, xs)
43+
| Cons (y, ys') -> Cons (y, prepend ys' xs)
44+
45+
// Combine a List and a NonEmptyList, putting the second before the first
46+
// Results in a NonEmptyList
47+
let prependList xs ys =
48+
match xs with
49+
| [] -> ys
50+
| h :: t -> prepend (ofList h t) ys
51+
52+
// Get the number of items in the NonEmptyList
53+
let rec length xs =
54+
match xs with
55+
| Single _ -> 1
56+
| Cons (_, xs') -> 1 + length xs'
57+
58+
// Get the first item in the NonEmptyList
59+
let head xs =
60+
match xs with
61+
| Single x -> x
62+
| Cons (x, _) -> x
63+
64+
// Get the last item in the NonEmptyList
65+
let rec last xs =
66+
match xs with
67+
| Single x -> x
68+
| Cons (_, xs') -> last xs'
69+
70+
// Get the first n items in the NonEmptyList
71+
// If n is greater than the count, returns the original NonEmptyList
72+
// n must be greater than zero in order to return a result
73+
let take i xs =
74+
if i < 1 then None
75+
else
76+
match List.truncate i (toList xs) with
77+
| [] -> None // theoretically impossible
78+
| h :: t -> Some (ofList h t)
79+
80+
// Get the first items in the NonEmptyList that meet the provided criteria
81+
let takeWhile f xs =
82+
match List.takeWhile f (toList xs) with
83+
| [] -> None
84+
| h :: t -> Some (ofList h t)
85+
86+
// Get all items in the NonEmptyList except for the last one
87+
let init xs =
88+
match xs with
89+
| Single _ -> None
90+
| _ -> take (length xs - 1) xs
91+
92+
// Get all items in the NonEmptyList except for the first one
93+
let tail xs =
94+
match xs with
95+
| Single _ -> None
96+
| Cons (_, xs') -> Some xs'
97+
98+
// Remove the first n items in the NonEmptyList
99+
// n must be greater than zero and less than the count in order to return a result
100+
// Technically this function can throw exceptions (List.skip), but the initial checks will avoid that
101+
let skip i xs =
102+
let xs' = toList xs
103+
if i < 0 then None
104+
elif i >= (List.length xs') then None
105+
else
106+
match List.skip i xs' with
107+
| [] -> None // theoretically impossible
108+
| h :: t -> Some (ofList h t)
109+
110+
// Remove the first items in the NonEmptyList that meet the provided criteria
111+
let skipWhile f xs =
112+
match List.skipWhile f (toList xs) with
113+
| [] -> None
114+
| h :: t -> Some (ofList h t)
115+
116+
// Apply the provided function to each item in the NonEmptyList
117+
let rec map f xs =
30118
match xs with
31-
| Single x -> [x]
32-
| Cons (x,xs') -> List.Cons (x, toList xs')
119+
| Single x -> Single (f x)
120+
| Cons (x, xs') -> Cons (f x, map f xs')
121+
122+
// Apply the provided function to each element of the collection, threading an
123+
// accumulator through the computation, returning a final result
124+
let fold f a xs = List.fold f a (toList xs)
125+
126+
// Determine if the provided item is in the NonEmptyList
127+
let contains x xs = List.contains x (toList xs)
128+
129+
// Get the provided item from the NonEmptyList if it exists
130+
let rec find x xs =
131+
match xs with
132+
| Single x' -> if x = x' then (Some x') else None
133+
| Cons (x', xs') -> if x = x' then (Some x') else find x xs'
134+
135+
// Determine if any item in the NonEmptyList meets the provided criteria
136+
let exists f xs =
137+
match List.where f (toList xs) with
138+
| [] -> false
139+
| _ -> true
140+
141+
// Determine if all items in the NonEmptyList meet the provided criteria
142+
let forall f xs = List.forall f (toList xs)
143+
144+
// Gets the items in the NonEmptyList that meet the provided criteria
145+
let where f xs =
146+
match List.where f (toList xs) with
147+
| [] -> None
148+
| h :: t -> Some (ofList h t)
149+
150+
// Get the item at the provided index (index zero) from the NonEmptyList
151+
// If the index is less than zero, get the item at the beginning of the NonEmptyList
152+
// If the index is greater than the (count - 1), get the item at the end of the NonEmptyList
153+
// Technically this function can throw exceptions (List.item), but the initial checks will avoid that
154+
let item i xs =
155+
let e = length xs - 1
156+
let i' = if i < 0 then 0 elif i > e then e else i
157+
List.item i' (toList xs)
158+
159+
// Add the provided item to the NonEmptyList at the provided index (index zero)
160+
// If the index is less than zero, the item will go at the beginning of the NonEmptyList
161+
// If the index is greater than the (count - 1), the item will go at the end of the NonEmptyList
162+
// Technically this function can throw exceptions (List.insertAt), but the initial checks will avoid that
163+
let insertAt i x xs =
164+
let e = length xs - 1
165+
let i' = if i < 0 then 0 elif i > e then e else i
166+
match List.insertAt i' x (toList xs) with
167+
| [] -> Single x // theoretically impossible
168+
| h :: t -> ofList h t
169+
170+
// Remove the item at the provided index (index zero) from the NonEmptyList
171+
// If the index is less than zero, the item at the beginning of the NonEmptyList will be removed
172+
// If the index is greater than the (count - 1), the item at the end of the NonEmptyList will be removed
173+
// If the NonEmptyList is a singleton, it will be returned as is
174+
// Technically this function can throw exceptions (List.removeAt), but the initial checks will avoid that
175+
let removeAt i xs =
176+
match xs with
177+
| Single _ -> xs
178+
| _ ->
179+
let e = length xs - 1
180+
let i' = if i < 0 then 0 elif i > e then e else i
181+
match List.removeAt i' (toList xs) with
182+
| [] -> xs // theoretically impossible
183+
| h :: t -> ofList h t
184+
185+
// Replace the item at the provided index (index zero) in the NonEmptyList
186+
// If the index is less than zero, the item at the beginning of the NonEmptyList will be replaced
187+
// If the index is greater than the (count - 1), the item at the end of the NonEmptyList will be replaced
188+
// If the NonEmptyList is a singleton, the single value will be replaced
189+
// Technically this function can throw exceptions (List.updateAt), but the initial checks will avoid that
190+
let updateAt i x xs =
191+
match xs with
192+
| Single _ -> Single x
193+
| _ ->
194+
let e = length xs - 1
195+
let i' = if i < 0 then 0 elif i > e then e else i
196+
match List.updateAt i' x (toList xs) with
197+
| [] -> xs // theoretically impossible
198+
| h :: t -> ofList h t
199+
200+
// Split the NonEmptyList into two NonEmptyLists, the first of which the provided constraint is true and the second is false
201+
// No result signifies that all the items in the NonEmptyList meet the constraint in the same way
202+
let partition f xs =
203+
match List.partition f (toList xs) with
204+
| ([], _) -> None
205+
| (_, []) -> None
206+
| (h :: t, h' :: t') -> Some (ofList h t, ofList h' t')
207+
208+
// Split the NonEmptyList at the provided index (index zero)
209+
// If the index is less than zero, the item at the beginning of the NonEmptyList will become a singleton
210+
// If the index is greater than the (count - 1), the item at the end of the NonEmptyList will become a singleton
211+
// No result signifies that the NonEmptyList is a singleton
212+
// Technically this function can throw exceptions (List.splitAt), but the initial checks will avoid that
213+
let splitAt i xs =
214+
match xs with
215+
| Single _ -> None
216+
| _ ->
217+
let e = length xs - 1
218+
let i' = if i < 0 then 0 elif i > e then e else i
219+
match List.splitAt i' (toList xs) with
220+
| ([], _) -> None // theoretically impossible
221+
| (_, []) -> None // theoretically impossible
222+
| (h :: t, h' :: t') -> Some (ofList h t, ofList h' t')
223+
224+
// Get the items in the NonEmptyList in reverse order
225+
// Technically this function can throw exceptions (List.head, List.tail), but the source NonEmptyList will avoid that
226+
let reverse xs =
227+
let xs' = toList xs |> List.rev
228+
ofList (List.head xs') (List.tail xs')
229+
230+
// Sort the NonEmptyList with Operators.compare
231+
let sort xs =
232+
match List.sort (toList xs) with
233+
| [] -> xs // theoretically impossible
234+
| h :: t -> ofList h t
235+
236+
// Sort the NonEmptyList with the keys given in the projection
237+
let sortBy f xs =
238+
match List.sortBy f (toList xs) with
239+
| [] -> xs // theoretically impossible
240+
| h :: t -> ofList h t
241+
242+
// Removes all duplicate items in the NonEmptyList
243+
let distinct xs =
244+
match List.distinct (toList xs) with
245+
| [] -> xs // theoretically impossible
246+
| h :: t -> ofList h t
33247

34-
//TODO: implement remaining functions.
248+
// Removes all duplicate items in the NonEmptyList, determined by the provided function
249+
let distinctBy f xs =
250+
match List.distinctBy f (toList xs) with
251+
| [] -> xs // theoretically impossible
252+
| h :: t -> ofList h t

0 commit comments

Comments
 (0)