diff --git a/PolyShim.Tests/Net60/EnumerableExtensionsTests.cs b/PolyShim.Tests/Net60/EnumerableExtensionsTests.cs index 54608d9..a0075cd 100644 --- a/PolyShim.Tests/Net60/EnumerableExtensionsTests.cs +++ b/PolyShim.Tests/Net60/EnumerableExtensionsTests.cs @@ -8,6 +8,52 @@ namespace PolyShim.Tests.Net60; public class EnumerableExtensionsTests { + [Fact] + public void Chunk_Test() + { + // Arrange + var source = Enumerable.Range(1, 10); + + // Act & assert + source + .Chunk(3) + .Should() + .BeEquivalentTo([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10], + ]); + } + + [Fact] + public void DistinctBy_Test() + { + // Arrange + var source = new[] + { + new KeyValuePair("Foo", 42), + new KeyValuePair("Bar", 13), + new KeyValuePair("Foo", 39), + new KeyValuePair("Qux", 17), + new KeyValuePair("Bar", 15), + new KeyValuePair("Baz", 69), + new KeyValuePair("Qux", 11), + new KeyValuePair("Baz", 54), + }; + + // Act & assert + source + .DistinctBy(x => x.Key) + .Should() + .Equal( + new KeyValuePair("Foo", 42), + new KeyValuePair("Bar", 13), + new KeyValuePair("Qux", 17), + new KeyValuePair("Baz", 69) + ); + } + [Fact] public void ElementAt_Test() { @@ -32,6 +78,30 @@ public void ElementAtOrDefault_Test() source.ElementAtOrDefault(^10).Should().Be(0); } + [Fact] + public void ExceptBy_Test() + { + // Arrange + var source = new[] + { + new KeyValuePair("Foo", 42), + new KeyValuePair("Bar", 13), + new KeyValuePair("Baz", 69), + new KeyValuePair("Qux", 17), + }; + + var other = new[] { "Bar", "Qux" }; + + // Act & assert + source + .ExceptBy(other, x => x.Key) + .Should() + .Equal( + new KeyValuePair("Foo", 42), + new KeyValuePair("Baz", 69) + ); + } + [Fact] public void FirstOrDefault_Test() { @@ -55,71 +125,63 @@ public void FirstOrDefault_Predicate_Test() } [Fact] - public void LastOrDefault_Test() - { - // Act & assert - new[] { 1, 2, 3 } - .LastOrDefault(69) - .Should() - .Be(3); - Array.Empty().LastOrDefault(69).Should().Be(69); - } - - [Fact] - public void LastOrDefault_Predicate_Test() + public void IntersectBy_Test() { // Arrange - var source = new[] { 1, 2, 3, 4, 5 }; + var source = new[] + { + new KeyValuePair("Foo", 42), + new KeyValuePair("Bar", 13), + new KeyValuePair("Baz", 69), + new KeyValuePair("Qux", 17), + }; - // Act & assert - source.LastOrDefault(x => x % 2 == 0, 69).Should().Be(4); - source.LastOrDefault(x => x > 5, 69).Should().Be(69); - } + var other = new[] { "Bar", "Qux" }; - [Fact] - public void SingleOrDefault_Test() - { // Act & assert - new[] { 1 } - .SingleOrDefault(69) + source + .IntersectBy(other, x => x.Key) .Should() - .Be(1); - Array.Empty().SingleOrDefault(69).Should().Be(69); + .Equal( + new KeyValuePair("Bar", 13), + new KeyValuePair("Qux", 17) + ); } [Fact] - public void SingleOrDefault_Predicate_Test() + public void LastOrDefault_Test() { - // Arrange - var source = new[] { 1, 2, 3, 4, 5 }; - // Act & assert - source.SingleOrDefault(x => x % 3 == 0, 69).Should().Be(3); - source.SingleOrDefault(x => x > 5, 69).Should().Be(69); + new[] { 1, 2, 3 } + .LastOrDefault(69) + .Should() + .Be(3); + Array.Empty().LastOrDefault(69).Should().Be(69); } [Fact] - public void Take_Test() + public void LastOrDefault_Predicate_Test() { // Arrange var source = new[] { 1, 2, 3, 4, 5 }; // Act & assert - source.Take(2..^1).Should().Equal(3, 4); + source.LastOrDefault(x => x % 2 == 0, 69).Should().Be(4); + source.LastOrDefault(x => x > 5, 69).Should().Be(69); } [Fact] - public void Min_Test() + public void Max_Test() { // Arrange var source = new[] { 1, 2, 3, 4, 5 }; // Act & assert - source.Min(Comparer.Default).Should().Be(1); + source.Max(Comparer.Default).Should().Be(5); } [Fact] - public void MinBy_Test() + public void MaxBy_Test() { // Arrange var source = new[] @@ -131,25 +193,25 @@ public void MinBy_Test() }; // Act - var result = source.MinBy(x => x.Value); + var result = source.MaxBy(x => x.Value); // Assert - result.Key.Should().Be("Bar"); - result.Value.Should().Be(13); + result.Key.Should().Be("Baz"); + result.Value.Should().Be(69); } [Fact] - public void Max_Test() + public void Min_Test() { // Arrange var source = new[] { 1, 2, 3, 4, 5 }; // Act & assert - source.Max(Comparer.Default).Should().Be(5); + source.Min(Comparer.Default).Should().Be(1); } [Fact] - public void MaxBy_Test() + public void MinBy_Test() { // Arrange var source = new[] @@ -161,87 +223,43 @@ public void MaxBy_Test() }; // Act - var result = source.MaxBy(x => x.Value); + var result = source.MinBy(x => x.Value); // Assert - result.Key.Should().Be("Baz"); - result.Value.Should().Be(69); + result.Key.Should().Be("Bar"); + result.Value.Should().Be(13); } [Fact] - public void DistinctBy_Test() + public void SingleOrDefault_Test() { - // Arrange - var source = new[] - { - new KeyValuePair("Foo", 42), - new KeyValuePair("Bar", 13), - new KeyValuePair("Foo", 39), - new KeyValuePair("Qux", 17), - new KeyValuePair("Bar", 15), - new KeyValuePair("Baz", 69), - new KeyValuePair("Qux", 11), - new KeyValuePair("Baz", 54), - }; - // Act & assert - source - .DistinctBy(x => x.Key) + new[] { 1 } + .SingleOrDefault(69) .Should() - .Equal( - new KeyValuePair("Foo", 42), - new KeyValuePair("Bar", 13), - new KeyValuePair("Qux", 17), - new KeyValuePair("Baz", 69) - ); + .Be(1); + Array.Empty().SingleOrDefault(69).Should().Be(69); } [Fact] - public void ExceptBy_Test() + public void SingleOrDefault_Predicate_Test() { // Arrange - var source = new[] - { - new KeyValuePair("Foo", 42), - new KeyValuePair("Bar", 13), - new KeyValuePair("Baz", 69), - new KeyValuePair("Qux", 17), - }; - - var other = new[] { "Bar", "Qux" }; + var source = new[] { 1, 2, 3, 4, 5 }; // Act & assert - source - .ExceptBy(other, x => x.Key) - .Should() - .Equal( - new KeyValuePair("Foo", 42), - new KeyValuePair("Baz", 69) - ); + source.SingleOrDefault(x => x % 3 == 0, 69).Should().Be(3); + source.SingleOrDefault(x => x > 5, 69).Should().Be(69); } [Fact] - public void IntersectBy_Test() + public void Take_Test() { // Arrange - var source = new[] - { - new KeyValuePair("Foo", 42), - new KeyValuePair("Bar", 13), - new KeyValuePair("Baz", 69), - new KeyValuePair("Qux", 17), - }; - - var other = new[] { "Bar", "Qux" }; + var source = new[] { 1, 2, 3, 4, 5 }; // Act & assert - source - .IntersectBy(other, x => x.Key) - .Should() - .Equal( - new KeyValuePair("Bar", 13), - new KeyValuePair("Qux", 17) - ); + source.Take(2..^1).Should().Equal(3, 4); } [Fact] @@ -275,24 +293,6 @@ public void UnionBy_Test() ); } - [Fact] - public void Chunk_Test() - { - // Arrange - var source = Enumerable.Range(1, 10); - - // Act & assert - source - .Chunk(3) - .Should() - .BeEquivalentTo([ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10], - ]); - } - [Fact] public void Zip_ThreeSequences_Test() { diff --git a/PolyShim.Tests/Net80/CollectionExtensionsTests.cs b/PolyShim.Tests/Net80/CollectionExtensionsTests.cs index 418efa5..e659e60 100644 --- a/PolyShim.Tests/Net80/CollectionExtensionsTests.cs +++ b/PolyShim.Tests/Net80/CollectionExtensionsTests.cs @@ -35,20 +35,6 @@ public void AddRange_Empty_Test() list.Should().Equal(1, 2, 3); } - [Fact] - public void InsertRange_Test() - { - // Arrange - var list = new List { 1, 2, 6 }; - var items = (ReadOnlySpan)new[] { 3, 4, 5 }; - - // Act - list.InsertRange(2, items); - - // Assert - list.Should().Equal(1, 2, 3, 4, 5, 6); - } - [Fact] public void CopyTo_Test() { @@ -78,4 +64,18 @@ public void CopyTo_TooSmall_Test() // Assert act.Should().Throw(); } + + [Fact] + public void InsertRange_Test() + { + // Arrange + var list = new List { 1, 2, 6 }; + var items = (ReadOnlySpan)new[] { 3, 4, 5 }; + + // Act + list.InsertRange(2, items); + + // Assert + list.Should().Equal(1, 2, 3, 4, 5, 6); + } } diff --git a/PolyShim.Tests/Net90/EnumerableExtensionsTests.cs b/PolyShim.Tests/Net90/EnumerableExtensionsTests.cs index 5347528..01813a4 100644 --- a/PolyShim.Tests/Net90/EnumerableExtensionsTests.cs +++ b/PolyShim.Tests/Net90/EnumerableExtensionsTests.cs @@ -8,16 +8,26 @@ namespace PolyShim.Tests.Net90; public class EnumerableExtensionsTests { [Fact] - public void Index_Test() + public void AggregateBy_Test() { // Arrange - var source = new[] { 42, 13, 69, 17 }; + var source = new[] { 42, 13, 69, 17, 42, 69, 42, 13, 69, 17 }; // Act - var result = source.Index(); + var result = source.AggregateBy(x => x, 0, (acc, x) => acc + x); // Assert - result.Should().Equal((0, 42), (1, 13), (2, 69), (3, 17)); + result + .Should() + .BeEquivalentTo( + new Dictionary + { + [42] = 126, + [13] = 26, + [69] = 207, + [17] = 34, + } + ); } [Fact] @@ -44,25 +54,15 @@ public void CountBy_Test() } [Fact] - public void AggregateBy_Test() + public void Index_Test() { // Arrange - var source = new[] { 42, 13, 69, 17, 42, 69, 42, 13, 69, 17 }; + var source = new[] { 42, 13, 69, 17 }; // Act - var result = source.AggregateBy(x => x, 0, (acc, x) => acc + x); + var result = source.Index(); // Assert - result - .Should() - .BeEquivalentTo( - new Dictionary - { - [42] = 126, - [13] = 26, - [69] = 207, - [17] = 34, - } - ); + result.Should().Equal((0, 42), (1, 13), (2, 69), (3, 17)); } } diff --git a/PolyShim.Tests/NetCore10/EnumerableExtensionsTests.cs b/PolyShim.Tests/NetCore10/EnumerableExtensionsTests.cs index 7250184..d17ba18 100644 --- a/PolyShim.Tests/NetCore10/EnumerableExtensionsTests.cs +++ b/PolyShim.Tests/NetCore10/EnumerableExtensionsTests.cs @@ -7,22 +7,22 @@ namespace PolyShim.Tests.NetCore10; public class EnumerableExtensionsTests { [Fact] - public void Prepend_Test() + public void Append_Test() { // Arrange var source = new[] { 1, 2, 3 }; // Act & assert - source.Prepend(0).Should().Equal(0, 1, 2, 3); + source.Append(4).Should().Equal(1, 2, 3, 4); } [Fact] - public void Append_Test() + public void Prepend_Test() { // Arrange var source = new[] { 1, 2, 3 }; // Act & assert - source.Append(4).Should().Equal(1, 2, 3, 4); + source.Prepend(0).Should().Equal(0, 1, 2, 3); } } diff --git a/PolyShim.Tests/NetCore20/CollectionExtensionsTests.cs b/PolyShim.Tests/NetCore20/CollectionExtensionsTests.cs index a68c477..01c1ad6 100644 --- a/PolyShim.Tests/NetCore20/CollectionExtensionsTests.cs +++ b/PolyShim.Tests/NetCore20/CollectionExtensionsTests.cs @@ -23,67 +23,67 @@ public void GetValueOrDefault_Test() } [Fact] - public void TryAdd_IDictionary_Test() + public void Remove_IDictionary_Test() { // Arrange var dictionary = (IDictionary)new Dictionary { ["apple"] = 1, ["banana"] = 2 }; // Act - var result = dictionary.TryAdd("cherry", 3); + var result = dictionary.Remove("apple", out var value); // Assert result.Should().BeTrue(); - dictionary.Should().ContainKey("cherry"); - dictionary["cherry"].Should().Be(3); - dictionary.Should().HaveCount(3); + value.Should().Be(1); + dictionary.Should().NotContainKey("apple"); + dictionary.Should().HaveCount(1); } [Fact] - public void TryAdd_IDictionary_Exists_Test() + public void Remove_IDictionary_NonExistent_Test() { // Arrange - var dictionary = - (IDictionary)new Dictionary { ["apple"] = 1, ["banana"] = 2 }; + var dictionary = (IDictionary)new Dictionary { ["apple"] = 1 }; // Act - var result = dictionary.TryAdd("apple", 99); + var result = dictionary.Remove("cherry", out var value); // Assert result.Should().BeFalse(); - dictionary["apple"].Should().Be(1); - dictionary.Should().HaveCount(2); + value.Should().Be(default); + dictionary.Should().HaveCount(1); } [Fact] - public void Remove_IDictionary_Test() + public void TryAdd_IDictionary_Test() { // Arrange var dictionary = (IDictionary)new Dictionary { ["apple"] = 1, ["banana"] = 2 }; // Act - var result = dictionary.Remove("apple", out var value); + var result = dictionary.TryAdd("cherry", 3); // Assert result.Should().BeTrue(); - value.Should().Be(1); - dictionary.Should().NotContainKey("apple"); - dictionary.Should().HaveCount(1); + dictionary.Should().ContainKey("cherry"); + dictionary["cherry"].Should().Be(3); + dictionary.Should().HaveCount(3); } [Fact] - public void Remove_IDictionary_NonExistent_Test() + public void TryAdd_IDictionary_Exists_Test() { // Arrange - var dictionary = (IDictionary)new Dictionary { ["apple"] = 1 }; + var dictionary = + (IDictionary)new Dictionary { ["apple"] = 1, ["banana"] = 2 }; // Act - var result = dictionary.Remove("cherry", out var value); + var result = dictionary.TryAdd("apple", 99); // Assert result.Should().BeFalse(); - value.Should().Be(default); - dictionary.Should().HaveCount(1); + dictionary["apple"].Should().Be(1); + dictionary.Should().HaveCount(2); } } diff --git a/PolyShim.Tests/NetCore20/EnumerableExtensionsTests.cs b/PolyShim.Tests/NetCore20/EnumerableExtensionsTests.cs index 71276ee..fbec49f 100644 --- a/PolyShim.Tests/NetCore20/EnumerableExtensionsTests.cs +++ b/PolyShim.Tests/NetCore20/EnumerableExtensionsTests.cs @@ -7,23 +7,23 @@ namespace PolyShim.Tests.NetCore20; public class EnumerableExtensionsTests { [Fact] - public void TakeLast_Test() + public void SkipLast_Test() { // Arrange var source = new[] { 1, 2, 3, 4, 5 }; // Act & assert - source.TakeLast(3).Should().Equal(3, 4, 5); + source.SkipLast(3).Should().Equal(1, 2); } [Fact] - public void SkipLast_Test() + public void TakeLast_Test() { // Arrange var source = new[] { 1, 2, 3, 4, 5 }; // Act & assert - source.SkipLast(3).Should().Equal(1, 2); + source.TakeLast(3).Should().Equal(3, 4, 5); } [Fact] diff --git a/PolyShim.Tests/NetCore21/MemoryExtensionsTests.cs b/PolyShim.Tests/NetCore21/MemoryExtensionsTests.cs index 9c92d0b..3bf9304 100644 --- a/PolyShim.Tests/NetCore21/MemoryExtensionsTests.cs +++ b/PolyShim.Tests/NetCore21/MemoryExtensionsTests.cs @@ -8,89 +8,89 @@ namespace PolyShim.Tests.NetCore21; public class MemoryExtensionsTests { [Fact] - public void Array_AsSpan_Test() + public void Array_AsMemory_Start_Length_Test() { // Arrange var array = new[] { 1, 2, 3, 4, 5 }; // Act - var span = array.AsSpan(); + var memory = array.AsMemory(1, 3); // Assert - span.ToArray().Should().Equal(1, 2, 3, 4, 5); + memory.ToArray().Should().Equal(2, 3, 4); } [Fact] - public void Array_AsSpan_Start_Test() + public void Array_AsMemory_Start_Test() { // Arrange var array = new[] { 1, 2, 3, 4, 5 }; // Act - var span = array.AsSpan(2); + var memory = array.AsMemory(2); // Assert - span.ToArray().Should().Equal(3, 4, 5); + memory.ToArray().Should().Equal(3, 4, 5); } [Fact] - public void Array_AsSpan_Start_Length_Test() + public void Array_AsMemory_Test() { // Arrange var array = new[] { 1, 2, 3, 4, 5 }; // Act - var span = array.AsSpan(1, 3); + var memory = array.AsMemory(); // Assert - span.ToArray().Should().Equal(2, 3, 4); + memory.ToArray().Should().Equal(1, 2, 3, 4, 5); } [Fact] - public void Array_AsMemory_Test() + public void Array_AsSpan_Start_Length_Test() { // Arrange var array = new[] { 1, 2, 3, 4, 5 }; // Act - var memory = array.AsMemory(); + var span = array.AsSpan(1, 3); // Assert - memory.ToArray().Should().Equal(1, 2, 3, 4, 5); + span.ToArray().Should().Equal(2, 3, 4); } [Fact] - public void Array_AsMemory_Start_Test() + public void Array_AsSpan_Start_Test() { // Arrange var array = new[] { 1, 2, 3, 4, 5 }; // Act - var memory = array.AsMemory(2); + var span = array.AsSpan(2); // Assert - memory.ToArray().Should().Equal(3, 4, 5); + span.ToArray().Should().Equal(3, 4, 5); } [Fact] - public void Array_AsMemory_Start_Length_Test() + public void Array_AsSpan_Test() { // Arrange var array = new[] { 1, 2, 3, 4, 5 }; // Act - var memory = array.AsMemory(1, 3); + var span = array.AsSpan(); // Assert - memory.ToArray().Should().Equal(2, 3, 4); + span.ToArray().Should().Equal(1, 2, 3, 4, 5); } [Fact] - public void Array_CopyTo_Span_Test() + public void Array_CopyTo_Memory_Test() { // Arrange var array = new[] { 1, 2, 3 }; - var destination = (Span)new int[3]; + var destination = (Memory)new int[3]; // Act array.CopyTo(destination); @@ -100,11 +100,11 @@ public void Array_CopyTo_Span_Test() } [Fact] - public void Array_CopyTo_Memory_Test() + public void Array_CopyTo_Span_Test() { // Arrange var array = new[] { 1, 2, 3 }; - var destination = (Memory)new int[3]; + var destination = (Span)new int[3]; // Act array.CopyTo(destination); @@ -127,63 +127,63 @@ public void ArraySegment_AsSpan_Test() } [Fact] - public void String_AsSpan_Test() + public void String_AsMemory_Start_Length_Test() { // Act - var span = "hello".AsSpan(); + var memory = "hello".AsMemory(1, 3); // Assert - span.ToArray().Should().Equal('h', 'e', 'l', 'l', 'o'); + memory.ToArray().Should().Equal('e', 'l', 'l'); } [Fact] - public void String_AsSpan_Start_Test() + public void String_AsMemory_Start_Test() { // Act - var span = "hello".AsSpan(2); + var memory = "hello".AsMemory(2); // Assert - span.ToArray().Should().Equal('l', 'l', 'o'); + memory.ToArray().Should().Equal('l', 'l', 'o'); } [Fact] - public void String_AsSpan_Start_Length_Test() + public void String_AsMemory_Test() { // Act - var span = "hello".AsSpan(1, 3); + var memory = "hello".AsMemory(); // Assert - span.ToArray().Should().Equal('e', 'l', 'l'); + memory.ToArray().Should().Equal('h', 'e', 'l', 'l', 'o'); } [Fact] - public void String_AsMemory_Test() + public void String_AsSpan_Start_Length_Test() { // Act - var memory = "hello".AsMemory(); + var span = "hello".AsSpan(1, 3); // Assert - memory.ToArray().Should().Equal('h', 'e', 'l', 'l', 'o'); + span.ToArray().Should().Equal('e', 'l', 'l'); } [Fact] - public void String_AsMemory_Start_Test() + public void String_AsSpan_Start_Test() { // Act - var memory = "hello".AsMemory(2); + var span = "hello".AsSpan(2); // Assert - memory.ToArray().Should().Equal('l', 'l', 'o'); + span.ToArray().Should().Equal('l', 'l', 'o'); } [Fact] - public void String_AsMemory_Start_Length_Test() + public void String_AsSpan_Test() { // Act - var memory = "hello".AsMemory(1, 3); + var span = "hello".AsSpan(); // Assert - memory.ToArray().Should().Equal('e', 'l', 'l'); + span.ToArray().Should().Equal('h', 'e', 'l', 'l', 'o'); } [Fact] @@ -206,6 +206,19 @@ public void Span_IndexOf_NotFound_Test() span.IndexOf(99).Should().Be(-1); } + [Fact] + public void Span_Reverse_Test() + { + // Arrange + var span = (Span)[1, 2, 3, 4, 5]; + + // Act + span.Reverse(); + + // Assert + span.ToArray().Should().Equal(5, 4, 3, 2, 1); + } + [Fact] public void Span_SequenceEqual_Equal_Test() { @@ -239,19 +252,6 @@ public void Span_SequenceEqual_DifferentLengths_Test() span.SequenceEqual(other).Should().BeFalse(); } - [Fact] - public void Span_Reverse_Test() - { - // Arrange - var span = (Span)[1, 2, 3, 4, 5]; - - // Act - span.Reverse(); - - // Assert - span.ToArray().Should().Equal(5, 4, 3, 2, 1); - } - [Fact] public void ReadOnlySpan_IndexOf_Found_Test() { diff --git a/PolyShim/Net60/EnumerableExtensions.cs b/PolyShim/Net60/EnumerableExtensions.cs index ec7ab62..1930364 100644 --- a/PolyShim/Net60/EnumerableExtensions.cs +++ b/PolyShim/Net60/EnumerableExtensions.cs @@ -14,6 +14,34 @@ internal static class MemberPolyfills_Net60_EnumerableExtensions { extension(IEnumerable source) { + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.chunk + public IEnumerable Chunk(int size) + { + var chunk = new List(size); + foreach (var item in source) + { + chunk.Add(item); + if (chunk.Count == size) + { + yield return chunk.ToArray(); + chunk.Clear(); + } + } + + if (chunk.Count > 0) + yield return chunk.ToArray(); + } + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.distinctby#system-linq-enumerable-distinctby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))) + public IEnumerable DistinctBy( + Func keySelector, + IEqualityComparer? comparer + ) => source.GroupBy(keySelector, comparer).Select(x => x.First()); + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.distinctby#system-linq-enumerable-distinctby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))) + public IEnumerable DistinctBy(Func keySelector) => + source.DistinctBy(keySelector, EqualityComparer.Default); + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.elementat#system-linq-enumerable-elementat-1(system-collections-generic-ienumerable((-0))-system-index) public T ElementAt(Index index) { @@ -56,6 +84,26 @@ source as IReadOnlyCollection ?? } } + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.exceptby#system-linq-enumerable-exceptby-2(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-1))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))) + public IEnumerable ExceptBy( + IEnumerable other, + Func keySelector, + IEqualityComparer? comparer + ) + { + var set = new HashSet(other, comparer); + + foreach (var item in source) + { + if (!set.Contains(keySelector(item))) + yield return item; + } + } + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.exceptby#system-linq-enumerable-exceptby-2(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-1))-system-func((-0-1))) + public IEnumerable ExceptBy(IEnumerable other, Func keySelector) => + source.ExceptBy(other, keySelector, EqualityComparer.Default); + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.firstordefault#system-linq-enumerable-firstordefault-1(system-collections-generic-ienumerable((-0))-0) public T FirstOrDefault(T defaultValue) { @@ -77,6 +125,28 @@ public T FirstOrDefault(Func predicate, T defaultValue) return defaultValue; } + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.intersectby#system-linq-enumerable-intersectby-2(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-1))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))) + public IEnumerable IntersectBy( + IEnumerable other, + Func keySelector, + IEqualityComparer? comparer + ) + { + var set = new HashSet(other, comparer); + + foreach (var item in source) + { + if (set.Contains(keySelector(item))) + yield return item; + } + } + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.intersectby#system-linq-enumerable-intersectby-2(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-1))-system-func((-0-1))) + public IEnumerable IntersectBy( + IEnumerable other, + Func keySelector + ) => source.IntersectBy(other, keySelector, EqualityComparer.Default); + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.lastordefault#system-linq-enumerable-lastordefault-1(system-collections-generic-ienumerable((-0))-0) public T LastOrDefault(T defaultValue) { @@ -102,6 +172,29 @@ public T LastOrDefault(Func predicate, T defaultValue) return result; } + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.max#system-linq-enumerable-max-1(system-collections-generic-ienumerable((-0))-system-collections-generic-icomparer((-0))) + public T? Max(IComparer? comparer) => + source.OrderByDescending(x => x, comparer).FirstOrDefault(); + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.maxby#system-linq-enumerable-maxby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-collections-generic-icomparer((-1))) + public T? MaxBy(Func keySelector, IComparer? comparer) => + source.OrderByDescending(keySelector, comparer).FirstOrDefault(); + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.maxby#system-linq-enumerable-maxby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))) + public T? MaxBy(Func keySelector) => + source.MaxBy(keySelector, Comparer.Default); + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.min#system-linq-enumerable-min-1(system-collections-generic-ienumerable((-0))-system-collections-generic-icomparer((-0))) + public T? Min(IComparer? comparer) => source.OrderBy(x => x, comparer).FirstOrDefault(); + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.minby#system-linq-enumerable-minby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-collections-generic-icomparer((-1))) + public T? MinBy(Func keySelector, IComparer? comparer) => + source.OrderBy(keySelector, comparer).FirstOrDefault(); + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.minby#system-linq-enumerable-minby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))) + public T? MinBy(Func keySelector) => + source.MinBy(keySelector, Comparer.Default); + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.singleordefault#system-linq-enumerable-singleordefault-1(system-collections-generic-ienumerable((-0))-0) public T SingleOrDefault(T defaultValue) { @@ -161,81 +254,6 @@ source as IReadOnlyCollection ?? return asCollection.Skip(offset).Take(length); } - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.min#system-linq-enumerable-min-1(system-collections-generic-ienumerable((-0))-system-collections-generic-icomparer((-0))) - public T? Min(IComparer? comparer) => source.OrderBy(x => x, comparer).FirstOrDefault(); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.minby#system-linq-enumerable-minby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-collections-generic-icomparer((-1))) - public T? MinBy(Func keySelector, IComparer? comparer) => - source.OrderBy(keySelector, comparer).FirstOrDefault(); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.minby#system-linq-enumerable-minby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))) - public T? MinBy(Func keySelector) => - source.MinBy(keySelector, Comparer.Default); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.max#system-linq-enumerable-max-1(system-collections-generic-ienumerable((-0))-system-collections-generic-icomparer((-0))) - public T? Max(IComparer? comparer) => - source.OrderByDescending(x => x, comparer).FirstOrDefault(); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.maxby#system-linq-enumerable-maxby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-collections-generic-icomparer((-1))) - public T? MaxBy(Func keySelector, IComparer? comparer) => - source.OrderByDescending(keySelector, comparer).FirstOrDefault(); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.maxby#system-linq-enumerable-maxby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))) - public T? MaxBy(Func keySelector) => - source.MaxBy(keySelector, Comparer.Default); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.distinctby#system-linq-enumerable-distinctby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))) - public IEnumerable DistinctBy( - Func keySelector, - IEqualityComparer? comparer - ) => source.GroupBy(keySelector, comparer).Select(x => x.First()); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.distinctby#system-linq-enumerable-distinctby-2(system-collections-generic-ienumerable((-0))-system-func((-0-1))) - public IEnumerable DistinctBy(Func keySelector) => - source.DistinctBy(keySelector, EqualityComparer.Default); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.exceptby#system-linq-enumerable-exceptby-2(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-1))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))) - public IEnumerable ExceptBy( - IEnumerable other, - Func keySelector, - IEqualityComparer? comparer - ) - { - var set = new HashSet(other, comparer); - - foreach (var item in source) - { - if (!set.Contains(keySelector(item))) - yield return item; - } - } - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.exceptby#system-linq-enumerable-exceptby-2(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-1))-system-func((-0-1))) - public IEnumerable ExceptBy(IEnumerable other, Func keySelector) => - source.ExceptBy(other, keySelector, EqualityComparer.Default); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.intersectby#system-linq-enumerable-intersectby-2(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-1))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))) - public IEnumerable IntersectBy( - IEnumerable other, - Func keySelector, - IEqualityComparer? comparer - ) - { - var set = new HashSet(other, comparer); - - foreach (var item in source) - { - if (set.Contains(keySelector(item))) - yield return item; - } - } - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.intersectby#system-linq-enumerable-intersectby-2(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-1))-system-func((-0-1))) - public IEnumerable IntersectBy( - IEnumerable other, - Func keySelector - ) => source.IntersectBy(other, keySelector, EqualityComparer.Default); - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.unionby#system-linq-enumerable-unionby-2(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-collections-generic-iequalitycomparer((-1))) public IEnumerable UnionBy( IEnumerable other, @@ -262,24 +280,6 @@ public IEnumerable UnionBy( public IEnumerable UnionBy(IEnumerable other, Func keySelector) => source.UnionBy(other, keySelector, EqualityComparer.Default); - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.chunk - public IEnumerable Chunk(int size) - { - var chunk = new List(size); - foreach (var item in source) - { - chunk.Add(item); - if (chunk.Count == size) - { - yield return chunk.ToArray(); - chunk.Clear(); - } - } - - if (chunk.Count > 0) - yield return chunk.ToArray(); - } - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.zip#system-linq-enumerable-zip-3(system-collections-generic-ienumerable((-0))-system-collections-generic-ienumerable((-1))-system-collections-generic-ienumerable((-2))) public IEnumerable<(T, TSecond, TThird)> Zip( IEnumerable second, diff --git a/PolyShim/Net80/CollectionExtensions.cs b/PolyShim/Net80/CollectionExtensions.cs index 60c06fc..e74fc14 100644 --- a/PolyShim/Net80/CollectionExtensions.cs +++ b/PolyShim/Net80/CollectionExtensions.cs @@ -20,10 +20,6 @@ public void AddRange(ReadOnlySpan source) list.Add(item); } - // https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.insertrange - public void InsertRange(int index, ReadOnlySpan source) => - list.InsertRange(index, source.ToArray()); - // https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.copyto public void CopyTo(Span destination) { @@ -33,6 +29,10 @@ public void CopyTo(Span destination) for (var i = 0; i < list.Count; i++) destination[i] = list[i]; } + + // https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.insertrange + public void InsertRange(int index, ReadOnlySpan source) => + list.InsertRange(index, source.ToArray()); } } #endif diff --git a/PolyShim/Net90/EnumerableExtensions.cs b/PolyShim/Net90/EnumerableExtensions.cs index 39ce4c7..53706b1 100644 --- a/PolyShim/Net90/EnumerableExtensions.cs +++ b/PolyShim/Net90/EnumerableExtensions.cs @@ -14,28 +14,6 @@ internal static class MemberPolyfills_Net90_EnumerableExtensions { extension(IEnumerable source) { - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.index - public IEnumerable<(int index, T value)> Index() => - source.Select((value, index) => (index, value)); - - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.countby - public IEnumerable> CountBy( - Func keySelector, - IEqualityComparer? comparer = null - ) - where TKey : notnull - { - var counts = new Dictionary(comparer); - - foreach (var item in source) - { - var key = keySelector(item); - counts[key] = counts.TryGetValue(key, out var count) ? count + 1 : 1; - } - - return counts; - } - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.aggregateby#system-linq-enumerable-aggregateby-3(system-collections-generic-ienumerable((-0))-system-func((-0-1))-system-func((-1-2))-system-func((-2-0-2))-system-collections-generic-iequalitycomparer((-1))) public IEnumerable> AggregateBy( Func keySelector, @@ -69,6 +47,28 @@ public IEnumerable> AggregateBy source.AggregateBy(keySelector, _ => seed, accumulator, keyComparer); + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.countby + public IEnumerable> CountBy( + Func keySelector, + IEqualityComparer? comparer = null + ) + where TKey : notnull + { + var counts = new Dictionary(comparer); + + foreach (var item in source) + { + var key = keySelector(item); + counts[key] = counts.TryGetValue(key, out var count) ? count + 1 : 1; + } + + return counts; + } + + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.index + public IEnumerable<(int index, T value)> Index() => + source.Select((value, index) => (index, value)); } } #endif diff --git a/PolyShim/NetCore10/EnumerableExtensions.cs b/PolyShim/NetCore10/EnumerableExtensions.cs index b70f7a4..d384c5e 100644 --- a/PolyShim/NetCore10/EnumerableExtensions.cs +++ b/PolyShim/NetCore10/EnumerableExtensions.cs @@ -14,22 +14,22 @@ internal static class MemberPolyfills_NetCore10_EnumerableExtensions { extension(IEnumerable source) { - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.prepend - public IEnumerable Prepend(T element) + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.append + public IEnumerable Append(T element) { - yield return element; - foreach (var item in source) yield return item; + + yield return element; } - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.append - public IEnumerable Append(T element) + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.prepend + public IEnumerable Prepend(T element) { + yield return element; + foreach (var item in source) yield return item; - - yield return element; } #if (NETFRAMEWORK && !NET40_OR_GREATER) diff --git a/PolyShim/NetCore20/CollectionExtensions.cs b/PolyShim/NetCore20/CollectionExtensions.cs index 0031164..e1247fb 100644 --- a/PolyShim/NetCore20/CollectionExtensions.cs +++ b/PolyShim/NetCore20/CollectionExtensions.cs @@ -29,16 +29,6 @@ IDictionary dictionary extension(IDictionary dictionary) where TKey : notnull { - // https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.tryadd - public bool TryAdd(TKey key, TValue value) - { - if (dictionary.ContainsKey(key)) - return false; - - dictionary.Add(key, value); - return true; - } - // https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.remove public bool Remove(TKey key, out TValue value) { @@ -51,6 +41,16 @@ public bool Remove(TKey key, out TValue value) value = default!; return false; } + + // https://learn.microsoft.com/dotnet/api/system.collections.generic.collectionextensions.tryadd + public bool TryAdd(TKey key, TValue value) + { + if (dictionary.ContainsKey(key)) + return false; + + dictionary.Add(key, value); + return true; + } } } #endif diff --git a/PolyShim/NetCore20/EnumerableExtensions.cs b/PolyShim/NetCore20/EnumerableExtensions.cs index 4c1b961..84655fd 100644 --- a/PolyShim/NetCore20/EnumerableExtensions.cs +++ b/PolyShim/NetCore20/EnumerableExtensions.cs @@ -14,12 +14,12 @@ internal static class MemberPolyfills_NetCore20_EnumerableExtensions { extension(IEnumerable source) { - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.takelast - public IEnumerable TakeLast(int count) => source.Reverse().Take(count).Reverse(); - // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.skiplast public IEnumerable SkipLast(int count) => source.Reverse().Skip(count).Reverse(); + // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.takelast + public IEnumerable TakeLast(int count) => source.Reverse().Take(count).Reverse(); + #if !NET472_OR_GREATER // https://learn.microsoft.com/dotnet/api/system.linq.enumerable.tohashset#system-linq-enumerable-tohashset-1(system-collections-generic-ienumerable((-0))-system-collections-generic-iequalitycomparer((-0))) public HashSet ToHashSet(IEqualityComparer comparer) => new(source, comparer); diff --git a/PolyShim/NetCore21/MemoryExtensions.cs b/PolyShim/NetCore21/MemoryExtensions.cs index f20e5c9..e74c50b 100644 --- a/PolyShim/NetCore21/MemoryExtensions.cs +++ b/PolyShim/NetCore21/MemoryExtensions.cs @@ -12,15 +12,6 @@ internal static class MemberPolyfills_NetCore21_MemoryExtensions { extension(T[]? array) { - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan-1(-0()-system-int32-system-int32) - public Span AsSpan(int start, int length) => new(array, start, length); - - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan-1(-0()-system-int32) - public Span AsSpan(int start) => array.AsSpan(start, (array?.Length ?? 0) - start); - - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan-1(-0()) - public Span AsSpan() => array.AsSpan(0); - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asmemory#system-memoryextensions-asmemory-1(-0()-system-int32-system-int32) public Memory AsMemory(int start, int length) => new(array, start, length); @@ -30,11 +21,20 @@ internal static class MemberPolyfills_NetCore21_MemoryExtensions // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asmemory#system-memoryextensions-asmemory-1(-0()) public Memory AsMemory() => array.AsMemory(0); - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.copyto#system-memoryextensions-copyto-1(-0()-system-span((-0))) - public void CopyTo(Span destination) => array.AsSpan().CopyTo(destination); + // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan-1(-0()-system-int32-system-int32) + public Span AsSpan(int start, int length) => new(array, start, length); + + // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan-1(-0()-system-int32) + public Span AsSpan(int start) => array.AsSpan(start, (array?.Length ?? 0) - start); + + // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan-1(-0()) + public Span AsSpan() => array.AsSpan(0); // https://learn.microsoft.com/dotnet/api/system.memoryextensions.copyto#system-memoryextensions-copyto-1(-0()-system-memory((-0))) public void CopyTo(Memory destination) => array.AsSpan().CopyTo(destination.Span); + + // https://learn.microsoft.com/dotnet/api/system.memoryextensions.copyto#system-memoryextensions-copyto-1(-0()-system-span((-0))) + public void CopyTo(Span destination) => array.AsSpan().CopyTo(destination); } extension(ArraySegment segment) @@ -45,17 +45,6 @@ internal static class MemberPolyfills_NetCore21_MemoryExtensions extension(string? text) { - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan(system-string-system-int32-system-int32) - public ReadOnlySpan AsSpan(int start, int length) => - new(text?.ToCharArray(), start, length); - - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan(system-string-system-int32) - public ReadOnlySpan AsSpan(int start) => - text.AsSpan(start, (text?.Length ?? 0) - start); - - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan(system-string) - public ReadOnlySpan AsSpan() => text.AsSpan(0); - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asmemory#system-memoryextensions-asmemory(system-string-system-int32-system-int32) public ReadOnlyMemory AsMemory(int start, int length) => new(text?.ToCharArray(), start, length); @@ -66,6 +55,17 @@ public ReadOnlyMemory AsMemory(int start) => // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asmemory#system-memoryextensions-asmemory(system-string) public ReadOnlyMemory AsMemory() => text.AsMemory(0); + + // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan(system-string-system-int32-system-int32) + public ReadOnlySpan AsSpan(int start, int length) => + new(text?.ToCharArray(), start, length); + + // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan(system-string-system-int32) + public ReadOnlySpan AsSpan(int start) => + text.AsSpan(start, (text?.Length ?? 0) - start); + + // https://learn.microsoft.com/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan(system-string) + public ReadOnlySpan AsSpan() => text.AsSpan(0); } extension(Span span) @@ -83,6 +83,20 @@ public int IndexOf(T value) return -1; } + // https://learn.microsoft.com/dotnet/api/system.memoryextensions.reverse#system-memoryextensions-reverse-1(system-span((-0))) + public void Reverse() + { + var i = 0; + var j = span.Length - 1; + + while (i < j) + { + (span[i], span[j]) = (span[j], span[i]); + i++; + j--; + } + } + // https://learn.microsoft.com/dotnet/api/system.memoryextensions.sequenceequal#system-memoryextensions-sequenceequal-1(system-span((-0))-system-readonlyspan((-0))) public bool SequenceEqual(ReadOnlySpan other) { @@ -97,20 +111,6 @@ public bool SequenceEqual(ReadOnlySpan other) return true; } - - // https://learn.microsoft.com/dotnet/api/system.memoryextensions.reverse#system-memoryextensions-reverse-1(system-span((-0))) - public void Reverse() - { - var i = 0; - var j = span.Length - 1; - - while (i < j) - { - (span[i], span[j]) = (span[j], span[i]); - i++; - j--; - } - } } extension(ReadOnlySpan span) diff --git a/PolyShim/NetCore30/TaskAsyncEnumerableExtensions.cs b/PolyShim/NetCore30/TaskAsyncEnumerableExtensions.cs index 811c960..455f923 100644 --- a/PolyShim/NetCore30/TaskAsyncEnumerableExtensions.cs +++ b/PolyShim/NetCore30/TaskAsyncEnumerableExtensions.cs @@ -19,15 +19,15 @@ internal static class MemberPolyfills_NetCore30_TaskAsyncEnumerableExtensions { extension(IAsyncEnumerable source) { - // https://learn.microsoft.com/dotnet/api/system.threading.tasks.taskasyncenumerableextensions.withcancellation - public ConfiguredCancelableAsyncEnumerable WithCancellation( - CancellationToken cancellationToken - ) => new(source, continueOnCapturedContext: true, cancellationToken); - // https://learn.microsoft.com/dotnet/api/system.threading.tasks.taskasyncenumerableextensions.configureawait public ConfiguredCancelableAsyncEnumerable ConfigureAwait( bool continueOnCapturedContext ) => new(source, continueOnCapturedContext, cancellationToken: default); + + // https://learn.microsoft.com/dotnet/api/system.threading.tasks.taskasyncenumerableextensions.withcancellation + public ConfiguredCancelableAsyncEnumerable WithCancellation( + CancellationToken cancellationToken + ) => new(source, continueOnCapturedContext: true, cancellationToken); } } #endif