diff --git a/src/FSharpy.TaskSeq.Test/FSharpy.TaskSeq.Test.fsproj b/src/FSharpy.TaskSeq.Test/FSharpy.TaskSeq.Test.fsproj
index 92376960..9c102755 100644
--- a/src/FSharpy.TaskSeq.Test/FSharpy.TaskSeq.Test.fsproj
+++ b/src/FSharpy.TaskSeq.Test/FSharpy.TaskSeq.Test.fsproj
@@ -18,9 +18,11 @@
+
+
diff --git a/src/FSharpy.TaskSeq.Test/TaskSeq.Find.Tests.fs b/src/FSharpy.TaskSeq.Test/TaskSeq.Find.Tests.fs
index 3b2abd98..5f69e4ce 100644
--- a/src/FSharpy.TaskSeq.Test/TaskSeq.Find.Tests.fs
+++ b/src/FSharpy.TaskSeq.Test/TaskSeq.Find.Tests.fs
@@ -206,7 +206,7 @@ module SideEffects =
found |> should equal 3
i |> should equal 3 // only partial evaluation!
- // find next item. We do get a new iterator, but mutable state is now starting at '3'
+ // find next item. We do get a new iterator, but mutable state is now starting at '3', so first item now returned is '4'.
let! found = ts |> TaskSeq.find ((=) 4)
found |> should equal 4
i |> should equal 4 // only partial evaluation!
@@ -226,7 +226,7 @@ module SideEffects =
found |> should equal 3
i |> should equal 3 // only partial evaluation!
- // find next item. We do get a new iterator, but mutable state is now starting at '3'
+ // find next item. We do get a new iterator, but mutable state is now starting at '3', so first item now returned is '4'.
let! found = ts |> TaskSeq.findAsync (fun x -> task { return x = 4 })
found |> should equal 4
i |> should equal 4
@@ -360,7 +360,7 @@ module SideEffects =
found |> should equal (Some 3)
i |> should equal 3 // only partial evaluation!
- // find next item. We do get a new iterator, but mutable state is now starting at '3'
+ // find next item. We do get a new iterator, but mutable state is now starting at '3', so first item now returned is '4'.
let! found = ts |> TaskSeq.tryFind ((=) 4)
found |> should equal (Some 4)
i |> should equal 4 // only partial evaluation!
@@ -380,7 +380,7 @@ module SideEffects =
found |> should equal (Some 3)
i |> should equal 3 // only partial evaluation!
- // find next item. We do get a new iterator, but mutable state is now starting at '3'
+ // find next item. We do get a new iterator, but mutable state is now starting at '3', so first item now returned is '4'.
let! found = ts |> TaskSeq.tryFindAsync (fun x -> task { return x = 4 })
found |> should equal (Some 4)
i |> should equal 4
diff --git a/src/FSharpy.TaskSeq.Test/TaskSeq.FindIndex.Tests.fs b/src/FSharpy.TaskSeq.Test/TaskSeq.FindIndex.Tests.fs
new file mode 100644
index 00000000..e99aac32
--- /dev/null
+++ b/src/FSharpy.TaskSeq.Test/TaskSeq.FindIndex.Tests.fs
@@ -0,0 +1,474 @@
+module FSharpy.Tests.FindIndex
+
+open Xunit
+open FsUnit.Xunit
+open FsToolkit.ErrorHandling
+
+open FSharpy
+open System.Collections.Generic
+
+//
+// TaskSeq.findIndex
+// TaskSeq.findIndexAsync
+// TaskSeq.tryFindIndex
+// TaskSeq.tryFindIndexAsync
+//
+
+module EmptySeq =
+ [)>]
+ let ``TaskSeq-findIndex raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getEmptyVariant variant
+ |> TaskSeq.findIndex ((=) 12)
+ |> Task.ignore
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-findIndexAsync raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getEmptyVariant variant
+ |> TaskSeq.findIndexAsync (fun x -> task { return x = 12 })
+ |> Task.ignore
+ |> should throwAsyncExact typeof
+
+
+ [)>]
+ let ``TaskSeq-tryFindIndex returns None`` variant =
+ Gen.getEmptyVariant variant
+ |> TaskSeq.tryFindIndex ((=) 12)
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-tryFindIndexAsync returns None`` variant =
+ Gen.getEmptyVariant variant
+ |> TaskSeq.tryFindIndexAsync (fun x -> task { return x = 12 })
+ |> Task.map (should be None')
+
+module Immutable =
+ [)>]
+ let ``TaskSeq-findIndex sad path raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndex ((=) 0) // dummy tasks sequence starts at 1
+ |> Task.ignore
+
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-findIndexAsync sad path raises KeyNotFoundException`` variant =
+ fun () ->
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexAsync (fun x -> task { return x = 0 }) // dummy tasks sequence starts at 1
+ |> Task.ignore
+
+ |> should throwAsyncExact typeof
+
+ [)>]
+ let ``TaskSeq-findIndex happy path middle of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndex (fun x -> x < 6 && x > 4)
+ |> Task.map (should equal 4) // zero based
+
+ [)>]
+ let ``TaskSeq-findIndexAsync happy path middle of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexAsync (fun x -> task { return x < 6 && x > 4 })
+ |> Task.map (should equal 4)
+
+ [)>]
+ let ``TaskSeq-findIndex happy path first item of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndex ((=) 1)
+ |> Task.map (should equal 0) // zero based
+
+ [)>]
+ let ``TaskSeq-findIndexAsync happy path first item of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexAsync (fun x -> task { return x = 1 })
+ |> Task.map (should equal 0) // zero based
+
+ [)>]
+ let ``TaskSeq-findIndex happy path last item of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndex ((=) 10)
+ |> Task.map (should equal 9) // zero based
+
+ [)>]
+ let ``TaskSeq-findIndexAsync happy path last item of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.findIndexAsync (fun x -> task { return x = 10 }) // dummy tasks seq ends at 50
+ |> Task.map (should equal 9) // zero based
+
+
+ //
+ //
+ // tryXXX stuff
+ // |
+ // |
+ // V
+
+ [)>]
+ let ``TaskSeq-tryFindIndex sad path returns None`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndex ((=) 0)
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-tryFindIndexAsync sad path return None`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndexAsync (fun x -> task { return x = 0 })
+ |> Task.map (should be None')
+
+ [)>]
+ let ``TaskSeq-tryFindIndex happy path middle of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndex (fun x -> x < 6 && x > 4)
+ |> Task.map (should equal (Some 4)) // zero based
+
+ [)>]
+ let ``TaskSeq-tryFindIndexAsync happy path middle of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndexAsync (fun x -> task { return x < 6 && x > 4 })
+ |> Task.map (should equal (Some 4)) // zero based
+
+ [)>]
+ let ``TaskSeq-tryFindIndex happy path first item of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndex ((=) 1)
+ |> Task.map (should equal (Some 0)) // zero based
+
+ [)>]
+ let ``TaskSeq-tryFindIndexAsync happy path first item of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndexAsync (fun x -> task { return x = 1 })
+ |> Task.map (should equal (Some 0)) // zero based
+
+ [)>]
+ let ``TaskSeq-tryFindIndex happy path last item of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndex ((=) 10)
+ |> Task.map (should equal (Some 9)) // zero based
+
+ [)>]
+ let ``TaskSeq-tryFindIndexAsync happy path last item of seq`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.tryFindIndexAsync (fun x -> task { return x = 10 })
+ |> Task.map (should equal (Some 9)) // zero based
+
+module SideEffects =
+ [)>]
+ let ``TaskSeq-findIndex KeyNotFoundException only sometimes for mutated state`` variant = task {
+ let ts = Gen.getSeqWithSideEffect variant
+ let findIndexer = (=) 11
+
+ // first: error, item is not there
+ fun () -> TaskSeq.findIndex findIndexer ts |> Task.ignore
+ |> should throwAsyncExact typeof
+
+ // findIndex again: no error, because of side effects
+ let! found = TaskSeq.findIndex findIndexer ts
+ found |> should equal 0 // zero based, first item in 'updated' sequence is 11
+
+ // findIndex once more: error, item is not there anymore.
+ fun () -> TaskSeq.findIndex findIndexer ts |> Task.ignore
+ |> should throwAsyncExact typeof
+ }
+
+ [)>]
+ let ``TaskSeq-findIndexAsync KeyNotFoundException only sometimes for mutated state`` variant = task {
+ let ts = Gen.getSeqWithSideEffect variant
+ let findIndexer x = task { return x = 11 }
+
+ // first: error, item is not there
+ fun () -> TaskSeq.findIndexAsync findIndexer ts |> Task.ignore
+ |> should throwAsyncExact typeof
+
+ // findIndex again: no error, because of side effects
+ let! found = TaskSeq.findIndexAsync findIndexer ts
+ found |> should equal 0 // zero based, first item in 'updated' sequence is 11
+
+ // findIndex once more: error, item is not there anymore.
+ fun () -> TaskSeq.findIndexAsync findIndexer ts |> Task.ignore
+ |> should throwAsyncExact typeof
+ }
+
+ []
+ let ``TaskSeq-findIndex _specialcase_ prove we don't read past the found item`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ for x in 10..19 do
+ i <- i + 1
+ yield x
+ }
+
+ let! found = ts |> TaskSeq.findIndex ((=) 13)
+ found |> should equal 3
+ i |> should equal 4 // only partial evaluation!
+
+ // findIndex next item. We do get a new iterator, but mutable state is now starting at '4'
+ let! found = ts |> TaskSeq.findIndex ((=) 14)
+ found |> should equal 4
+ i |> should equal 9 // only partial evaluation!
+ }
+
+ []
+ let ``TaskSeq-findIndexAsync _specialcase_ prove we don't read past the found item`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ for x in 10..19 do
+ i <- i + 1
+ yield x
+ }
+
+ let! found = TaskSeq.findIndexAsync (fun x -> task { return x = 13 }) ts
+ found |> should equal 3
+ i |> should equal 4 // only partial evaluation!
+
+ // findIndex next item. We do get a new iterator, but mutable state is now starting at '4'
+ let! found = TaskSeq.findIndexAsync (fun x -> task { return x = 14 }) ts
+ found |> should equal 4
+ i |> should equal 9 // started counting again
+ }
+
+ []
+ let ``TaskSeq-findIndex _specialcase_ prove we don't read past the found item v2`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ yield 42
+ i <- i + 1
+ i <- i + 1
+ }
+
+ let! found = ts |> TaskSeq.findIndex ((=) 42)
+ found |> should equal 0 // first item has index 0
+ i |> should equal 0 // because no MoveNext after found item, the last statements are not executed
+ }
+
+ []
+ let ``TaskSeq-findIndexAsync _specialcase_ prove we don't read past the found item v2`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ yield 42
+ i <- i + 1
+ i <- i + 1
+ }
+
+ let! found = TaskSeq.findIndexAsync (fun x -> task { return x = 42 }) ts
+ found |> should equal 0 // first item has index 0
+ i |> should equal 0 // because no MoveNext after found item, the last statements are not executed
+ }
+
+ []
+ let ``TaskSeq-findIndex _specialcase_ prove statement after yield is not evaluated`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ for x in 10..19 do
+ yield x
+ i <- i + 1
+ }
+
+ let! found = ts |> TaskSeq.findIndex ((=) 10)
+ found |> should equal 0
+ i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated
+
+ // findIndex some next item. We do get a new iterator, but mutable state is now starting at '1'
+ let! found = ts |> TaskSeq.findIndex ((=) 14)
+ found |> should equal 4
+ i |> should equal 4 // only partial evaluation!
+ }
+
+ []
+ let ``TaskSeq-findIndexAsync _specialcase_ prove statement after yield is not evaluated`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ for x in 10..19 do
+ yield x
+ i <- i + 1
+ }
+
+ let! found =
+ ts
+ |> TaskSeq.findIndexAsync (fun x -> task { return x = 10 })
+
+ found |> should equal 0
+ i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated
+
+ // findIndex some next item. We do get a new iterator, but mutable state is now starting at '1'
+ let! found =
+ ts
+ |> TaskSeq.findIndexAsync (fun x -> task { return x = 14 })
+
+ found |> should equal 4
+ i |> should equal 4 // only partial evaluation!
+ }
+
+
+ //
+ //
+ // tryXXX stuff
+ // |
+ // |
+ // V
+
+ [)>]
+ let ``TaskSeq-tryFindIndex KeyNotFoundException only sometimes for mutated state`` variant = task {
+ let ts = Gen.getSeqWithSideEffect variant
+ let findIndexer = (=) 11
+
+ // first: None
+ let! found = TaskSeq.tryFindIndex findIndexer ts
+ found |> should be None'
+
+ // findIndex again: found now, because of side effects
+ let! found = TaskSeq.tryFindIndex findIndexer ts
+ found |> should equal (Some 0) // item with value '11' is at index 0 in 'updated' sequence
+
+ // findIndex once more: None
+ let! found = TaskSeq.tryFindIndex findIndexer ts
+ found |> should be None'
+ }
+
+ [)>]
+ let ``TaskSeq-tryFindIndexAsync KeyNotFoundException only sometimes for mutated state`` variant = task {
+ let ts = Gen.getSeqWithSideEffect variant
+ let findIndexer x = task { return x = 11 }
+
+ // first: None
+ let! found = TaskSeq.tryFindIndexAsync findIndexer ts
+ found |> should be None'
+
+ // findIndex again: found now, because of side effects
+ let! found = TaskSeq.tryFindIndexAsync findIndexer ts
+ found |> should equal (Some 0) // item with value '11' is at index 0 in 'updated' sequence
+
+ // findIndex once more: None
+ let! found = TaskSeq.tryFindIndexAsync findIndexer ts
+ found |> should be None'
+ }
+
+ []
+ let ``TaskSeq-tryFindIndex _specialcase_ prove we don't read past the found item`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ for x in 10..19 do
+ i <- i + 1
+ yield x
+ }
+
+ let! found = ts |> TaskSeq.tryFindIndex ((=) 13)
+ found |> should equal (Some 3)
+ i |> should equal 4 // only partial evaluation!
+
+ // findIndex next item. We do get a new iterator, but mutable state is now starting at '4'
+ let! found = ts |> TaskSeq.tryFindIndex ((=) 14)
+ found |> should equal (Some 4)
+ i |> should equal 9 // only partial evaluation!
+ }
+
+ []
+ let ``TaskSeq-tryFindIndexAsync _specialcase_ prove we don't read past the found item`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ for x in 10..19 do
+ i <- i + 1
+ yield x
+ }
+
+ let! found = TaskSeq.tryFindIndexAsync (fun x -> task { return x = 13 }) ts
+
+ found |> should equal (Some 3)
+ i |> should equal 4 // only partial evaluation!
+
+ // findIndex next item. We do get a new iterator, but mutable state is now starting at '4'
+ let! found = TaskSeq.tryFindIndexAsync (fun x -> task { return x = 14 }) ts
+
+ found |> should equal (Some 4)
+ i |> should equal 9
+ }
+
+ []
+ let ``TaskSeq-tryFindIndex _specialcase_ prove we don't read past the found item v2`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ yield 42
+ i <- i + 1
+ i <- i + 1
+ }
+
+ let! found = ts |> TaskSeq.tryFindIndex ((=) 42)
+ found |> should equal (Some 0) // first item has index 0
+ i |> should equal 0 // because no MoveNext after found item, the last statements are not executed
+ }
+
+ []
+ let ``TaskSeq-tryFindIndexAsync _specialcase_ prove we don't read past the found item v2`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ yield 42
+ i <- i + 1
+ i <- i + 1
+ }
+
+ let! found =
+ ts
+ |> TaskSeq.tryFindIndexAsync (fun x -> task { return x = 42 })
+
+ found |> should equal (Some 0) // first item: idx 0
+ i |> should equal 0 // because no MoveNext after found item, the last statements are not executed
+ }
+
+ []
+ let ``TaskSeq-tryFindIndex _specialcase_ prove statement after yield is not evaluated`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ for x in 10..19 do
+ yield x
+ i <- i + 1
+ }
+
+ let! found = ts |> TaskSeq.tryFindIndex ((=) 10)
+ found |> should equal (Some 0)
+ i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated
+
+ // findIndex some next item. We do get a new iterator, but mutable state is now starting at '1'
+ let! found = ts |> TaskSeq.tryFindIndex ((=) 14)
+ found |> should equal (Some 4)
+ i |> should equal 4 // only partial evaluation!
+ }
+
+ []
+ let ``TaskSeq-tryFindIndexAsync _specialcase_ prove statement after yield is not evaluated`` () = task {
+ let mutable i = 0
+
+ let ts = taskSeq {
+ for x in 10..19 do
+ yield x
+ i <- i + 1
+ }
+
+ let! found =
+ ts
+ |> TaskSeq.tryFindIndexAsync (fun x -> task { return x = 10 })
+
+ found |> should equal (Some 0)
+ i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated
+
+ // findIndex some next item. We do get a new iterator, but mutable state is now starting at '1'
+ let! found =
+ ts
+ |> TaskSeq.tryFindIndexAsync (fun x -> task { return x = 14 })
+
+ found |> should equal (Some 4)
+ i |> should equal 4 // only partial evaluation!
+ }
diff --git a/src/FSharpy.TaskSeq.Test/TaskSeq.Indexed.Tests.fs b/src/FSharpy.TaskSeq.Test/TaskSeq.Indexed.Tests.fs
new file mode 100644
index 00000000..8b38a55a
--- /dev/null
+++ b/src/FSharpy.TaskSeq.Test/TaskSeq.Indexed.Tests.fs
@@ -0,0 +1,34 @@
+module FSharpy.Tests.Indexed
+
+open Xunit
+open FsUnit.Xunit
+open FsToolkit.ErrorHandling
+
+open FSharpy
+
+//
+// TaskSeq.indexed
+//
+
+module EmptySeq =
+ [)>]
+ let ``TaskSeq-indexed on empty`` variant =
+ Gen.getEmptyVariant variant
+ |> TaskSeq.indexed
+ |> verifyEmpty
+
+module Immutable =
+ []
+ let ``TaskSeq-indexed starts at zero`` () =
+ taskSeq { yield 99 }
+ |> TaskSeq.indexed
+ |> TaskSeq.head
+ |> Task.map (should equal (0, 99))
+
+ [)>]
+ let ``TaskSeq-indexed`` variant =
+ Gen.getSeqImmutable variant
+ |> TaskSeq.indexed
+ |> TaskSeq.toArrayAsync
+ |> Task.map (Array.forall (fun (x, y) -> x + 1 = y))
+ |> Task.map (should be True)
diff --git a/src/FSharpy.TaskSeq/TaskSeq.fs b/src/FSharpy.TaskSeq/TaskSeq.fs
index 9e47f186..3bb1a9c7 100644
--- a/src/FSharpy.TaskSeq/TaskSeq.fs
+++ b/src/FSharpy.TaskSeq/TaskSeq.fs
@@ -165,7 +165,10 @@ module TaskSeq =
let cast source : taskSeq<'T> = Internal.map (SimpleAction(fun (x: obj) -> x :?> 'T)) source
let box source = Internal.map (SimpleAction(fun x -> box x)) source
- let unbox<'U when 'U: struct> (source: taskSeq) : taskSeq<'U> = Internal.map (SimpleAction(fun x -> unbox x)) source
+
+ let unbox<'U when 'U: struct> (source: taskSeq) : taskSeq<'U> =
+ Internal.map (SimpleAction(fun x -> unbox x)) source
+
let iter action source = Internal.iter (SimpleAction action) source
let iteri action source = Internal.iter (CountableAction action) source
let iterAsync action source = Internal.iter (AsyncSimpleAction action) source
@@ -222,6 +225,14 @@ module TaskSeq =
| None -> return invalidArg (nameof source) "The input sequence contains more than one element."
}
+ let indexed (source: taskSeq<'T>) = taskSeq {
+ let mutable i = 0
+
+ for x in source do
+ yield i, x
+ i <- i + 1
+ }
+
let choose chooser source = Internal.choose (TryPick chooser) source
let chooseAsync chooser source = Internal.choose (TryPickAsync chooser) source
let filter predicate source = Internal.filter (Predicate predicate) source
@@ -230,6 +241,8 @@ module TaskSeq =
let tryPickAsync chooser source = Internal.tryPick (TryPickAsync chooser) source
let tryFind predicate source = Internal.tryFind (Predicate predicate) source
let tryFindAsync predicate source = Internal.tryFind (PredicateAsync predicate) source
+ let tryFindIndex predicate source = Internal.tryFindIndex (Predicate predicate) source
+ let tryFindIndexAsync predicate source = Internal.tryFindIndex (PredicateAsync predicate) source
let pick chooser source = task {
match! Internal.tryPick (TryPick chooser) source with
@@ -255,6 +268,20 @@ module TaskSeq =
| None -> return Internal.raiseNotFound ()
}
+ let findIndex predicate source = task {
+ match! Internal.tryFindIndex (Predicate predicate) source with
+ | Some item -> return item
+ | None -> return Internal.raiseNotFound ()
+ }
+
+ let findIndexAsync predicate source = task {
+ match! Internal.tryFindIndex (PredicateAsync predicate) source with
+ | Some item -> return item
+ | None -> return Internal.raiseNotFound ()
+ }
+
+
+
//
// zip/unzip etc functions
//
diff --git a/src/FSharpy.TaskSeq/TaskSeq.fsi b/src/FSharpy.TaskSeq/TaskSeq.fsi
index 7463d74a..4841289d 100644
--- a/src/FSharpy.TaskSeq/TaskSeq.fsi
+++ b/src/FSharpy.TaskSeq/TaskSeq.fsi
@@ -125,6 +125,16 @@ module TaskSeq =
/// Maps over the taskSeq, applying the mapper function to each item. This function is non-blocking.
val map: mapper: ('T -> 'U) -> source: taskSeq<'T> -> taskSeq<'U>
+ ///
+ /// Builds a new task sequence whose elements are the corresponding elements of the input task
+ /// sequence paired with the integer index (from 0) of each element.
+ /// Does not evaluate the input sequence until requested.
+ ///
+ /// The input task sequence.
+ /// The resulting task sequence of tuples.
+ /// Thrown when the input sequence is null.
+ val indexed: source: taskSeq<'T> -> taskSeq
+
/// Maps over the taskSeq with an index, applying the mapper function to each item. This function is non-blocking.
val mapi: mapper: (int -> 'T -> 'U) -> source: taskSeq<'T> -> taskSeq<'U>
@@ -255,6 +265,20 @@ module TaskSeq =
///
val tryFindAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task<'T option>
+ ///
+ /// Returns the index, starting from zero, of the task sequence in for which the given function
+ /// returns . Returns if no such element exists.
+ /// If is asynchronous, consider using .
+ ///
+ val tryFindIndex: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task
+
+ ///
+ /// Returns the index, starting from zero, of the task sequence in for which the given asynchronous function
+ /// returns . Returns if no such element exists.
+ /// If does not need to be asynchronous, consider using .
+ ///
+ val tryFindIndexAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task
+
///
/// Applies the given function to successive elements of the task sequence
@@ -292,6 +316,24 @@ module TaskSeq =
/// evaluated by the function.
val findAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task<'T>
+ ///
+ /// Returns the index, starting from zero, of the first element of the task sequence in for which
+ /// the given function returns .
+ /// If is asynchronous, consider using .
+ ///
+ /// Thrown if no element returns when
+ /// evaluated by the function.
+ val findIndex: predicate: ('T -> bool) -> source: taskSeq<'T> -> Task
+
+ ///
+ /// Returns the index, starting from zero, of the task sequence in for which the given
+ /// asynchronous function returns .
+ /// If does not need to be asynchronous, consider using .
+ ///
+ /// Thrown if no element returns when
+ /// evaluated by the function.
+ val findIndexAsync: predicate: ('T -> #Task) -> source: taskSeq<'T> -> Task
+
///
/// Zips two task sequences, returning a taskSeq of the tuples of each sequence, in order. May raise ArgumentException
/// if the sequences are or unequal length.
diff --git a/src/FSharpy.TaskSeq/TaskSeqInternal.fs b/src/FSharpy.TaskSeq/TaskSeqInternal.fs
index 9acfd42a..5d804b12 100644
--- a/src/FSharpy.TaskSeq/TaskSeqInternal.fs
+++ b/src/FSharpy.TaskSeq/TaskSeqInternal.fs
@@ -374,6 +374,38 @@ module internal TaskSeqInternal =
return foundItem
}
+ let tryFindIndex predicate (source: taskSeq<_>) = task {
+ use e = source.GetAsyncEnumerator(CancellationToken())
+
+ let mutable go = true
+ let mutable isFound = false
+ let mutable index = -1
+ let! step = e.MoveNextAsync()
+ go <- step
+
+ match predicate with
+ | Predicate predicate ->
+ while go && not isFound do
+ index <- index + 1
+ isFound <- predicate e.Current
+
+ if not isFound then
+ let! step = e.MoveNextAsync()
+ go <- step
+
+ | PredicateAsync predicate ->
+ while go && not isFound do
+ index <- index + 1
+ let! predicateResult = predicate e.Current
+ isFound <- predicateResult
+
+ if not isFound then
+ let! step = e.MoveNextAsync()
+ go <- step
+
+ if isFound then return Some index else return None
+ }
+
let choose chooser (source: taskSeq<_>) = taskSeq {
match chooser with
| TryPick picker ->