diff --git a/paket.dependencies b/paket.dependencies index 075e0a2f..40bf08c0 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -1,7 +1,7 @@ framework: netstandard2.0, net8.0 source https://api.nuget.org/v3/index.json -nuget FSharp.Core >= 4.3.4 lowest_matching:true +nuget FSharp.Core >= 8.0.403 lowest_matching:true nuget Expecto nuget Expecto.FsCheck nuget FsCheck diff --git a/paket.lock b/paket.lock index c6d2ebce..b86b68d5 100644 --- a/paket.lock +++ b/paket.lock @@ -9,7 +9,7 @@ NUGET FsCheck (>= 2.14) FsCheck (2.16.6) FSharp.Core (>= 4.2.3) - FSharp.Core (4.3.4) + FSharp.Core (8.0.403) Microsoft.CodeCoverage (18.0.1) - restriction: || (== net8.0) (&& (== netstandard2.0) (>= net462)) (&& (== netstandard2.0) (>= net8.0)) Microsoft.NET.Test.Sdk (18.0.1) Microsoft.CodeCoverage (>= 18.0.1) - restriction: || (== net8.0) (&& (== netstandard2.0) (>= net462)) (&& (== netstandard2.0) (>= net8.0)) diff --git a/src/FSharpx.Collections/Collections.fs b/src/FSharpx.Collections/Collections.fs index 1dff6f9f..0c2b8d48 100644 --- a/src/FSharpx.Collections/Collections.fs +++ b/src/FSharpx.Collections/Collections.fs @@ -81,6 +81,16 @@ module Seq = let splitAt n seq = (Seq.take n seq, Seq.skip n seq) + /// The same as Seq.skip except will return None if not enough elements to skip or count passed is < 1 + [] + let rec trySkip<'T> (count: int) (source: seq<'T>) : Option> = + if count < 1 then + None + else + match tryHeadTail source with + | None -> None + | Some(head, tail) -> if count = 1 then Some tail else trySkip (count - 1) tail + /// Splits a sequences up to the point where the predicate holds let span predicate source = (Seq.takeWhile predicate source, Seq.skipWhile predicate source) diff --git a/tests/FSharpx.Collections.Tests/SeqTests.fs b/tests/FSharpx.Collections.Tests/SeqTests.fs index 1d334e63..757b62ac 100644 --- a/tests/FSharpx.Collections.Tests/SeqTests.fs +++ b/tests/FSharpx.Collections.Tests/SeqTests.fs @@ -74,6 +74,32 @@ module SeqTests = | _ -> failwith "Unreachable" } + test "If I trySkip and I don't have a head, I should return None" { Seq.empty |> Seq.trySkip 1 |> Expect.isNone "trySkip1" } + + test "If I trySkip1 a non-empty seq, I should return just tail" { + let data = [ 1; 2; 3 ] + let actual = data |> Seq.trySkip 1 + Expect.isSome "trySkip1" actual + + match actual with + | Some subSeq -> Expect.sequenceEqual "trySkip1" [ 2; 3 ] subSeq + | _ -> failwith "Unreachable" + } + + test "If I trySkip2 a non-empty seq, I should return just last" { + let data = [ 1; 2; 3 ] + let actual = data |> Seq.trySkip 2 + Expect.isSome "trySkip2" actual + + match actual with + | Some subSeq -> Expect.sequenceEqual "trySkip2" [ 3 ] subSeq + | _ -> failwith "Unreachable" + } + + test "If I trySkip2 and seq only contains 1 element, I should return None" { + seq { yield 1 } |> Seq.trySkip 2 |> Expect.isNone "trySkip2" + } + test "I should be a to split a seq at an index" { let (a, b) = Seq.splitAt 5 data Expect.sequenceEqual "splitAt" (List.toSeq [ 1.; 2.; 3.; 4.; 5. ]) a @@ -138,9 +164,11 @@ module SeqTests = test "I should get some if try to get a index inside the seq" { Seq.tryNth 2 data |> Expect.equal "tryNth" (Some(3.)) } - test "I should get none when trySkip past the end of the seq" { Seq.skipNoFail 20 data |> Expect.sequenceEqual "skipNoFail" Seq.empty } + test "I should get empty seq when skipNoFail past the end of the seq" { + Seq.skipNoFail 20 data |> Expect.sequenceEqual "skipNoFail" Seq.empty + } - test "I should get Some when trySkip" { + test "I should get Some when skipNoFail" { Seq.skipNoFail 5 data |> Expect.sequenceEqual "skipNoFail" (List.toSeq [ 6.; 7.; 8.; 9.; 10. ]) }