Skip to content

Commit f82b71a

Browse files
Merge pull request #1700 from dotnet/ix-MinWithTies
2 parents a75748f + c4700f2 commit f82b71a

File tree

8 files changed

+225
-48
lines changed

8 files changed

+225
-48
lines changed

Ix.NET/Source/System.Interactive.Providers/System/Linq/QueryableEx.Generated.cs

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public static IList<TSource> MinBy<TSource, TKey>(IEnumerable<TSource> source, F
108108
#if REFERENCE_ASSEMBLY
109109
return default;
110110
#else
111-
return EnumerableEx.MinBy(source, keySelector);
111+
return EnumerableEx.MinByWithTies(source, keySelector);
112112
#endif
113113
}
114114
#pragma warning restore 1591
@@ -142,14 +142,53 @@ public static IList<TSource> MinBy<TSource, TKey>(this IQueryable<TSource> sourc
142142
);
143143
}
144144

145+
/// <summary>
146+
/// Returns the elements with the minimum key value by using the specified comparer to compare key values.
147+
/// </summary>
148+
/// <typeparam name="TSource">Source sequence element type.</typeparam>
149+
/// <typeparam name="TKey">Key type.</typeparam>
150+
/// <param name="source">Source sequence.</param>
151+
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
152+
/// <param name="comparer">Comparer used to determine the minimum key value.</param>
153+
/// <returns>List with the elements that share the same minimum key value.</returns>
154+
public static IList<TSource> MinByWithTies<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TKey> comparer)
155+
{
156+
if (source == null)
157+
throw new ArgumentNullException(nameof(source));
158+
if (keySelector == null)
159+
throw new ArgumentNullException(nameof(keySelector));
160+
if (comparer == null)
161+
throw new ArgumentNullException(nameof(comparer));
162+
163+
return source.Provider.Execute<IList<TSource>>(
164+
Expression.Call(
165+
null,
166+
((MethodInfo)MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(TSource), typeof(TKey)),
167+
source.Expression,
168+
keySelector,
169+
Expression.Constant(comparer, typeof(IComparer<TKey>))
170+
)
171+
);
172+
}
173+
145174
#pragma warning disable 1591
146175
[EditorBrowsable(EditorBrowsableState.Never)]
147176
public static IList<TSource> MinBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
148177
{
149178
#if REFERENCE_ASSEMBLY
150179
return default;
151180
#else
152-
return EnumerableEx.MinBy(source, keySelector, comparer);
181+
return EnumerableEx.MinByWithTies(source, keySelector, comparer);
182+
#endif
183+
}
184+
185+
[EditorBrowsable(EditorBrowsableState.Never)]
186+
public static IList<TSource> MinByWithTies<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
187+
{
188+
#if REFERENCE_ASSEMBLY
189+
return default;
190+
#else
191+
return EnumerableEx.MinByWithTies(source, keySelector, comparer);
153192
#endif
154193
}
155194
#pragma warning restore 1591
@@ -215,18 +254,55 @@ public static IList<TSource> MaxBy<TSource, TKey>(this IQueryable<TSource> sourc
215254
);
216255
}
217256

257+
/// <summary>
258+
/// Returns the elements with the maximum key value by using the default comparer to compare key values.
259+
/// </summary>
260+
/// <typeparam name="TSource">Source sequence element type.</typeparam>
261+
/// <typeparam name="TKey">Key type.</typeparam>
262+
/// <param name="source">Source sequence.</param>
263+
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
264+
/// <returns>List with the elements that share the same maximum key value.</returns>
265+
public static IList<TSource> MaxByWithTies<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
266+
{
267+
if (source == null)
268+
throw new ArgumentNullException(nameof(source));
269+
if (keySelector == null)
270+
throw new ArgumentNullException(nameof(keySelector));
271+
272+
return source.Provider.Execute<IList<TSource>>(
273+
Expression.Call(
274+
null,
275+
((MethodInfo)MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(TSource), typeof(TKey)),
276+
source.Expression,
277+
keySelector
278+
)
279+
);
280+
}
281+
218282
#pragma warning disable 1591
219283
[EditorBrowsable(EditorBrowsableState.Never)]
220284
public static IList<TSource> MaxBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
221285
{
222286
#if REFERENCE_ASSEMBLY
223287
return default;
224288
#else
225-
return EnumerableEx.MaxBy(source, keySelector);
289+
return EnumerableEx.MaxByWithTies(source, keySelector);
290+
#endif
291+
}
292+
293+
[EditorBrowsable(EditorBrowsableState.Never)]
294+
public static IList<TSource> MaxByWithTies<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
295+
{
296+
#if REFERENCE_ASSEMBLY
297+
return default;
298+
#else
299+
return EnumerableEx.MaxByWithTies(source, keySelector);
226300
#endif
227301
}
228302
#pragma warning restore 1591
229303

304+
305+
230306
/// <summary>
231307
/// Returns the elements with the minimum key value by using the specified comparer to compare key values.
232308
/// </summary>
@@ -263,7 +339,7 @@ public static IList<TSource> MaxBy<TSource, TKey>(IEnumerable<TSource> source, F
263339
#if REFERENCE_ASSEMBLY
264340
return default;
265341
#else
266-
return EnumerableEx.MaxBy(source, keySelector, comparer);
342+
return EnumerableEx.MaxByWithTies(source, keySelector, comparer);
267343
#endif
268344
}
269345
#pragma warning restore 1591

Ix.NET/Source/System.Interactive.Tests/System.Interactive.Tests.csproj

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@
1717

1818
<ItemGroup>
1919
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
20-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
21-
<PrivateAssets>all</PrivateAssets>
22-
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
23-
</PackageReference>
20+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
2421
<PackageReference Include="FluentAssertions" Version="6.4.0" />
2522
<PackageReference Include="xunit" Version="2.4.1" />
2623
</ItemGroup>

Ix.NET/Source/System.Interactive/System/Linq/Operators/Max.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace System.Linq
99
public static partial class EnumerableEx
1010
{
1111

12-
#if !(REFERENCE_ASSEMBLY && (NET6_0_OR_GREATER))
12+
#if !(REFERENCE_ASSEMBLY && NET6_0_OR_GREATER)
1313
/// <summary>
1414
/// Returns the maximum value in the enumerable sequence by using the specified comparer to compare values.
1515
/// </summary>
@@ -24,7 +24,7 @@ public static TSource Max<TSource>(this IEnumerable<TSource> source, IComparer<T
2424
if (comparer == null)
2525
throw new ArgumentNullException(nameof(comparer));
2626

27-
return MaxBy(source, x => x, comparer).First();
27+
return MaxByWithTies(source, x => x, comparer).First();
2828
}
2929
#endif
3030
}

Ix.NET/Source/System.Interactive/System/Linq/Operators/MaxBy.cs

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace System.Linq
88
{
99
public static partial class EnumerableEx
1010
{
11-
#if !(REFERENCE_ASSEMBLY && (NET6_0_OR_GREATER))
11+
#if !(REFERENCE_ASSEMBLY && NET6_0_OR_GREATER)
1212
/// <summary>
1313
/// Returns the elements with the maximum key value by using the default comparer to compare key values.
1414
/// </summary>
@@ -17,6 +17,7 @@ public static partial class EnumerableEx
1717
/// <param name="source">Source sequence.</param>
1818
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
1919
/// <returns>List with the elements that share the same maximum key value.</returns>
20+
[Obsolete("Use MaxByWithTies to maintain same behavior with .NET 6 and later", false)]
2021
public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
2122
{
2223
if (source == null)
@@ -36,6 +37,7 @@ public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> sour
3637
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
3738
/// <param name="comparer">Comparer used to determine the maximum key value.</param>
3839
/// <returns>List with the elements that share the same maximum key value.</returns>
40+
[Obsolete("Use MaxByWithTies to maintain same behavior with .NET 6 and later", false)]
3941
public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
4042
{
4143
if (source == null)
@@ -47,40 +49,6 @@ public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> sour
4749

4850
return ExtremaBy(source, keySelector, (key, minValue) => comparer.Compare(key, minValue));
4951
}
50-
51-
private static IList<TSource> ExtremaBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, int> compare)
52-
{
53-
var result = new List<TSource>();
54-
55-
using (var e = source.GetEnumerator())
56-
{
57-
if (!e.MoveNext())
58-
throw new InvalidOperationException("Source sequence doesn't contain any elements.");
59-
60-
var current = e.Current;
61-
var resKey = keySelector(current);
62-
result.Add(current);
63-
64-
while (e.MoveNext())
65-
{
66-
var cur = e.Current;
67-
var key = keySelector(cur);
68-
69-
var cmp = compare(key, resKey);
70-
if (cmp == 0)
71-
{
72-
result.Add(cur);
73-
}
74-
else if (cmp > 0)
75-
{
76-
result = new List<TSource> { cur };
77-
resKey = key;
78-
}
79-
}
80-
}
81-
82-
return result;
83-
}
8452
#endif
8553
}
8654
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT License.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Generic;
6+
7+
namespace System.Linq
8+
{
9+
public static partial class EnumerableEx
10+
{
11+
/// <summary>
12+
/// Returns the elements with the maximum key value by using the default comparer to compare key values.
13+
/// </summary>
14+
/// <typeparam name="TSource">Source sequence element type.</typeparam>
15+
/// <typeparam name="TKey">Key type.</typeparam>
16+
/// <param name="source">Source sequence.</param>
17+
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
18+
/// <returns>List with the elements that share the same maximum key value.</returns>
19+
public static IList<TSource> MaxByWithTies<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
20+
{
21+
if (source == null)
22+
throw new ArgumentNullException(nameof(source));
23+
if (keySelector == null)
24+
throw new ArgumentNullException(nameof(keySelector));
25+
26+
return MaxByWithTies(source, keySelector, Comparer<TKey>.Default);
27+
}
28+
29+
/// <summary>
30+
/// Returns the elements with the minimum key value by using the specified comparer to compare key values.
31+
/// </summary>
32+
/// <typeparam name="TSource">Source sequence element type.</typeparam>
33+
/// <typeparam name="TKey">Key type.</typeparam>
34+
/// <param name="source">Source sequence.</param>
35+
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
36+
/// <param name="comparer">Comparer used to determine the maximum key value.</param>
37+
/// <returns>List with the elements that share the same maximum key value.</returns>
38+
public static IList<TSource> MaxByWithTies<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
39+
{
40+
if (source == null)
41+
throw new ArgumentNullException(nameof(source));
42+
if (keySelector == null)
43+
throw new ArgumentNullException(nameof(keySelector));
44+
if (comparer == null)
45+
throw new ArgumentNullException(nameof(comparer));
46+
47+
return ExtremaBy(source, keySelector, (key, minValue) => comparer.Compare(key, minValue));
48+
}
49+
50+
private static IList<TSource> ExtremaBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, int> compare)
51+
{
52+
var result = new List<TSource>();
53+
54+
using (var e = source.GetEnumerator())
55+
{
56+
if (!e.MoveNext())
57+
throw new InvalidOperationException("Source sequence doesn't contain any elements.");
58+
59+
var current = e.Current;
60+
var resKey = keySelector(current);
61+
result.Add(current);
62+
63+
while (e.MoveNext())
64+
{
65+
var cur = e.Current;
66+
var key = keySelector(cur);
67+
68+
var cmp = compare(key, resKey);
69+
if (cmp == 0)
70+
{
71+
result.Add(cur);
72+
}
73+
else if (cmp > 0)
74+
{
75+
result = new List<TSource> { cur };
76+
resKey = key;
77+
}
78+
}
79+
}
80+
81+
return result;
82+
}
83+
}
84+
}

Ix.NET/Source/System.Interactive/System/Linq/Operators/Min.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace System.Linq
88
{
99
public static partial class EnumerableEx
1010
{
11-
#if !(REFERENCE_ASSEMBLY && (NET6_0_OR_GREATER))
11+
#if !(REFERENCE_ASSEMBLY && NET6_0_OR_GREATER)
1212
/// <summary>
1313
/// Returns the minimum value in the enumerable sequence by using the specified comparer to compare values.
1414
/// </summary>
@@ -23,7 +23,7 @@ public static TSource Min<TSource>(this IEnumerable<TSource> source, IComparer<T
2323
if (comparer == null)
2424
throw new ArgumentNullException(nameof(comparer));
2525

26-
return MinBy(source, x => x, comparer).First();
26+
return MinByWithTies(source, x => x, comparer).First();
2727
}
2828
#endif
2929
}

Ix.NET/Source/System.Interactive/System/Linq/Operators/MinBy.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace System.Linq
99
public static partial class EnumerableEx
1010
{
1111

12-
#if !(REFERENCE_ASSEMBLY && (NET6_0_OR_GREATER))
12+
#if !(REFERENCE_ASSEMBLY && NET6_0_OR_GREATER)
1313
/// <summary>
1414
/// Returns the elements with the minimum key value by using the default comparer to compare key values.
1515
/// </summary>
@@ -18,6 +18,7 @@ public static partial class EnumerableEx
1818
/// <param name="source">Source sequence.</param>
1919
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
2020
/// <returns>List with the elements that share the same minimum key value.</returns>
21+
[Obsolete("Use MinByWithTies to maintain same behavior with .NET 6 and later", false)]
2122
public static IList<TSource> MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
2223
{
2324
if (source == null)
@@ -37,6 +38,7 @@ public static IList<TSource> MinBy<TSource, TKey>(this IEnumerable<TSource> sour
3738
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
3839
/// <param name="comparer">Comparer used to determine the minimum key value.</param>
3940
/// <returns>List with the elements that share the same minimum key value.</returns>
41+
[Obsolete("Use MinByWithTies to maintain same behavior with .NET 6 and later", false)]
4042
public static IList<TSource> MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
4143
{
4244
if (source == null)

0 commit comments

Comments
 (0)