Skip to content

Commit fdb316a

Browse files
committed
Implement max|min, maxBy|minBy and maxByAsync|minByAsync
1 parent 06bc268 commit fdb316a

File tree

3 files changed

+152
-1
lines changed

3 files changed

+152
-1
lines changed

src/FSharp.Control.TaskSeq/TaskSeq.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ type TaskSeq private () =
162162
// Utility functions
163163
//
164164

165+
static member max source = Internal.maxMin max source
166+
static member min source = Internal.maxMin min source
167+
static member maxBy projection source = Internal.maxMinBy (<) projection source // looks like 'less than', is 'greater than'
168+
static member minBy projection source = Internal.maxMinBy (>) projection source
169+
static member maxByAsync projection source = Internal.maxMinByAsync (<) projection source // looks like 'less than', is 'greater than'
170+
static member minByAsync projection source = Internal.maxMinByAsync (>) projection source
165171
static member length source = Internal.lengthBy None source
166172
static member lengthOrMax max source = Internal.lengthBeforeMax max source
167173
static member lengthBy predicate source = Internal.lengthBy (Some(Predicate predicate)) source

src/FSharp.Control.TaskSeq/TaskSeq.fsi

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,82 @@ type TaskSeq =
7171
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
7272
static member lengthByAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> Task<int>
7373

74+
/// <summary>
75+
/// Returns the greatest of all elements of the sequence, compared via <see cref="Operators.max" />.
76+
/// </summary>
77+
///
78+
/// <param name="source">The input task sequence.</param>
79+
/// <returns>The largest element of the sequence.</returns>
80+
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
81+
/// <exception cref="T:ArgumentException">Thrown when the input task sequence is empty.</exception>
82+
static member max: source: TaskSeq<'T> -> Task<'T> when 'T: comparison
83+
84+
/// <summary>
85+
/// Returns the smallest of all elements of the sequence, compared via <see cref="Operators.min" />.
86+
/// </summary>
87+
///
88+
/// <param name="source">The input task sequence.</param>
89+
/// <returns>The smallest element of the sequence.</returns>
90+
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
91+
/// <exception cref="T:ArgumentException">Thrown when the input task sequence is empty.</exception>
92+
static member min: source: TaskSeq<'T> -> Task<'T> when 'T: comparison
93+
94+
/// <summary>
95+
/// Returns the greatest of all elements of the task sequence, compared via <see cref="Operators.max" />
96+
/// on the result of applying the function <paramref name="projection" /> to each element.
97+
///
98+
/// If <paramref name="projection" /> is asynchronous, use <see cref="TaskSeq.maxByAsync" />.
99+
/// </summary>
100+
///
101+
/// <param name="projection">A function to transform items from the input sequence into comparable keys.</param>
102+
/// <param name="source">The input sequence.</param>
103+
/// <returns>The largest element of the sequence.</returns>
104+
/// <exception cref="T:ArgumentNullException">Thrown when the input sequence is null.</exception>
105+
/// <exception cref="T:ArgumentException">Thrown when the input sequence is empty.</exception>
106+
static member maxBy: projection: ('T -> 'U) -> source: TaskSeq<'T> -> Task<'T> when 'U: comparison
107+
108+
/// <summary>
109+
/// Returns the smallest of all elements of the task sequence, compared via <see cref="Operators.min" />
110+
/// on the result of applying the function <paramref name="projection" /> to each element.
111+
///
112+
/// If <paramref name="projection" /> is asynchronous, use <see cref="TaskSeq.minByAsync" />.
113+
/// </summary>
114+
///
115+
/// <param name="projection">A function to transform items from the input sequence into comparable keys.</param>
116+
/// <param name="source">The input sequence.</param>
117+
/// <returns>The smallest element of the sequence.</returns>
118+
/// <exception cref="T:ArgumentNullException">Thrown when the input sequence is null.</exception>
119+
/// <exception cref="T:ArgumentException">Thrown when the input sequence is empty.</exception>
120+
static member minBy: projection: ('T -> 'U) -> source: TaskSeq<'T> -> Task<'T> when 'U: comparison
121+
122+
/// <summary>
123+
/// Returns the greatest of all elements of the task sequence, compared via <see cref="Operators.max" />
124+
/// on the result of applying the function <paramref name="projection" /> to each element.
125+
///
126+
/// If <paramref name="projection" /> is synchronous, use <see cref="TaskSeq.maxBy" />.
127+
/// </summary>
128+
///
129+
/// <param name="projection">A function to transform items from the input sequence into comparable keys.</param>
130+
/// <param name="source">The input sequence.</param>
131+
/// <returns>The largest element of the sequence.</returns>
132+
/// <exception cref="T:ArgumentNullException">Thrown when the input sequence is null.</exception>
133+
/// <exception cref="T:ArgumentException">Thrown when the input sequence is empty.</exception>
134+
static member maxByAsync: projection: ('T -> #Task<'U>) -> source: TaskSeq<'T> -> Task<'T> when 'U: comparison
135+
136+
/// <summary>
137+
/// Returns the smallest of all elements of the task sequence, compared via <see cref="Operators.min" />
138+
/// on the result of applying the function <paramref name="projection" /> to each element.
139+
///
140+
/// If <paramref name="projection" /> is synchronous, use <see cref="TaskSeq.minBy" />.
141+
/// </summary>
142+
///
143+
/// <param name="projection">A function to transform items from the input sequence into comparable keys.</param>
144+
/// <param name="source">The input sequence.</param>
145+
/// <returns>The smallest element of the sequence.</returns>
146+
/// <exception cref="T:ArgumentNullException">Thrown when the input sequence is null.</exception>
147+
/// <exception cref="T:ArgumentException">Thrown when the input sequence is empty.</exception>
148+
static member minByAsync: projection: ('T -> #Task<'U>) -> source: TaskSeq<'T> -> Task<'T> when 'U: comparison
149+
74150
/// <summary>
75151
/// Returns a task sequence that is given by the delayed specification of a task sequence.
76152
/// </summary>

src/FSharp.Control.TaskSeq/TaskSeqInternal.fs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ module internal TaskSeqInternal =
163163
checkNonNull (nameof source) source
164164

165165
task {
166-
167166
use e = source.GetAsyncEnumerator CancellationToken.None
168167
let mutable go = true
169168
let mutable i = 0
@@ -178,6 +177,76 @@ module internal TaskSeqInternal =
178177
return i
179178
}
180179

180+
let inline maxMin ([<InlineIfLambda>] maxOrMin) (source: TaskSeq<_>) =
181+
checkNonNull (nameof source) source
182+
183+
task {
184+
use e = source.GetAsyncEnumerator CancellationToken.None
185+
let! nonEmpty = e.MoveNextAsync()
186+
187+
if not nonEmpty then
188+
raiseEmptySeq ()
189+
190+
let mutable acc = e.Current
191+
192+
while! e.MoveNextAsync() do
193+
acc <- maxOrMin e.Current acc
194+
195+
return acc
196+
}
197+
198+
let inline maxMinBy ([<InlineIfLambda>] compare) ([<InlineIfLambda>] projection) (source: TaskSeq<_>) =
199+
checkNonNull (nameof source) source
200+
201+
task {
202+
use e = source.GetAsyncEnumerator CancellationToken.None
203+
let! nonEmpty = e.MoveNextAsync()
204+
205+
if not nonEmpty then
206+
raiseEmptySeq ()
207+
208+
let value = e.Current
209+
let mutable accProjection = projection value
210+
let mutable accValue = value
211+
212+
while! e.MoveNextAsync() do
213+
let value = e.Current
214+
let currentProjection = projection value
215+
216+
if compare currentProjection accProjection then
217+
accProjection <- currentProjection
218+
accValue <- value
219+
220+
return accValue
221+
}
222+
223+
224+
let inline maxMinByAsync ([<InlineIfLambda>] compare) ([<InlineIfLambda>] projectionAsync) (source: TaskSeq<_>) =
225+
checkNonNull (nameof source) source
226+
227+
task {
228+
use e = source.GetAsyncEnumerator CancellationToken.None
229+
let! nonEmpty = e.MoveNextAsync()
230+
231+
if not nonEmpty then
232+
raiseEmptySeq ()
233+
234+
let value = e.Current
235+
let! projValue = projectionAsync value
236+
let mutable accProjection = projValue
237+
let mutable accValue = value
238+
239+
while! e.MoveNextAsync() do
240+
let value = e.Current
241+
let! currentProjection = projectionAsync value
242+
243+
if compare currentProjection accProjection then
244+
accProjection <- currentProjection
245+
accValue <- value
246+
247+
return accValue
248+
}
249+
181250
let tryExactlyOne (source: TaskSeq<_>) =
182251
checkNonNull (nameof source) source
183252

0 commit comments

Comments
 (0)