Skip to content

[Repo Assist] feat: add TaskSeq.groupBy, countBy, and partition (195 tests)#307

Merged
dsyme merged 5 commits intomainfrom
repo-assist/feat-groupby-countby-partition-2026-03-7606207c355db1fa
Mar 8, 2026
Merged

[Repo Assist] feat: add TaskSeq.groupBy, countBy, and partition (195 tests)#307
dsyme merged 5 commits intomainfrom
repo-assist/feat-groupby-countby-partition-2026-03-7606207c355db1fa

Conversation

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented Mar 7, 2026

🤖 This PR was created by Repo Assist, an automated AI assistant.

Summary

Adds six new combinators to TaskSeq, completing a common set of aggregation and splitting operations that mirror List, Array, and Seq equivalents:

Function Description Return type
TaskSeq.groupBy Groups elements by sync key projection Task<('Key * 'T[])[]>
TaskSeq.groupByAsync Groups elements by async key projection Task<('Key * 'T[])[]>
TaskSeq.countBy Counts elements per key Task<('Key * int)[]>
TaskSeq.countByAsync Counts elements per key (async) Task<('Key * int)[]>
TaskSeq.partition Splits by predicate into two arrays Task<'T[] * 'T[]>
TaskSeq.partitionAsync Splits by async predicate into two arrays Task<'T[] * 'T[]>

Design notes

  • groupBy / countBy return Task<...> (not TaskSeq<...>) because they must consume the entire sequence before forming groups — the same reason List.groupBy returns a list rather than a lazy sequence.
  • Both return results in first-occurrence key order, matching Seq.groupBy / Seq.countBy behaviour.
  • Keys use HashIdentity.Structural for structural equality, consistent with except/distinctBy.
  • A new [(Struct)] ProjectorAction DU was added alongside the existing FolderAction, PredicateAction, etc. to share one internal implementation for sync/async projections.
  • partition reuses the existing PredicateAction DU.

Examples

// Group integers by parity
let! groups = taskSeq { 1..10 } |> TaskSeq.groupBy (fun x -> x % 2 = 0)
// [| (false, [|1;3;5;7;9|]); (true, [|2;4;6;8;10|]) |]

// Count words by first character
let! counts = words |> TaskSeq.countByAsync (fun w -> task { return w[0] })

// Split items
let! evens, odds = taskSeq { 1..10 } |> TaskSeq.partition (fun x -> x % 2 = 0)
// evens = [|2;4;6;8;10|], odds = [|1;3;5;7;9|]

Files changed

  • src/FSharp.Control.TaskSeq/TaskSeqInternal.fsProjectorAction DU + groupBy, countBy, partition implementations
  • src/FSharp.Control.TaskSeq/TaskSeq.fs — 6 new static members
  • src/FSharp.Control.TaskSeq/TaskSeq.fsi — full XML doc signatures with when 'Key: equality constraints
  • src/FSharp.Control.TaskSeq.Test/TaskSeq.GroupBy.Tests.fs — 195 tests (null checks, empty, immutable, side-effect variants for all 6 functions)
  • src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj — adds new test file
  • release-notes.txt — updated for 0.5.0

Test Status

Build succeeded — 0 warnings, 0 errors
All 4045 existing tests pass (release configuration)
195 new GroupBy/CountBy/Partition tests pass
Formatting clean (dotnet fantomas . --check passes)

ref #289

Generated by Repo Assist ·

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@ec7d342403c9912c87320110f8822a8fbb817a0c

- TaskSeq.groupBy / groupByAsync: groups elements by key, returns Task<('Key * 'T[])[]>
- TaskSeq.countBy / countByAsync: counts per key, returns Task<('Key * int)[]>
- TaskSeq.partition / partitionAsync: splits into two arrays by predicate, returns Task<'T[] * 'T[]>
- Adds ProjectorAction struct DU for sync/async projection
- 195 tests covering empty, immutable, and side-effect variants
- ref #289

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dsyme dsyme marked this pull request as ready for review March 8, 2026 00:10
@dsyme dsyme merged commit 329fd86 into main Mar 8, 2026
4 checks passed
@dsyme dsyme deleted the repo-assist/feat-groupby-countby-partition-2026-03-7606207c355db1fa branch March 8, 2026 02:03
github-actions bot added a commit that referenced this pull request Mar 8, 2026
Mark as implemented (✅) following the wave of PRs merged for v0.6.0:
- TaskSeq.average, averageBy, averageByAsync (#304)
- TaskSeq.sum, sumBy, sumByAsync (#304)
- TaskSeq.distinct, distinctBy, distinctByAsync, distinctUntilChanged (#305)
- TaskSeq.mapFold, mapFoldAsync (#306)
- TaskSeq.groupBy, groupByAsync (#307)
- TaskSeq.countBy, countByAsync — add missing table row (#307)
- TaskSeq.partition, partitionAsync — add missing table row (#307)

Also:
- Fix typo 'dictinctBy' → 'distinctBy'
- Update 'Status & planning' checklist to reflect completed work
- Add PR link definitions (#304#307) at the bottom

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant