Skip to content

Commit 61d603f

Browse files
Ensure MaxBy/MinBy return first element if all keys are null. (#61364)
1 parent 057e34d commit 61d603f

File tree

4 files changed

+50
-36
lines changed

4 files changed

+50
-36
lines changed

src/libraries/System.Linq/src/System/Linq/Max.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -583,15 +583,22 @@ public static decimal Max(this IEnumerable<decimal> source)
583583

584584
if (default(TKey) is null)
585585
{
586-
while (key == null)
586+
if (key == null)
587587
{
588-
if (!e.MoveNext())
588+
TSource firstValue = value;
589+
590+
do
589591
{
590-
return value;
591-
}
592+
if (!e.MoveNext())
593+
{
594+
// All keys are null, surface the first element.
595+
return firstValue;
596+
}
592597

593-
value = e.Current;
594-
key = keySelector(value);
598+
value = e.Current;
599+
key = keySelector(value);
600+
}
601+
while (key == null);
595602
}
596603

597604
while (e.MoveNext())

src/libraries/System.Linq/src/System/Linq/Min.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -541,15 +541,22 @@ public static decimal Min(this IEnumerable<decimal> source)
541541

542542
if (default(TKey) is null)
543543
{
544-
while (key == null)
544+
if (key == null)
545545
{
546-
if (!e.MoveNext())
546+
TSource firstValue = value;
547+
548+
do
547549
{
548-
return value;
549-
}
550+
if (!e.MoveNext())
551+
{
552+
// All keys are null, surface the first element.
553+
return firstValue;
554+
}
550555

551-
value = e.Current;
552-
key = keySelector(value);
556+
value = e.Current;
557+
key = keySelector(value);
558+
}
559+
while (key == null);
553560
}
554561

555562
while (e.MoveNext())

src/libraries/System.Linq/tests/MaxTests.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -890,27 +890,27 @@ public static void MaxBy_Generic_EmptyReferenceSource_ReturnsNull()
890890
}
891891

892892
[Fact]
893-
public static void MaxBy_Generic_StructSourceAllKeysAreNull_ReturnsLastElement()
893+
public static void MaxBy_Generic_StructSourceAllKeysAreNull_ReturnsFirstElement()
894894
{
895-
Assert.Equal(4, Enumerable.Range(0, 5).MaxBy(x => default(string)));
896-
Assert.Equal(4, Enumerable.Range(0, 5).MaxBy(x => default(string), comparer: null));
897-
Assert.Equal(4, Enumerable.Range(0, 5).MaxBy(x => default(string), Comparer<string>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
895+
Assert.Equal(0, Enumerable.Range(0, 5).MaxBy(x => default(string)));
896+
Assert.Equal(0, Enumerable.Range(0, 5).MaxBy(x => default(string), comparer: null));
897+
Assert.Equal(0, Enumerable.Range(0, 5).MaxBy(x => default(string), Comparer<string>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
898898
}
899899

900900
[Fact]
901-
public static void MaxBy_Generic_NullableSourceAllKeysAreNull_ReturnsLastElement()
901+
public static void MaxBy_Generic_NullableSourceAllKeysAreNull_ReturnsFirstElement()
902902
{
903-
Assert.Equal(4, Enumerable.Range(0, 5).Cast<int?>().MaxBy(x => default(int?)));
904-
Assert.Equal(4, Enumerable.Range(0, 5).Cast<int?>().MaxBy(x => default(int?), comparer: null));
905-
Assert.Equal(4, Enumerable.Range(0, 5).Cast<int?>().MaxBy(x => default(int?), Comparer<int?>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
903+
Assert.Equal(0, Enumerable.Range(0, 5).Cast<int?>().MaxBy(x => default(int?)));
904+
Assert.Equal(0, Enumerable.Range(0, 5).Cast<int?>().MaxBy(x => default(int?), comparer: null));
905+
Assert.Equal(0, Enumerable.Range(0, 5).Cast<int?>().MaxBy(x => default(int?), Comparer<int?>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
906906
}
907907

908908
[Fact]
909-
public static void MaxBy_Generic_ReferenceSourceAllKeysAreNull_ReturnsLastElement()
909+
public static void MaxBy_Generic_ReferenceSourceAllKeysAreNull_ReturnsFirstElement()
910910
{
911-
Assert.Equal("4", Enumerable.Range(0, 5).Select(x => x.ToString()).MaxBy(x => default(string)));
912-
Assert.Equal("4", Enumerable.Range(0, 5).Select(x => x.ToString()).MaxBy(x => default(string), comparer: null));
913-
Assert.Equal("4", Enumerable.Range(0, 5).Select(x => x.ToString()).MaxBy(x => default(string), Comparer<string>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
911+
Assert.Equal("0", Enumerable.Range(0, 5).Select(x => x.ToString()).MaxBy(x => default(string)));
912+
Assert.Equal("0", Enumerable.Range(0, 5).Select(x => x.ToString()).MaxBy(x => default(string), comparer: null));
913+
Assert.Equal("0", Enumerable.Range(0, 5).Select(x => x.ToString()).MaxBy(x => default(string), Comparer<string>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
914914
}
915915

916916
[Theory]

src/libraries/System.Linq/tests/MinTests.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -868,27 +868,27 @@ public static void MinBy_Generic_EmptyReferenceSource_ReturnsNull()
868868
}
869869

870870
[Fact]
871-
public static void MinBy_Generic_StructSourceAllKeysAreNull_ReturnsLastElement()
871+
public static void MinBy_Generic_StructSourceAllKeysAreNull_ReturnsFirstElement()
872872
{
873-
Assert.Equal(4, Enumerable.Range(0, 5).MinBy(x => default(string)));
874-
Assert.Equal(4, Enumerable.Range(0, 5).MinBy(x => default(string), comparer: null));
875-
Assert.Equal(4, Enumerable.Range(0, 5).MinBy(x => default(string), Comparer<string>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
873+
Assert.Equal(0, Enumerable.Range(0, 5).MinBy(x => default(string)));
874+
Assert.Equal(0, Enumerable.Range(0, 5).MinBy(x => default(string), comparer: null));
875+
Assert.Equal(0, Enumerable.Range(0, 5).MinBy(x => default(string), Comparer<string>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
876876
}
877877

878878
[Fact]
879-
public static void MinBy_Generic_NullableSourceAllKeysAreNull_ReturnsLastElement()
879+
public static void MinBy_Generic_NullableSourceAllKeysAreNull_ReturnsFirstElement()
880880
{
881-
Assert.Equal(4, Enumerable.Range(0, 5).Cast<int?>().MinBy(x => default(int?)));
882-
Assert.Equal(4, Enumerable.Range(0, 5).Cast<int?>().MinBy(x => default(int?), comparer: null));
883-
Assert.Equal(4, Enumerable.Range(0, 5).Cast<int?>().MinBy(x => default(int?), Comparer<int?>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
881+
Assert.Equal(0, Enumerable.Range(0, 5).Cast<int?>().MinBy(x => default(int?)));
882+
Assert.Equal(0, Enumerable.Range(0, 5).Cast<int?>().MinBy(x => default(int?), comparer: null));
883+
Assert.Equal(0, Enumerable.Range(0, 5).Cast<int?>().MinBy(x => default(int?), Comparer<int?>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
884884
}
885885

886886
[Fact]
887-
public static void MinBy_Generic_ReferenceSourceAllKeysAreNull_ReturnsLastElement()
887+
public static void MinBy_Generic_ReferenceSourceAllKeysAreNull_ReturnsFirstElement()
888888
{
889-
Assert.Equal("4", Enumerable.Range(0, 5).Select(x => x.ToString()).MinBy(x => default(string)));
890-
Assert.Equal("4", Enumerable.Range(0, 5).Select(x => x.ToString()).MinBy(x => default(string), comparer: null));
891-
Assert.Equal("4", Enumerable.Range(0, 5).Select(x => x.ToString()).MinBy(x => default(string), Comparer<string>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
889+
Assert.Equal("0", Enumerable.Range(0, 5).Select(x => x.ToString()).MinBy(x => default(string)));
890+
Assert.Equal("0", Enumerable.Range(0, 5).Select(x => x.ToString()).MinBy(x => default(string), comparer: null));
891+
Assert.Equal("0", Enumerable.Range(0, 5).Select(x => x.ToString()).MinBy(x => default(string), Comparer<string>.Create((_, _) => throw new InvalidOperationException("comparer should not be called."))));
892892
}
893893

894894
[Theory]

0 commit comments

Comments
 (0)