Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add List.chunkBy #561

Merged
merged 2 commits into from
Nov 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/FSharpPlus/Control/Collection.fs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,11 @@ type GroupBy =
type ChunkBy =
static member ChunkBy (x: Id<'T> , f: 'T->'Key, _: Id<'Key*Id<'T>> , [<Optional>]_impl: ChunkBy) = let a = Id.run x in Id.create (f a, x)
static member ChunkBy (x: seq<'T> , f: 'T->'Key, _: seq<'Key*seq<'T>> , [<Optional>]_impl: ChunkBy) = Seq.chunkBy f x |> Seq.map (fun (x,y) -> x, y :> _ seq)
static member ChunkBy (x: list<'T>, f: 'T->'Key, _: list<'Key*list<'T>>, [<Optional>]_impl: ChunkBy) = Seq.chunkBy f x |> Seq.map (fun (x,y) -> x, Seq.toList y) |> Seq.toList
static member ChunkBy (x: list<'T>, f: 'T->'Key, _: list<'Key*list<'T>>, [<Optional>]_impl: ChunkBy) =
#if TEST_TRACE
Traces.add "ChunkBy, list<'T>"
#endif
List.chunkBy f x
static member ChunkBy (x: 'T [] , f: 'T->'Key, _: ('Key*('T [])) [] , [<Optional>]_impl: ChunkBy) = Seq.chunkBy f x |> Seq.map (fun (x,y) -> x, Seq.toArray y) |> Seq.toArray

static member inline Invoke (projection: 'T->'Key) (source: '``Collection<'T>``) : '``Collection<'Key * 'Collection<'T>>`` =
Expand Down
42 changes: 42 additions & 0 deletions src/FSharpPlus/Extensions/List.fs
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,48 @@ module List =
loop (ls,rs)
loop (list1, list2)
#endif

/// <summary>
/// Chunks the list up into groups with the same projected key by applying
/// the key-generating projection function to each element and yielding a list of
/// keys tupled with values.
/// </summary>
///
/// <remarks>
/// Each key is tupled with an array of all adjacent elements that match
/// to the key, therefore keys are not unique but can't be adjacent
/// as each time the key changes a new group is yield.
///
/// The ordering of the original list is respected.
/// </remarks>
///
/// <param name="projection">A function that transforms an element of the list into a comparable key.</param>
/// <param name="source">The input list.</param>
///
/// <returns>The resulting list of keys tupled with a list of matching values</returns>
let chunkBy (projection: 'T -> 'Key) (source: _ list) =
#if FABLE_COMPILER
Seq.chunkBy projection source |> Seq.map (fun (x, y) -> x, Seq.toList y) |> Seq.toList
#else
match source with
| [] -> []
| x::xs ->
let mutable acc = new ListCollector<_> ()
let mutable members = new ListCollector<_> ()
let rec loop source g =
match source with
| [] -> acc.Add (g, members.Close ())
| x::xs ->
let key = projection x
if g <> key then
acc.Add (g, members.Close ())
members <- new ListCollector<_> ()
members.Add x
loop xs key
members.Add x
loop xs (projection x)
acc.Close ()
#endif

/// <summary>Same as choose but with access to the index.</summary>
/// <param name="mapping">The mapping function, taking index and element as parameters.</param>
Expand Down
24 changes: 24 additions & 0 deletions tests/FSharpPlus.Tests/Collections.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace FSharpPlus.Tests

open FSharpPlus
open FSharpPlus.Data
open NUnit.Framework

#if TEST_TRACE
open FSharpPlus.Internals
#endif

module Collections =

[<Test>]
let chunkBy () =
#if TEST_TRACE
Traces.reset()
#endif
let source = [1; 2; 3; 5; 7; 9]
let expected = [(1, [1]); (0, [2]); (1, [3; 5; 7; 9])]
let actual = chunkBy (flip (%) 2) source
CollectionAssert.AreEqual(expected, actual)
#if TEST_TRACE
CollectionAssert.AreEqual (["ChunkBy, list<'T>"], Traces.get())
#endif
1 change: 1 addition & 0 deletions tests/FSharpPlus.Tests/FSharpPlus.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<Compile Include="Parsing.fs" />
<Compile Include="Traversals.fs" />
<Compile Include="Indexables.fs" />
<Compile Include="Collections.fs" />
<Compile Include="Validations.fs" />
<Compile Include="Task.fs" />
<Compile Include="ValueTask.fs" />
Expand Down
2 changes: 1 addition & 1 deletion tests/FSharpPlus.Tests/General.fs
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ module Functor =
let nel = zip (NonEmptyList.ofList [1; 2]) (NonEmptyList.ofList ["a"; "b"; "c"])
CollectionAssert.AreEqual (NonEmptyList.ofList [1,"a"; 2,"b"], nel)

module Collections =
module Collections2 =

open System.Collections.Concurrent

Expand Down
Loading