Skip to content

Commit 679013f

Browse files
committed
Add distinctUntilChanged and tests
1 parent d2713a1 commit 679013f

File tree

5 files changed

+71
-0
lines changed

5 files changed

+71
-0
lines changed

src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<Compile Include="TaskSeq.Empty.Tests.fs" />
2020
<Compile Include="TaskSeq.ExactlyOne.Tests.fs" />
2121
<Compile Include="TaskSeq.Except.Tests.fs" />
22+
<Compile Include="TaskSeq.DistinctUntilChanged.Tests.fs" />
2223
<Compile Include="TaskSeq.Exists.Tests.fs" />
2324
<Compile Include="TaskSeq.Filter.Tests.fs" />
2425
<Compile Include="TaskSeq.FindIndex.Tests.fs" />
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module TaskSeq.Tests.DistinctUntilChanged
2+
3+
open Xunit
4+
open FsUnit.Xunit
5+
6+
open FSharp.Control
7+
8+
//
9+
// TaskSeq.distinctUntilChanged
10+
//
11+
12+
13+
module EmptySeq =
14+
[<Fact>]
15+
let ``TaskSeq-distinctUntilChanged with null source raises`` () = assertNullArg <| fun () -> TaskSeq.distinctUntilChanged null
16+
17+
[<Theory; ClassData(typeof<TestEmptyVariants>)>]
18+
let ``TaskSeq-distinctUntilChanged has no effect`` variant = task {
19+
do!
20+
Gen.getEmptyVariant variant
21+
|> TaskSeq.distinctUntilChanged
22+
|> TaskSeq.toListAsync
23+
|> Task.map (List.isEmpty >> should be True)
24+
}
25+
26+
module Functionality =
27+
[<Fact>]
28+
let ``TaskSeq-distinctUntilChanged should return no consecutive duplicates`` () = task {
29+
let ts =
30+
[ 'A'; 'A'; 'B'; 'Z'; 'C'; 'C'; 'Z'; 'C'; 'D'; 'D'; 'D'; 'Z' ]
31+
|> TaskSeq.ofList
32+
33+
let! xs = ts |> TaskSeq.distinctUntilChanged |> TaskSeq.toListAsync
34+
35+
xs
36+
|> List.map string
37+
|> String.concat ""
38+
|> should equal "ABZCZCDZ"
39+
}

src/FSharp.Control.TaskSeq/TaskSeq.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,8 @@ type TaskSeq private () =
358358
static member except itemsToExclude source = Internal.except itemsToExclude source
359359
static member exceptOfSeq itemsToExclude source = Internal.exceptOfSeq itemsToExclude source
360360

361+
static member distinctUntilChanged source = Internal.distinctUntilChanged source
362+
361363
static member forall predicate source = Internal.forall (Predicate predicate) source
362364
static member forallAsync predicate source = Internal.forall (PredicateAsync predicate) source
363365

src/FSharp.Control.TaskSeq/TaskSeq.fsi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,16 @@ type TaskSeq =
12971297
/// <exception cref="T:ArgumentNullException">Thrown when either of the two input task sequences is null.</exception>
12981298
static member exceptOfSeq<'T when 'T: equality> : itemsToExclude: seq<'T> -> source: TaskSeq<'T> -> TaskSeq<'T>
12991299

1300+
/// <summary>
1301+
/// Returns a new task sequence without consecutive duplicate elements.
1302+
/// </summary>
1303+
///
1304+
/// <param name="source">The input task sequence whose consecutive duplicates will be removed.</param>
1305+
/// <returns>A sequence without consecutive duplicates elements.</returns>
1306+
///
1307+
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequences is null.</exception>
1308+
static member distinctUntilChanged<'T when 'T: equality> : source: TaskSeq<'T> -> TaskSeq<'T>
1309+
13001310
/// <summary>
13011311
/// Combines the two task sequences into a new task sequence of pairs. The two sequences need not have equal lengths:
13021312
/// when one sequence is exhausted any remaining elements in the other sequence are ignored.

src/FSharp.Control.TaskSeq/TaskSeqInternal.fs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,3 +1097,22 @@ module internal TaskSeqInternal =
10971097
go <- step
10981098

10991099
}
1100+
1101+
let distinctUntilChanged (source: TaskSeq<_>) =
1102+
checkNonNull (nameof source) source
1103+
1104+
taskSeq {
1105+
let mutable maybePrevious = ValueNone
1106+
1107+
for current in source do
1108+
match maybePrevious with
1109+
| ValueNone ->
1110+
yield current
1111+
maybePrevious <- ValueSome current
1112+
| ValueSome previous ->
1113+
if previous = current then
1114+
() // skip
1115+
else
1116+
yield current
1117+
maybePrevious <- ValueSome current
1118+
}

0 commit comments

Comments
 (0)