소스 검색

Add MinByWithTies and MaxByWithTies to bring back a MinBy/MaxBy that returns a list.

Claire Novotny 3 년 전
부모
커밋
c4700f28b4

+ 80 - 4
Ix.NET/Source/System.Interactive.Providers/System/Linq/QueryableEx.Generated.cs

@@ -108,7 +108,7 @@ namespace System.Linq
 #if REFERENCE_ASSEMBLY
             return default;
 #else
-            return EnumerableEx.MinBy(source, keySelector);
+            return EnumerableEx.MinByWithTies(source, keySelector);
 #endif
         }
 #pragma warning restore 1591
@@ -142,6 +142,35 @@ namespace System.Linq
             );
         }
 
+        /// <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)
@@ -149,7 +178,17 @@ namespace System.Linq
 #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
@@ -215,6 +254,31 @@ namespace System.Linq
             );
         }
 
+        /// <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)
@@ -222,11 +286,23 @@ namespace System.Linq
 #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>
@@ -263,7 +339,7 @@ namespace System.Linq
 #if REFERENCE_ASSEMBLY
             return default;
 #else
-            return EnumerableEx.MaxBy(source, keySelector, comparer);
+            return EnumerableEx.MaxByWithTies(source, keySelector, comparer);
 #endif
         }
 #pragma warning restore 1591

+ 1 - 4
Ix.NET/Source/System.Interactive.Tests/System.Interactive.Tests.csproj

@@ -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>

+ 2 - 2
Ix.NET/Source/System.Interactive/System/Linq/Operators/Max.cs

@@ -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>
@@ -24,7 +24,7 @@ namespace System.Linq
             if (comparer == null)
                 throw new ArgumentNullException(nameof(comparer));
 
-            return MaxBy(source, x => x, comparer).First();
+            return MaxByWithTies(source, x => x, comparer).First();
         }
 #endif
     }

+ 3 - 35
Ix.NET/Source/System.Interactive/System/Linq/Operators/MaxBy.cs

@@ -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>
@@ -17,6 +17,7 @@ namespace System.Linq
         /// <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)
@@ -36,6 +37,7 @@ namespace System.Linq
         /// <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)
@@ -47,40 +49,6 @@ namespace System.Linq
 
             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
     }
 }

+ 84 - 0
Ix.NET/Source/System.Interactive/System/Linq/Operators/MaxByWithTies.cs

@@ -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;
+        }
+    }
+}

+ 2 - 2
Ix.NET/Source/System.Interactive/System/Linq/Operators/Min.cs

@@ -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>
@@ -23,7 +23,7 @@ namespace System.Linq
             if (comparer == null)
                 throw new ArgumentNullException(nameof(comparer));
 
-            return MinBy(source, x => x, comparer).First();
+            return MinByWithTies(source, x => x, comparer).First();
         }
 #endif
     }

+ 3 - 1
Ix.NET/Source/System.Interactive/System/Linq/Operators/MinBy.cs

@@ -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>
@@ -18,6 +18,7 @@ namespace System.Linq
         /// <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)
@@ -37,6 +38,7 @@ namespace System.Linq
         /// <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)

+ 50 - 0
Ix.NET/Source/System.Interactive/System/Linq/Operators/MinByWithTies.cs

@@ -0,0 +1,50 @@
+// 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 minimum 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 minimum key value.</returns>
+        public static IList<TSource> MinByWithTies<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 MinByWithTies(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 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 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));
+        }
+    }
+}