diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 0c575f0..73d8504 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,10 +3,11 @@ "isRoot": true, "tools": { "fable": { - "version": "4.0.6", + "version": "4.24.0", "commands": [ "fable" - ] + ], + "rollForward": false } } } \ No newline at end of file diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 6f1dedc..5cd8ac3 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -12,15 +12,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.x.x + dotnet-version: 8.x.x - name: Restore fable run: dotnet tool restore - name: Setup Node.js environment - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 16 - name: install node modules @@ -36,15 +36,15 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.x.x + dotnet-version: 8.x.x - name: Restore fable run: dotnet tool restore - name: Setup Node.js environment - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 16 - name: install node modules diff --git a/FSharpAux.sln b/FSharpAux.sln index 93d437f..52d3934 100644 --- a/FSharpAux.sln +++ b/FSharpAux.sln @@ -21,6 +21,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".build", ".build", "{7C6D08 ProjectSection(SolutionItems) = preProject build.cmd = build.cmd build.sh = build.sh + .config\dotnet-tools.json = .config\dotnet-tools.json + global.json = global.json package.json = package.json EndProjectSection EndProject diff --git a/README.md b/README.md index 5d5fa4d..0460bae 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ end ### Requirements -- .Net 6.0 +- .Net 8.0 - node.js ~16 (higher might work) [only for fable testing] - npm ~8 (higher might work) [only for fable testing] diff --git a/build.cmd b/build.cmd index 97d967d..8d82883 100644 --- a/build.cmd +++ b/build.cmd @@ -1,4 +1,3 @@ @echo off -cls dotnet run --project ./build/build.fsproj %* \ No newline at end of file diff --git a/build/BasicTasks.fs b/build/BasicTasks.fs index d7f28ad..768dcd8 100644 --- a/build/BasicTasks.fs +++ b/build/BasicTasks.fs @@ -26,5 +26,19 @@ let clean = BuildTask.create "Clean" [] { let build = BuildTask.create "Build" [clean] { solutionFile - |> DotNet.build id + |> DotNet.build (fun p -> + let msBuildParams = + {p.MSBuildParams with + Properties = ([ + "warnon", "3390" + ]) + DisableInternalBinLog = true + } + { + p with + MSBuildParams = msBuildParams + + } + |> DotNet.Options.withCustomParams (Some "-tl") + ) } \ No newline at end of file diff --git a/build/PackageTasks.fs b/build/PackageTasks.fs index d51d99a..29e9ec9 100644 --- a/build/PackageTasks.fs +++ b/build/PackageTasks.fs @@ -8,6 +8,7 @@ open TestTasks open BlackFox.Fake open Fake.Core +open Fake.DotNet open Fake.IO.Globbing.Operators let pack = BuildTask.create "Pack" [clean; build; runTests] { @@ -22,12 +23,14 @@ let pack = BuildTask.create "Pack" [clean; build; runTests] { "Version",stableVersionTag "PackageReleaseNotes", (release.Notes |> String.concat "\r\n") ] @ p.MSBuildParams.Properties) + DisableInternalBinLog = true } { p with MSBuildParams = msBuildParams OutputPath = Some pkgDir } + |> DotNet.Options.withCustomParams (Some "--no-dependencies -tl") )) else failwith "aborted" } @@ -44,6 +47,7 @@ let packPrerelease = BuildTask.create "PackPrerelease" [setPrereleaseTag; clean; "Version", prereleaseTag "PackageReleaseNotes", (release.Notes |> String.toLines ) ] @ p.MSBuildParams.Properties) + DisableInternalBinLog = true } { p with @@ -51,6 +55,7 @@ let packPrerelease = BuildTask.create "PackPrerelease" [setPrereleaseTag; clean; OutputPath = Some pkgDir MSBuildParams = msBuildParams } + |> DotNet.Options.withCustomParams (Some "--no-dependencies -tl") )) else failwith "aborted" diff --git a/build/TestTasks.fs b/build/TestTasks.fs index f0732c2..1dcd93d 100644 --- a/build/TestTasks.fs +++ b/build/TestTasks.fs @@ -131,6 +131,7 @@ module RunTests = Logger = Some "console;verbosity=detailed" Configuration = DotNet.BuildConfiguration.fromString configuration NoBuild = true + MSBuildParams = { testParams.MSBuildParams with DisableInternalBinLog = true } } ) testProject ) diff --git a/build/build.fsproj b/build/build.fsproj index 43ead2f..9e203de 100644 --- a/build/build.fsproj +++ b/build/build.fsproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 Exe @@ -18,15 +18,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/global.json b/global.json index 74536f9..b2ede13 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.100", + "version": "8.0.100", "rollForward": "latestMinor" } } \ No newline at end of file diff --git a/src/FSharpAux.Core/Seq.fs b/src/FSharpAux.Core/Seq.fs index a7c2466..bd4e1cf 100644 --- a/src/FSharpAux.Core/Seq.fs +++ b/src/FSharpAux.Core/Seq.fs @@ -110,6 +110,40 @@ module Seq = hsSs.IntersectWith largerSeq hsSs + /// + /// Divides the input sequence into chunks by a key projection function. + /// + /// A new chunk is created each time the projection function returns a new value, and the resulting chunks are tupled with their keys. + /// + /// Example: Seq.chunkBy isOdd [3;3;2;4;1;2] = seq [(true, [3; 3]); (false, [2; 4]); (true, [1]); (false, [2])] + /// + /// The function to determine if an element creates a new chunk + /// + let chunkBy (projection: 'T -> 'Key) (source: seq<'T>) = + seq { + use e = source.GetEnumerator () + if e.MoveNext () then + // the key of the current chunk + let mutable chunkKey = projection e.Current + // the members of the current chunk + let mutable members = ResizeArray () + members.Add e.Current + + while e.MoveNext () do + let currentKey = projection e.Current + if chunkKey = currentKey then + // add item to current chunk if projection returns the chunk key again + members.Add e.Current + else + // yield current chunk and start a new one if projection returns a new key + yield chunkKey, members |> Seq.cast<'T> + chunkKey <- currentKey + members <- ResizeArray () + members.Add e.Current + + yield chunkKey, members |> Seq.cast<'T> + } + // // Without continuation passing // let groupWhen f (input:seq<_>) = seq { diff --git a/tests/FSharpAux.Core.Tests/FSharpAux.Core.Tests.fsproj b/tests/FSharpAux.Core.Tests/FSharpAux.Core.Tests.fsproj index d75fea1..aa9bc91 100644 --- a/tests/FSharpAux.Core.Tests/FSharpAux.Core.Tests.fsproj +++ b/tests/FSharpAux.Core.Tests/FSharpAux.Core.Tests.fsproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 false @@ -17,15 +17,15 @@ - - + + - - - - + + + + diff --git a/tests/FSharpAux.Core.Tests/SeqTests.fs b/tests/FSharpAux.Core.Tests/SeqTests.fs index ec3e4ec..8155a43 100644 --- a/tests/FSharpAux.Core.Tests/SeqTests.fs +++ b/tests/FSharpAux.Core.Tests/SeqTests.fs @@ -13,6 +13,7 @@ let private testSeq3 = seq {3; 3; 2; 4; 1; 1} let private testSeq4 = seq {3; 3; 2; 4; 2; 2} let private testSeq5 = seq {3; 3; 2; 4; 2; 1} let private testSeq6 = seq {6; 6; 2; 4; 2; 8} +let private testSeq7 = seq {"2"; "A"; "A"; "B"; "B"; "C"; "D"} let private testSeq2_groupWhen_Equal = seq {seq {3}; seq {3; 2; 4}; seq {1; 2}} let private testSeq2_groupWhen_NotEqual = seq {seq {3}; seq {3; 2; 4}; seq {1}; seq {2}} @@ -23,45 +24,61 @@ let private testSeq4_groupWhen_NotEqual = seq {seq {3}; seq {3; 2; 4; 2} let private testSeq5_groupWhen_Equal = seq {seq {3}; seq {3; 2; 4; 2}; seq {1}} let private testSeq5_groupWhen_NotEqual = seq {seq {3}; seq {3; 2; 4; 2; 1}} +let private testSeq2_chunkByIsOdd = seq {(true, seq {3; 3}); (false, seq {2; 4}); (true, seq {1}); (false, seq {2})} +let private testSeq6_chunkByIsOdd = seq {(false, seq {6; 6; 2; 4; 2; 8})} +let private testSeq7_chunkById = seq {("2", seq {"2"}); ("A", seq {"A"; "A"}); ("B", seq {"B"; "B"}); ("C", seq {"C"}); ("D", seq {"D"})} + // helper functions let private list s = Seq.toList s let private list2 s = Seq.map (Seq.toList) s |> Seq.toList +let private list2Tpl (s: seq<'Key*seq<'Items>>) = Seq.map (fun (k, i) -> k, i |> Seq.toList) s |> Seq.toList +let isOdd = fun n -> n % 2 <> 0 let seqTests = testList "SeqTests" [ - let isOdd = fun n -> n % 2 <> 0 testList "Seq.groupWhen" [ testCase "returns correct jagged list, case1: [3; 3; 2; 4; 1; 2]" (fun _ -> - Expect.equal (testSeq2 |> Seq.groupWhen isOdd |> list2) (testSeq2_groupWhen_Equal |> list2) "Seq.groupWhen did return correct jagged list" + Expect.equal (testSeq2 |> Seq.groupWhen isOdd |> list2) (testSeq2_groupWhen_Equal |> list2) "Seq.groupWhen did return incorrect jagged list" ) testCase "does not return incorrect jagged list, case1: [3; 3; 2; 4; 1; 2]" (fun _ -> - Expect.notEqual (testSeq2 |> Seq.groupWhen isOdd |> list2) (testSeq2_groupWhen_NotEqual |> list2) "Seq.groupWhen did not return incorrect jagged list" + Expect.notEqual (testSeq2 |> Seq.groupWhen isOdd |> list2) (testSeq2_groupWhen_NotEqual |> list2) "Seq.groupWhen did return incorrect jagged list" ) testCase "returns correct jagged list, case2: [3; 3; 2; 4; 1; 1]" (fun _ -> - Expect.equal (testSeq3 |> Seq.groupWhen isOdd |> list2) (testSeq3_groupWhen_Equal |> list2) "Seq.groupWhen did return correct jagged list" + Expect.equal (testSeq3 |> Seq.groupWhen isOdd |> list2) (testSeq3_groupWhen_Equal |> list2) "Seq.groupWhen did return incorrect jagged list" ) testCase "does not return incorrect jagged list, case2: [3; 3; 2; 4; 1; 1]" (fun _ -> - Expect.notEqual (testSeq3 |> Seq.groupWhen isOdd |> list2) (testSeq3_groupWhen_NotEqual |> list2) "Seq.groupWhen did not return incorrect jagged list" + Expect.notEqual (testSeq3 |> Seq.groupWhen isOdd |> list2) (testSeq3_groupWhen_NotEqual |> list2) "Seq.groupWhen did return incorrect jagged list" ) testCase "returns correct jagged list, case3: [3; 3; 2; 4; 2; 2]" (fun _ -> - Expect.equal (testSeq4 |> Seq.groupWhen isOdd |> list2) (testSeq4_groupWhen_Equal |> list2) "Seq.groupWhen did return correct jagged list" + Expect.equal (testSeq4 |> Seq.groupWhen isOdd |> list2) (testSeq4_groupWhen_Equal |> list2) "Seq.groupWhen did return incorrect jagged list" ) testCase "does not return incorrect jagged list, case3: [3; 3; 2; 4; 2; 2]" (fun _ -> - Expect.notEqual (testSeq4 |> Seq.groupWhen isOdd |> list2) (testSeq4_groupWhen_NotEqual |> list2) "Seq.groupWhen did not return incorrect jagged list" + Expect.notEqual (testSeq4 |> Seq.groupWhen isOdd |> list2) (testSeq4_groupWhen_NotEqual |> list2) "Seq.groupWhen did return incorrect jagged list" ) testCase "returns correct jagged list, case4: [3; 3; 2; 4; 2; 1]" (fun _ -> - Expect.equal (testSeq5 |> Seq.groupWhen isOdd |> list2) (testSeq5_groupWhen_Equal |> list2) "Seq.groupWhen did return correct jagged list" + Expect.equal (testSeq5 |> Seq.groupWhen isOdd |> list2) (testSeq5_groupWhen_Equal |> list2) "Seq.groupWhen did return incorrect jagged list" ) testCase "does not return incorrect jagged list, case4: [3; 3; 2; 4; 2; 1]" (fun _ -> - Expect.notEqual (testSeq5 |> Seq.groupWhen isOdd |> list2) (testSeq5_groupWhen_NotEqual |> list2) "Seq.groupWhen did not return incorrect jagged list" + Expect.notEqual (testSeq5 |> Seq.groupWhen isOdd |> list2) (testSeq5_groupWhen_NotEqual |> list2) "Seq.groupWhen did return incorrect jagged list" ) testCase "returns correct jagged list, case4: [6; 6; 2; 4; 2; 8]" (fun _ -> - Expect.equal (testSeq6 |> Seq.groupWhen isOdd |> list2) ([testSeq6 |> list]) "Seq.groupWhen did return correct jagged list" + Expect.equal (testSeq6 |> Seq.groupWhen isOdd |> list2) ([testSeq6 |> list]) "Seq.groupWhen did return incorrect jagged list" ) testCase "does not return incorrect jagged list, case4: [6; 6; 2; 4; 2; 8]" (fun _ -> Expect.notEqual (testSeq6 |> Seq.groupWhen isOdd |> list2) ([]) "Seq.groupWhen did not return empty (jagged) list" ) ] + testList "Seq.chunkBy" [ + test "chunk integers by odd/even" { + Expect.equal (testSeq2 |> Seq.chunkBy isOdd |> list2Tpl) (testSeq2_chunkByIsOdd |> list2Tpl) "Seq.chunkBy did return incorrect jagged list" + } + test "chunk integers by odd/even only one chunk" { + Expect.equal (testSeq6 |> Seq.chunkBy isOdd |> list2Tpl) (testSeq6_chunkByIsOdd |> list2Tpl) "Seq.chunkBy did return incorrect jagged list" + } + test "chunk strings by id" { + Expect.equal (testSeq7 |> Seq.chunkBy id |> list2Tpl) (testSeq7_chunkById |> list2Tpl) "Seq.chunkBy did return incorrect jagged list" + } + ] testList "Seq.intersect" [ testCase "returns correct list, case1: []" (fun _ -> Expect.equal (Seq.intersect Seq.empty Seq.empty |> list) [] "Seq.intersect did return correct list" diff --git a/tests/FSharpAux.IO.Tests/FSharpAux.IO.Tests.fsproj b/tests/FSharpAux.IO.Tests/FSharpAux.IO.Tests.fsproj index 1362dc2..058c43a 100644 --- a/tests/FSharpAux.IO.Tests/FSharpAux.IO.Tests.fsproj +++ b/tests/FSharpAux.IO.Tests/FSharpAux.IO.Tests.fsproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 false @@ -13,9 +13,9 @@ - - - + + + diff --git a/tests/FSharpAux.Tests/FSharpAux.Tests.fsproj b/tests/FSharpAux.Tests/FSharpAux.Tests.fsproj index 6c2773b..ba62721 100644 --- a/tests/FSharpAux.Tests/FSharpAux.Tests.fsproj +++ b/tests/FSharpAux.Tests/FSharpAux.Tests.fsproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 false @@ -17,11 +17,11 @@ - - - + + + - +