Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/FSharpx.Collections/NonEmptyList.fs
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,47 @@ module NonEmptyList =
[<CompiledName("Zip")>]
let zip list1 list2 =
{ List = List.zip list1.List list2.List }

[<CompiledName("Iterate")>]
let iter action list =
List.iter action list.List

[<CompiledName("IterateIndexed")>]
let iteri action list =
List.iteri action list.List

[<CompiledName("SelectIndexed")>]
let mapi mapping list =
{ List = List.mapi mapping list.List }

[<CompiledName("Exists")>]
let exists predicate list =
List.exists predicate list.List

[<CompiledName("ForAll")>]
let forall predicate list =
List.forall predicate list.List

[<CompiledName("Contains")>]
let contains value list =
List.contains value list.List

[<CompiledName("SortWith")>]
let sortWith comparer list =
{ List = List.sortWith comparer list.List }

[<CompiledName("SortBy")>]
let sortBy projection list =
{ List = List.sortBy projection list.List }

[<CompiledName("Sort")>]
let sort list =
{ List = List.sort list.List }

[<CompiledName("MaximumBy")>]
let maxBy projection list =
List.maxBy projection list.List

[<CompiledName("MinimumBy")>]
let minBy projection list =
List.minBy projection list.List
111 changes: 110 additions & 1 deletion tests/FSharpx.Collections.Tests/NonEmptyListTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -257,4 +257,113 @@ module NonEmptyListTests =
(Prop.forAll(twoDifferentLengths())
<| fun (nel1, nel2) ->
Expect.throwsT<System.ArgumentException> (sprintf "length %i; length %i" nel1.Length nel2.Length) (fun () ->
NonEmptyList.zip nel1 nel2 |> ignore)) ]
NonEmptyList.zip nel1 nel2 |> ignore))

testPropertyWithConfig
config10k
"cons prepends an element"
(Prop.forAll(NonEmptyListGen.NonEmptyList())
<| fun nel ->
let consed = NonEmptyList.cons 42 nel
consed.Head = 42 && consed.Length = nel.Length + 1)

testPropertyWithConfig config10k "appendList combines NonEmptyList with plain list"
<| fun (a: _ list) (b: _ list) ->
if a.IsEmpty then
true
else
let neA = NonEmptyList.create a.Head a.Tail
(NonEmptyList.appendList neA b).Length = neA.Length + b.Length

testPropertyWithConfig
config10k
"toSeq yields all elements"
(Prop.forAll(NonEmptyListGen.NonEmptyList())
<| fun nel -> Seq.forall2 (=) (NonEmptyList.toSeq nel) (NonEmptyList.toList nel))

testPropertyWithConfig
config10k
"iter visits all elements"
(Prop.forAll(NonEmptyListGen.NonEmptyList())
<| fun nel ->
let visited = System.Collections.Generic.List<int>()
NonEmptyList.iter visited.Add nel
Seq.toList visited = NonEmptyList.toList nel)

testPropertyWithConfig
config10k
"iteri visits all elements with correct indices"
(Prop.forAll(NonEmptyListGen.NonEmptyList())
<| fun nel ->
let pairs = System.Collections.Generic.List<int * int>()
NonEmptyList.iteri (fun i x -> pairs.Add(i, x)) nel
let expected = nel |> NonEmptyList.toList |> List.mapi(fun i x -> i, x)
Seq.toList pairs = expected)

testPropertyWithConfig
config10k
"mapi produces indexed values"
(Prop.forAll(NonEmptyListGen.NonEmptyList())
<| fun nel ->
let actual = NonEmptyList.mapi (fun i x -> i, x) nel |> NonEmptyList.toList
let expected = nel |> NonEmptyList.toList |> List.mapi(fun i x -> i, x)
actual = expected)

testPropertyWithConfig
config10k
"exists returns true when element satisfies predicate"
(Prop.forAll(NonEmptyListGen.NonEmptyList())
<| fun nel -> NonEmptyList.exists (fun _ -> true) nel = true)

testPropertyWithConfig
config10k
"forall returns true when all elements satisfy predicate"
(Prop.forAll(NonEmptyListGen.NonEmptyList())
<| fun nel -> NonEmptyList.forall (fun _ -> true) nel = true)

testPropertyWithConfig config10k "contains finds a present element"
<| fun (xs: int list) ->
if xs.IsEmpty then
true
else
let nel = NonEmptyList.create xs.Head xs.Tail
NonEmptyList.contains xs.Head nel

testPropertyWithConfig
config10k
"sort produces sorted output"
(Prop.forAll(neListOfInt())
<| fun nel ->
let sorted = NonEmptyList.sort nel |> NonEmptyList.toList
let expected = nel |> NonEmptyList.toList |> List.sort
sorted = expected)

testPropertyWithConfig
config10k
"sortBy produces sorted output"
(Prop.forAll(neListOfInt())
<| fun nel ->
let sorted = NonEmptyList.sortBy id nel |> NonEmptyList.toList
let expected = nel |> NonEmptyList.toList |> List.sort
sorted = expected)

testPropertyWithConfig
config10k
"sortWith produces sorted output"
(Prop.forAll(neListOfInt())
<| fun nel ->
let sorted = NonEmptyList.sortWith compare nel |> NonEmptyList.toList
let expected = nel |> NonEmptyList.toList |> List.sort
sorted = expected)

testPropertyWithConfig
config10k
"maxBy returns maximum element"
(Prop.forAll(neListOfInt())
<| fun nel -> NonEmptyList.maxBy id nel = (nel |> NonEmptyList.toList |> List.max))

testPropertyWithConfig
config10k
"minBy returns minimum element"
(Prop.forAll(neListOfInt())
<| fun nel -> NonEmptyList.minBy id nel = (nel |> NonEmptyList.toList |> List.min)) ]
85 changes: 84 additions & 1 deletion tests/FSharpx.Collections.Tests/SeqTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -244,4 +244,87 @@ module SeqTests =
Expect.sequenceEqual "" a (a |> Seq.intersperse ',')
}

testPropertyWithConfig config10k "I should interperse always 2n-1 elements" intersperse ]
testPropertyWithConfig config10k "I should interperse always 2n-1 elements" intersperse

test "cons prepends an element to a seq" {
Seq.cons 0 [ 1; 2; 3 ]
|> Expect.sequenceEqual "cons" (List.toSeq [ 0; 1; 2; 3 ])
}

test "unCons returns None for empty seq" { Seq.unCons Seq.empty<int> |> Expect.isNone "unCons" }

test "unCons returns Some (head, tail) for non-empty seq" {
match Seq.unCons [ 1; 2; 3 ] with
| Some(head, tail) ->
Expect.equal "unCons head" 1 head
Expect.sequenceEqual "unCons tail" [ 2; 3 ] tail
| None -> failwith "Expected Some"
}

test "findExactlyOne returns the single matching element" {
Expect.equal "findExactlyOne" 3 (Seq.findExactlyOne ((=) 3) [ 1; 2; 3; 4; 5 ])
}

test "catOptions extracts Some values from a seq of options" {
let opts = [ Some 1; None; Some 2; None; Some 3 ]

Seq.catOptions opts
|> Expect.sequenceEqual "catOptions" (List.toSeq [ 1; 2; 3 ])
}

test "catOptions returns empty seq when all are None" {
let opts: int option list = [ None; None ]

Seq.catOptions opts
|> Expect.sequenceEqual "catOptions empty" Seq.empty
}

test "choice1s extracts Choice1Of2 values" {
let choices = [ Choice1Of2 1; Choice2Of2 "a"; Choice1Of2 2; Choice2Of2 "b" ]

Seq.choice1s choices
|> Expect.sequenceEqual "choice1s" (List.toSeq [ 1; 2 ])
}

test "choice2s extracts Choice2Of2 values" {
let choices = [ Choice1Of2 1; Choice2Of2 "a"; Choice1Of2 2; Choice2Of2 "b" ]

Seq.choice2s choices
|> Expect.sequenceEqual "choice2s" (List.toSeq [ "a"; "b" ])
}

test "partitionChoices splits into two seqs" {
let choices = [ Choice1Of2 1; Choice2Of2 "a"; Choice1Of2 2; Choice2Of2 "b" ]
let c1s, c2s = Seq.partitionChoices choices
Expect.sequenceEqual "partitionChoices c1s" (List.toSeq [ 1; 2 ]) c1s
Expect.sequenceEqual "partitionChoices c2s" (List.toSeq [ "a"; "b" ]) c2s
}

test "equalsWith returns true for equal seqs" { Expect.isTrue "equalsWith" (Seq.equalsWith (=) [ 1; 2; 3 ] [ 1; 2; 3 ]) }

test "equalsWith returns false for unequal seqs" { Expect.isFalse "equalsWith" (Seq.equalsWith (=) [ 1; 2; 3 ] [ 1; 2; 4 ]) }

test "equalsWith returns true for empty seqs" {
Expect.isTrue "equalsWith empty" (Seq.equalsWith (=) (Seq.empty<int>) (Seq.empty<int>))
}

test "groupNeighboursBy groups consecutive equal-key elements" {
let input = [ 1; 1; 2; 2; 1; 1 ]
let result = Seq.groupNeighboursBy id input |> Seq.toList
let keys = result |> List.map fst
Expect.equal "groupNeighboursBy keys" [ 1; 2; 1 ] keys
let groups = result |> List.map(snd >> Seq.toList)
Expect.equal "groupNeighboursBy groups" [ [ 1; 1 ]; [ 2; 2 ]; [ 1; 1 ] ] groups
}

test "groupNeighboursBy on empty seq gives empty result" {
Seq.groupNeighboursBy id (Seq.empty<int>)
|> Seq.isEmpty
|> Expect.isTrue "groupNeighboursBy empty"
}

test "groupNeighboursBy on singleton gives one group" {
let result = Seq.groupNeighboursBy id [ 42 ] |> Seq.toList
Expect.equal "groupNeighboursBy singleton" 1 result.Length
Expect.equal "groupNeighboursBy singleton key" 42 (fst result.[0])
} ]
Loading