Skip to content

Commit

Permalink
Merge pull request #1700 from dotnet/ix-MinWithTies
Browse files Browse the repository at this point in the history
  • Loading branch information
clairernovotny authored Feb 1, 2022
2 parents a75748f + c4700f2 commit f82b71a
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public static IList<TSource> MinBy<TSource, TKey>(IEnumerable<TSource> source, F
#if REFERENCE_ASSEMBLY
return default;
#else
return EnumerableEx.MinBy(source, keySelector);
return EnumerableEx.MinByWithTies(source, keySelector);
#endif
}
#pragma warning restore 1591
Expand Down Expand Up @@ -142,14 +142,53 @@ public static IList<TSource> MinBy<TSource, TKey>(this IQueryable<TSource> sourc
);
}

/// <summary>
/// Returns the elements with the minimum key value by using the specified comparer to compare key values.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <typeparam name="TKey">Key type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <param name="comparer">Comparer used to determine the minimum key value.</param>
/// <returns>List with the elements that share the same minimum key value.</returns>
public static IList<TSource> MinByWithTies<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TKey> comparer)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (keySelector == null)
throw new ArgumentNullException(nameof(keySelector));
if (comparer == null)
throw new ArgumentNullException(nameof(comparer));

return source.Provider.Execute<IList<TSource>>(
Expression.Call(
null,
((MethodInfo)MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(TSource), typeof(TKey)),
source.Expression,
keySelector,
Expression.Constant(comparer, typeof(IComparer<TKey>))
)
);
}

#pragma warning disable 1591
[EditorBrowsable(EditorBrowsableState.Never)]
public static IList<TSource> MinBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
{
#if REFERENCE_ASSEMBLY
return default;
#else
return EnumerableEx.MinBy(source, keySelector, comparer);
return EnumerableEx.MinByWithTies(source, keySelector, comparer);
#endif
}

[EditorBrowsable(EditorBrowsableState.Never)]
public static IList<TSource> MinByWithTies<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
{
#if REFERENCE_ASSEMBLY
return default;
#else
return EnumerableEx.MinByWithTies(source, keySelector, comparer);
#endif
}
#pragma warning restore 1591
Expand Down Expand Up @@ -215,18 +254,55 @@ public static IList<TSource> MaxBy<TSource, TKey>(this IQueryable<TSource> sourc
);
}

/// <summary>
/// Returns the elements with the maximum key value by using the default comparer to compare key values.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <typeparam name="TKey">Key type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <returns>List with the elements that share the same maximum key value.</returns>
public static IList<TSource> MaxByWithTies<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (keySelector == null)
throw new ArgumentNullException(nameof(keySelector));

return source.Provider.Execute<IList<TSource>>(
Expression.Call(
null,
((MethodInfo)MethodInfo.GetCurrentMethod()).MakeGenericMethod(typeof(TSource), typeof(TKey)),
source.Expression,
keySelector
)
);
}

#pragma warning disable 1591
[EditorBrowsable(EditorBrowsableState.Never)]
public static IList<TSource> MaxBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
#if REFERENCE_ASSEMBLY
return default;
#else
return EnumerableEx.MaxBy(source, keySelector);
return EnumerableEx.MaxByWithTies(source, keySelector);
#endif
}

[EditorBrowsable(EditorBrowsableState.Never)]
public static IList<TSource> MaxByWithTies<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
#if REFERENCE_ASSEMBLY
return default;
#else
return EnumerableEx.MaxByWithTies(source, keySelector);
#endif
}
#pragma warning restore 1591



/// <summary>
/// Returns the elements with the minimum key value by using the specified comparer to compare key values.
/// </summary>
Expand Down Expand Up @@ -263,7 +339,7 @@ public static IList<TSource> MaxBy<TSource, TKey>(IEnumerable<TSource> source, F
#if REFERENCE_ASSEMBLY
return default;
#else
return EnumerableEx.MaxBy(source, keySelector, comparer);
return EnumerableEx.MaxByWithTies(source, keySelector, comparer);
#endif
}
#pragma warning restore 1591
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageReference Include="FluentAssertions" Version="6.4.0" />
<PackageReference Include="xunit" Version="2.4.1" />
</ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions Ix.NET/Source/System.Interactive/System/Linq/Operators/Max.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace System.Linq
public static partial class EnumerableEx
{

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

return MaxBy(source, x => x, comparer).First();
return MaxByWithTies(source, x => x, comparer).First();
}
#endif
}
Expand Down
38 changes: 3 additions & 35 deletions Ix.NET/Source/System.Interactive/System/Linq/Operators/MaxBy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace System.Linq
{
public static partial class EnumerableEx
{
#if !(REFERENCE_ASSEMBLY && (NET6_0_OR_GREATER))
#if !(REFERENCE_ASSEMBLY && NET6_0_OR_GREATER)
/// <summary>
/// Returns the elements with the maximum key value by using the default comparer to compare key values.
/// </summary>
Expand All @@ -17,6 +17,7 @@ public static partial class EnumerableEx
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <returns>List with the elements that share the same maximum key value.</returns>
[Obsolete("Use MaxByWithTies to maintain same behavior with .NET 6 and later", false)]
public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
if (source == null)
Expand All @@ -36,6 +37,7 @@ public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> sour
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <param name="comparer">Comparer used to determine the maximum key value.</param>
/// <returns>List with the elements that share the same maximum key value.</returns>
[Obsolete("Use MaxByWithTies to maintain same behavior with .NET 6 and later", false)]
public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
{
if (source == null)
Expand All @@ -47,40 +49,6 @@ public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> sour

return ExtremaBy(source, keySelector, (key, minValue) => comparer.Compare(key, minValue));
}

private static IList<TSource> ExtremaBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, int> compare)
{
var result = new List<TSource>();

using (var e = source.GetEnumerator())
{
if (!e.MoveNext())
throw new InvalidOperationException("Source sequence doesn't contain any elements.");

var current = e.Current;
var resKey = keySelector(current);
result.Add(current);

while (e.MoveNext())
{
var cur = e.Current;
var key = keySelector(cur);

var cmp = compare(key, resKey);
if (cmp == 0)
{
result.Add(cur);
}
else if (cmp > 0)
{
result = new List<TSource> { cur };
resKey = key;
}
}
}

return result;
}
#endif
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;

namespace System.Linq
{
public static partial class EnumerableEx
{
/// <summary>
/// Returns the elements with the maximum key value by using the default comparer to compare key values.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <typeparam name="TKey">Key type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <returns>List with the elements that share the same maximum key value.</returns>
public static IList<TSource> MaxByWithTies<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (keySelector == null)
throw new ArgumentNullException(nameof(keySelector));

return MaxByWithTies(source, keySelector, Comparer<TKey>.Default);
}

/// <summary>
/// Returns the elements with the minimum key value by using the specified comparer to compare key values.
/// </summary>
/// <typeparam name="TSource">Source sequence element type.</typeparam>
/// <typeparam name="TKey">Key type.</typeparam>
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <param name="comparer">Comparer used to determine the maximum key value.</param>
/// <returns>List with the elements that share the same maximum key value.</returns>
public static IList<TSource> MaxByWithTies<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
{
if (source == null)
throw new ArgumentNullException(nameof(source));
if (keySelector == null)
throw new ArgumentNullException(nameof(keySelector));
if (comparer == null)
throw new ArgumentNullException(nameof(comparer));

return ExtremaBy(source, keySelector, (key, minValue) => comparer.Compare(key, minValue));
}

private static IList<TSource> ExtremaBy<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TKey, TKey, int> compare)
{
var result = new List<TSource>();

using (var e = source.GetEnumerator())
{
if (!e.MoveNext())
throw new InvalidOperationException("Source sequence doesn't contain any elements.");

var current = e.Current;
var resKey = keySelector(current);
result.Add(current);

while (e.MoveNext())
{
var cur = e.Current;
var key = keySelector(cur);

var cmp = compare(key, resKey);
if (cmp == 0)
{
result.Add(cur);
}
else if (cmp > 0)
{
result = new List<TSource> { cur };
resKey = key;
}
}
}

return result;
}
}
}
4 changes: 2 additions & 2 deletions Ix.NET/Source/System.Interactive/System/Linq/Operators/Min.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace System.Linq
{
public static partial class EnumerableEx
{
#if !(REFERENCE_ASSEMBLY && (NET6_0_OR_GREATER))
#if !(REFERENCE_ASSEMBLY && NET6_0_OR_GREATER)
/// <summary>
/// Returns the minimum value in the enumerable sequence by using the specified comparer to compare values.
/// </summary>
Expand All @@ -23,7 +23,7 @@ public static TSource Min<TSource>(this IEnumerable<TSource> source, IComparer<T
if (comparer == null)
throw new ArgumentNullException(nameof(comparer));

return MinBy(source, x => x, comparer).First();
return MinByWithTies(source, x => x, comparer).First();
}
#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace System.Linq
public static partial class EnumerableEx
{

#if !(REFERENCE_ASSEMBLY && (NET6_0_OR_GREATER))
#if !(REFERENCE_ASSEMBLY && NET6_0_OR_GREATER)
/// <summary>
/// Returns the elements with the minimum key value by using the default comparer to compare key values.
/// </summary>
Expand All @@ -18,6 +18,7 @@ public static partial class EnumerableEx
/// <param name="source">Source sequence.</param>
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <returns>List with the elements that share the same minimum key value.</returns>
[Obsolete("Use MinByWithTies to maintain same behavior with .NET 6 and later", false)]
public static IList<TSource> MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
if (source == null)
Expand All @@ -37,6 +38,7 @@ public static IList<TSource> MinBy<TSource, TKey>(this IEnumerable<TSource> sour
/// <param name="keySelector">Key selector used to extract the key for each element in the sequence.</param>
/// <param name="comparer">Comparer used to determine the minimum key value.</param>
/// <returns>List with the elements that share the same minimum key value.</returns>
[Obsolete("Use MinByWithTies to maintain same behavior with .NET 6 and later", false)]
public static IList<TSource> MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
{
if (source == null)
Expand Down
Loading

0 comments on commit f82b71a

Please sign in to comment.