未验证 提交 abd01f54 编写于 作者: E Eirik Tsarpalis 提交者: GitHub

Implement Enumerable.*By methods (DistinctBy, ExceptBy, IntersectBy, UnionBy,...

Implement Enumerable.*By methods (DistinctBy, ExceptBy, IntersectBy, UnionBy, MinBy, MaxBy) (#50335)

* Implement Enumerable.*By operators

* apply feedback

* Update src/libraries/System.Linq/src/System/Linq/Distinct.cs
Co-authored-by: NStephen Toub <stoub@microsoft.com>
Co-authored-by: NStephen Toub <stoub@microsoft.com>
上级 f4df53be
......@@ -77,18 +77,22 @@ public static partial class Queryable
public static int Count<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<TSource> DefaultIfEmpty<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static System.Linq.IQueryable<TSource> DefaultIfEmpty<TSource>(this System.Linq.IQueryable<TSource> source, TSource defaultValue) { throw null; }
public static System.Linq.IQueryable<TSource> DistinctBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
public static System.Linq.IQueryable<TSource> DistinctBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Linq.IQueryable<TSource> Distinct<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static System.Linq.IQueryable<TSource> Distinct<TSource>(this System.Linq.IQueryable<TSource> source, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static TSource? ElementAtOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, int index) { throw null; }
public static TSource? ElementAtOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, System.Index index) { throw null; }
public static TSource ElementAt<TSource>(this System.Linq.IQueryable<TSource> source, int index) { throw null; }
public static TSource? ElementAtOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, int index) { throw null; }
public static TSource ElementAt<TSource>(this System.Linq.IQueryable<TSource> source, System.Index index) { throw null; }
public static TSource ElementAt<TSource>(this System.Linq.IQueryable<TSource> source, int index) { throw null; }
public static System.Linq.IQueryable<TSource> ExceptBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TKey> source2, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
public static System.Linq.IQueryable<TSource> ExceptBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TKey> source2, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Linq.IQueryable<TSource> Except<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2) { throw null; }
public static System.Linq.IQueryable<TSource> Except<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static TSource? FirstOrDefault<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static TSource FirstOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, TSource defaultValue) { throw null; }
public static TSource? FirstOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
public static TSource FirstOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate, TSource defaultValue) { throw null; }
public static TSource FirstOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, TSource defaultValue) { throw null; }
public static TSource First<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static TSource First<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<System.Linq.IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
......@@ -101,21 +105,29 @@ public static partial class Queryable
public static System.Linq.IQueryable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Linq.Expressions.Expression<System.Func<TSource, TElement>> elementSelector, System.Linq.Expressions.Expression<System.Func<TKey, System.Collections.Generic.IEnumerable<TElement>, TResult>> resultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Linq.IQueryable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this System.Linq.IQueryable<TOuter> outer, System.Collections.Generic.IEnumerable<TInner> inner, System.Linq.Expressions.Expression<System.Func<TOuter, TKey>> outerKeySelector, System.Linq.Expressions.Expression<System.Func<TInner, TKey>> innerKeySelector, System.Linq.Expressions.Expression<System.Func<TOuter, System.Collections.Generic.IEnumerable<TInner>, TResult>> resultSelector) { throw null; }
public static System.Linq.IQueryable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this System.Linq.IQueryable<TOuter> outer, System.Collections.Generic.IEnumerable<TInner> inner, System.Linq.Expressions.Expression<System.Func<TOuter, TKey>> outerKeySelector, System.Linq.Expressions.Expression<System.Func<TInner, TKey>> innerKeySelector, System.Linq.Expressions.Expression<System.Func<TOuter, System.Collections.Generic.IEnumerable<TInner>, TResult>> resultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Linq.IQueryable<TSource> IntersectBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TKey> source2, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
public static System.Linq.IQueryable<TSource> IntersectBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TKey> source2, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Linq.IQueryable<TSource> Intersect<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2) { throw null; }
public static System.Linq.IQueryable<TSource> Intersect<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static System.Linq.IQueryable<TResult> Join<TOuter, TInner, TKey, TResult>(this System.Linq.IQueryable<TOuter> outer, System.Collections.Generic.IEnumerable<TInner> inner, System.Linq.Expressions.Expression<System.Func<TOuter, TKey>> outerKeySelector, System.Linq.Expressions.Expression<System.Func<TInner, TKey>> innerKeySelector, System.Linq.Expressions.Expression<System.Func<TOuter, TInner, TResult>> resultSelector) { throw null; }
public static System.Linq.IQueryable<TResult> Join<TOuter, TInner, TKey, TResult>(this System.Linq.IQueryable<TOuter> outer, System.Collections.Generic.IEnumerable<TInner> inner, System.Linq.Expressions.Expression<System.Func<TOuter, TKey>> outerKeySelector, System.Linq.Expressions.Expression<System.Func<TInner, TKey>> innerKeySelector, System.Linq.Expressions.Expression<System.Func<TOuter, TInner, TResult>> resultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static TSource? LastOrDefault<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static TSource LastOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, TSource defaultValue) { throw null; }
public static TSource? LastOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
public static TSource LastOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate, TSource defaultValue) { throw null; }
public static TSource LastOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, TSource defaultValue) { throw null; }
public static TSource Last<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static TSource Last<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
public static long LongCount<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static long LongCount<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
public static TSource? MaxBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
public static TSource? MaxBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
public static TSource? Max<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static TSource? Max<TSource>(this System.Linq.IQueryable<TSource> source, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
public static TResult? Max<TSource, TResult>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TResult>> selector) { throw null; }
public static TSource? MinBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
public static TSource? MinBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
public static TSource? Min<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static TSource? Min<TSource>(this System.Linq.IQueryable<TSource> source, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
public static TResult? Min<TSource, TResult>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TResult>> selector) { throw null; }
public static System.Linq.IQueryable<TResult> OfType<TResult>(this System.Linq.IQueryable source) { throw null; }
public static System.Linq.IOrderedQueryable<TSource> OrderByDescending<TSource, TKey>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
......@@ -133,9 +145,9 @@ public static partial class Queryable
public static bool SequenceEqual<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2) { throw null; }
public static bool SequenceEqual<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static TSource? SingleOrDefault<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static TSource SingleOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, TSource defaultValue) { throw null; }
public static TSource? SingleOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
public static TSource SingleOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate, TSource defaultValue) { throw null; }
public static TSource SingleOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, TSource defaultValue) { throw null; }
public static TSource Single<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
public static TSource Single<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<TSource> SkipLast<TSource>(this System.Linq.IQueryable<TSource> source, int count) { throw null; }
......@@ -171,6 +183,8 @@ public static partial class Queryable
public static System.Linq.IOrderedQueryable<TSource> ThenByDescending<TSource, TKey>(this System.Linq.IOrderedQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TKey>? comparer) { throw null; }
public static System.Linq.IOrderedQueryable<TSource> ThenBy<TSource, TKey>(this System.Linq.IOrderedQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
public static System.Linq.IOrderedQueryable<TSource> ThenBy<TSource, TKey>(this System.Linq.IOrderedQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IComparer<TKey>? comparer) { throw null; }
public static System.Linq.IQueryable<TSource> UnionBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }
public static System.Linq.IQueryable<TSource> UnionBy<TSource, TKey>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Linq.IQueryable<TSource> Union<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2) { throw null; }
public static System.Linq.IQueryable<TSource> Union<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
......
......@@ -224,6 +224,18 @@ internal static class CachedReflectionInfo
(s_Distinct_TSource_2 ??= new Func<IQueryable<object>, IEqualityComparer<object>, IQueryable<object>>(Queryable.Distinct).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);
private static MethodInfo? s_DistinctBy_TSource_TKey_2;
public static MethodInfo DistinctBy_TSource_TKey_2(Type TSource, Type TKey) =>
(s_DistinctBy_TSource_TKey_2 ??= new Func<IQueryable<object>, Expression<Func<object, object>>, IQueryable<object>>(Queryable.DistinctBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_DistinctBy_TSource_TKey_3;
public static MethodInfo DistinctBy_TSource_TKey_3(Type TSource, Type TKey) =>
(s_DistinctBy_TSource_TKey_3 ??= new Func<IQueryable<object>, Expression<Func<object, object>>, IEqualityComparer<object>, IQueryable<object>>(Queryable.DistinctBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_ElementAt_Int32_TSource_2;
public static MethodInfo ElementAt_Int32_TSource_2(Type TSource) =>
......@@ -260,6 +272,18 @@ internal static class CachedReflectionInfo
(s_Except_TSource_3 ??= new Func<IQueryable<object>, IEnumerable<object>, IEqualityComparer<object>, IQueryable<object>>(Queryable.Except).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);
private static MethodInfo? s_ExceptBy_TSource_TKey_3;
public static MethodInfo ExceptBy_TSource_TKey_3(Type TSource, Type TKey) =>
(s_ExceptBy_TSource_TKey_3 ??= new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object>>, IQueryable<object>>(Queryable.ExceptBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_ExceptBy_TSource_TKey_4;
public static MethodInfo ExceptBy_TSource_TKey_4(Type TSource, Type TKey) =>
(s_ExceptBy_TSource_TKey_4 ??= new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object>>, IEqualityComparer<object>, IQueryable<object>>(Queryable.ExceptBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_First_TSource_1;
public static MethodInfo First_TSource_1(Type TSource) =>
......@@ -370,6 +394,18 @@ internal static class CachedReflectionInfo
(s_Intersect_TSource_3 ??= new Func<IQueryable<object>, IEnumerable<object>, IEqualityComparer<object>, IQueryable<object>>(Queryable.Intersect).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);
private static MethodInfo? s_IntersectBy_TSource_TKey_3;
public static MethodInfo IntersectBy_TSource_TKey_3(Type TSource, Type TKey) =>
(s_IntersectBy_TSource_TKey_3 ??= new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object>>, IQueryable<object>>(Queryable.IntersectBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_IntersectBy_TSource_TKey_4;
public static MethodInfo IntersectBy_TSource_TKey_4(Type TSource, Type TKey) =>
(s_IntersectBy_TSource_TKey_4 ??= new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object>>, IEqualityComparer<object>, IQueryable<object>>(Queryable.IntersectBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_Join_TOuter_TInner_TKey_TResult_5;
public static MethodInfo Join_TOuter_TInner_TKey_TResult_5(Type TOuter, Type TInner, Type TKey, Type TResult) =>
......@@ -438,24 +474,60 @@ internal static class CachedReflectionInfo
(s_Max_TSource_1 ??= new Func<IQueryable<object>, object?>(Queryable.Max).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);
private static MethodInfo? s_Max_TSource_2;
public static MethodInfo Max_TSource_2(Type TSource) =>
(s_Max_TSource_2 ??= new Func<IQueryable<object>, IComparer<object>, object?>(Queryable.Max).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);
private static MethodInfo? s_Max_TSource_TResult_2;
public static MethodInfo Max_TSource_TResult_2(Type TSource, Type TResult) =>
(s_Max_TSource_TResult_2 ??= new Func<IQueryable<object>, Expression<Func<object, object>>, object?>(Queryable.Max).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TResult);
private static MethodInfo? s_MaxBy_TSource_TKey_2;
public static MethodInfo MaxBy_TSource_TKey_2(Type TSource, Type TKey) =>
(s_MaxBy_TSource_TKey_2 ??= new Func<IQueryable<object>, Expression<Func<object, object>>, object?>(Queryable.MaxBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_MaxBy_TSource_TKey_3;
public static MethodInfo MaxBy_TSource_TKey_3(Type TSource, Type TKey) =>
(s_MaxBy_TSource_TKey_3 ??= new Func<IQueryable<object>, Expression<Func<object, object>>, IComparer<object>, object?>(Queryable.MaxBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_Min_TSource_1;
public static MethodInfo Min_TSource_1(Type TSource) =>
(s_Min_TSource_1 ??= new Func<IQueryable<object>, object?>(Queryable.Min).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);
private static MethodInfo? s_Min_TSource_2;
public static MethodInfo Min_TSource_2(Type TSource) =>
(s_Min_TSource_2 ??= new Func<IQueryable<object>, IComparer<object>, object?>(Queryable.Min).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);
private static MethodInfo? s_Min_TSource_TResult_2;
public static MethodInfo Min_TSource_TResult_2(Type TSource, Type TResult) =>
(s_Min_TSource_TResult_2 ??= new Func<IQueryable<object>, Expression<Func<object, object>>, object?>(Queryable.Min).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TResult);
private static MethodInfo? s_MinBy_TSource_TKey_2;
public static MethodInfo MinBy_TSource_TKey_2(Type TSource, Type TKey) =>
(s_MinBy_TSource_TKey_2 ??= new Func<IQueryable<object>, Expression<Func<object, object>>, object?>(Queryable.MinBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_MinBy_TSource_TKey_3;
public static MethodInfo MinBy_TSource_TKey_3(Type TSource, Type TKey) =>
(s_MinBy_TSource_TKey_3 ??= new Func<IQueryable<object>, Expression<Func<object, object>>, IComparer<object>, object?>(Queryable.MinBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_OfType_TResult_1;
public static MethodInfo OfType_TResult_1(Type TResult) =>
......@@ -766,6 +838,18 @@ internal static class CachedReflectionInfo
(s_Union_TSource_3 ??= new Func<IQueryable<object>, IEnumerable<object>, IEqualityComparer<object>, IQueryable<object>>(Queryable.Union).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);
private static MethodInfo? s_UnionBy_TSource_TKey_3;
public static MethodInfo UnionBy_TSource_TKey_3(Type TSource, Type TKey) =>
(s_UnionBy_TSource_TKey_3 ??= new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object>>, IQueryable<object>>(Queryable.UnionBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_UnionBy_TSource_TKey_4;
public static MethodInfo UnionBy_TSource_TKey_4(Type TSource, Type TKey) =>
(s_UnionBy_TSource_TKey_4 ??= new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object>>, IEqualityComparer<object>, IQueryable<object>>(Queryable.UnionBy).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource, TKey);
private static MethodInfo? s_Where_TSource_2;
public static MethodInfo Where_TSource_2(Type TSource) =>
......
......@@ -639,6 +639,36 @@ public static IQueryable<TSource> Distinct<TSource>(this IQueryable<TSource> sou
));
}
[DynamicDependency("DistinctBy`2", typeof(Enumerable))]
public static IQueryable<TSource> DistinctBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.DistinctBy_TSource_TKey_2(typeof(TSource), typeof(TKey)),
source.Expression, Expression.Quote(keySelector)
));
}
[DynamicDependency("DistinctBy`2", typeof(Enumerable))]
public static IQueryable<TSource> DistinctBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IEqualityComparer<TKey>? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.DistinctBy_TSource_TKey_3(typeof(TSource), typeof(TKey)),
source.Expression, Expression.Quote(keySelector), Expression.Constant(comparer, typeof(IEqualityComparer<TKey>))
));
}
[DynamicDependency("Chunk`1", typeof(Enumerable))]
public static IQueryable<TSource[]> Chunk<TSource>(this IQueryable<TSource> source, int size)
{
......@@ -763,6 +793,43 @@ public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source
));
}
[DynamicDependency("UnionBy`2", typeof(Enumerable))]
public static IQueryable<TSource> UnionBy<TSource, TKey>(this IQueryable<TSource> source1, IEnumerable<TSource> source2, Expression<Func<TSource, TKey>> keySelector)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source1.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.UnionBy_TSource_TKey_3(typeof(TSource), typeof(TKey)),
source1.Expression, GetSourceExpression(source2), Expression.Quote(keySelector)
));
}
[DynamicDependency("UnionBy`2", typeof(Enumerable))]
public static IQueryable<TSource> UnionBy<TSource, TKey>(this IQueryable<TSource> source1, IEnumerable<TSource> source2, Expression<Func<TSource, TKey>> keySelector, IEqualityComparer<TKey>? comparer)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source1.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.UnionBy_TSource_TKey_4(typeof(TSource), typeof(TKey)),
source1.Expression,
GetSourceExpression(source2),
Expression.Quote(keySelector),
Expression.Constant(comparer, typeof(IEqualityComparer<TKey>))
));
}
[DynamicDependency("Intersect`1", typeof(Enumerable))]
public static IQueryable<TSource> Intersect<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
......@@ -795,6 +862,45 @@ public static IQueryable<TSource> Intersect<TSource>(this IQueryable<TSource> so
));
}
[DynamicDependency("IntersectBy`2", typeof(Enumerable))]
public static IQueryable<TSource> IntersectBy<TSource, TKey>(this IQueryable<TSource> source1, IEnumerable<TKey> source2, Expression<Func<TSource, TKey>> keySelector)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source1.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.IntersectBy_TSource_TKey_3(typeof(TSource), typeof(TKey)),
source1.Expression,
GetSourceExpression(source2),
Expression.Quote(keySelector)
));
}
[DynamicDependency("IntersectBy`2", typeof(Enumerable))]
public static IQueryable<TSource> IntersectBy<TSource, TKey>(this IQueryable<TSource> source1, IEnumerable<TKey> source2, Expression<Func<TSource, TKey>> keySelector, IEqualityComparer<TKey>? comparer)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source1.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.IntersectBy_TSource_TKey_4(typeof(TSource), typeof(TKey)),
source1.Expression,
GetSourceExpression(source2),
Expression.Quote(keySelector),
Expression.Constant(comparer, typeof(IEqualityComparer<TKey>))
));
}
[DynamicDependency("Except`1", typeof(Enumerable))]
public static IQueryable<TSource> Except<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
......@@ -827,6 +933,45 @@ public static IQueryable<TSource> Except<TSource>(this IQueryable<TSource> sourc
));
}
[DynamicDependency("ExceptBy`2", typeof(Enumerable))]
public static IQueryable<TSource> ExceptBy<TSource, TKey>(this IQueryable<TSource> source1, IEnumerable<TKey> source2, Expression<Func<TSource, TKey>> keySelector)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source1.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.ExceptBy_TSource_TKey_3(typeof(TSource), typeof(TKey)),
source1.Expression,
GetSourceExpression(source2),
Expression.Quote(keySelector)
));
}
[DynamicDependency("ExceptBy`2", typeof(Enumerable))]
public static IQueryable<TSource> ExceptBy<TSource, TKey>(this IQueryable<TSource> source1, IEnumerable<TKey> source2, Expression<Func<TSource, TKey>> keySelector, IEqualityComparer<TKey>? comparer)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source1.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.ExceptBy_TSource_TKey_4(typeof(TSource), typeof(TKey)),
source1.Expression,
GetSourceExpression(source2),
Expression.Quote(keySelector),
Expression.Constant(comparer, typeof(IEqualityComparer<TKey>))
));
}
[DynamicDependency("First`1", typeof(Enumerable))]
public static TSource First<TSource>(this IQueryable<TSource> source)
{
......@@ -1339,6 +1484,20 @@ public static long LongCount<TSource>(this IQueryable<TSource> source, Expressio
CachedReflectionInfo.Min_TSource_1(typeof(TSource)), source.Expression));
}
[DynamicDependency("Min`1", typeof(Enumerable))]
public static TSource? Min<TSource>(this IQueryable<TSource> source, IComparer<TSource>? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
return source.Provider.Execute<TSource>(
Expression.Call(
null,
CachedReflectionInfo.Min_TSource_2(typeof(TSource)),
source.Expression,
Expression.Constant(comparer, typeof(IComparer<TSource>))
));
}
[DynamicDependency("Min`2", typeof(Enumerable))]
public static TResult? Min<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
{
......@@ -1354,6 +1513,39 @@ public static long LongCount<TSource>(this IQueryable<TSource> source, Expressio
));
}
[DynamicDependency("MinBy`2", typeof(Enumerable))]
public static TSource? MinBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source.Provider.Execute<TSource>(
Expression.Call(
null,
CachedReflectionInfo.MinBy_TSource_TKey_2(typeof(TSource), typeof(TKey)),
source.Expression,
Expression.Quote(keySelector)
));
}
[DynamicDependency("MinBy`2", typeof(Enumerable))]
public static TSource? MinBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TSource>? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source.Provider.Execute<TSource>(
Expression.Call(
null,
CachedReflectionInfo.MinBy_TSource_TKey_3(typeof(TSource), typeof(TKey)),
source.Expression,
Expression.Quote(keySelector),
Expression.Constant(comparer, typeof(IComparer<TSource>))
));
}
[DynamicDependency("Max`1", typeof(Enumerable))]
public static TSource? Max<TSource>(this IQueryable<TSource> source)
{
......@@ -1365,6 +1557,20 @@ public static long LongCount<TSource>(this IQueryable<TSource> source, Expressio
CachedReflectionInfo.Max_TSource_1(typeof(TSource)), source.Expression));
}
[DynamicDependency("Max`1", typeof(Enumerable))]
public static TSource? Max<TSource>(this IQueryable<TSource> source, IComparer<TSource>? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
return source.Provider.Execute<TSource>(
Expression.Call(
null,
CachedReflectionInfo.Max_TSource_2(typeof(TSource)),
source.Expression,
Expression.Constant(comparer, typeof(IComparer<TSource>))
));
}
[DynamicDependency("Max`2", typeof(Enumerable))]
public static TResult? Max<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
{
......@@ -1380,6 +1586,39 @@ public static long LongCount<TSource>(this IQueryable<TSource> source, Expressio
));
}
[DynamicDependency("MaxBy`2", typeof(Enumerable))]
public static TSource? MaxBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source.Provider.Execute<TSource>(
Expression.Call(
null,
CachedReflectionInfo.MaxBy_TSource_TKey_2(typeof(TSource), typeof(TKey)),
source.Expression,
Expression.Quote(keySelector)
));
}
[DynamicDependency("MaxBy`2", typeof(Enumerable))]
public static TSource? MaxBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, IComparer<TSource>? comparer)
{
if (source == null)
throw Error.ArgumentNull(nameof(source));
if (keySelector == null)
throw Error.ArgumentNull(nameof(keySelector));
return source.Provider.Execute<TSource>(
Expression.Call(
null,
CachedReflectionInfo.MaxBy_TSource_TKey_3(typeof(TSource), typeof(TKey)),
source.Expression,
Expression.Quote(keySelector),
Expression.Constant(comparer, typeof(IComparer<TSource>))
));
}
[DynamicDependency("Sum", typeof(Enumerable))]
public static int Sum(this IQueryable<int> source)
{
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq.Expressions;
using Xunit;
namespace System.Linq.Tests
......@@ -77,5 +78,40 @@ public void Distinct2()
var count = (new int[] { 0, 1, 2, 2, 0 }).AsQueryable().Distinct(EqualityComparer<int>.Default).Count();
Assert.Equal(3, count);
}
[Fact]
public void DistinctBy_NullSource_ThrowsArgumentNullException()
{
IQueryable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.DistinctBy(x => x));
AssertExtensions.Throws<ArgumentNullException>("source", () => source.DistinctBy(x => x, EqualityComparer<int>.Default));
}
[Fact]
public void DistinctBy_NullKeySelector_ThrowsArgumentNullException()
{
IQueryable<int> source = Enumerable.Empty<int>().AsQueryable();
Expression<Func<int, int>> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.DistinctBy(keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.DistinctBy(keySelector, EqualityComparer<int>.Default));
}
[Fact]
public void DistinctBy()
{
var expected = Enumerable.Range(0, 3);
var actual = Enumerable.Range(0, 20).AsQueryable().DistinctBy(x => x % 3).ToArray();
Assert.Equal(expected, actual);
}
[Fact]
public void DistinctBy_CustomComparison()
{
var expected = Enumerable.Range(0, 3);
var actual = Enumerable.Range(0, 20).AsQueryable().DistinctBy(x => x % 3, EqualityComparer<int>.Default).ToArray();
Assert.Equal(expected, actual);
}
}
}
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq.Expressions;
using Xunit;
namespace System.Linq.Tests
......@@ -85,5 +86,50 @@ public void Except2()
var count = (new int[] { 0, 1, 2 }).AsQueryable().Except((new int[] { 1, 2, 3 }).AsQueryable(), EqualityComparer<int>.Default).Count();
Assert.Equal(1, count);
}
[Fact]
public void ExceptBy_NullSource1_ThrowsArgumentNullException()
{
IQueryable<int> source1 = null;
AssertExtensions.Throws<ArgumentNullException>("source1", () => source1.ExceptBy(Enumerable.Empty<int>(), x => x));
AssertExtensions.Throws<ArgumentNullException>("source1", () => source1.ExceptBy(Enumerable.Empty<int>(), x => x, EqualityComparer<int>.Default));
}
[Fact]
public void ExceptBy_NullSource2_ThrowsArgumentNullException()
{
IQueryable<int> source1 = Enumerable.Empty<int>().AsQueryable();
IQueryable<int> source2 = null;
AssertExtensions.Throws<ArgumentNullException>("source2", () => source1.ExceptBy(source2, x => x));
AssertExtensions.Throws<ArgumentNullException>("source2", () => source1.ExceptBy(source2, x => x, EqualityComparer<int>.Default));
}
[Fact]
public void ExceptBy_NullKeySelector_ThrowsArgumentNullException()
{
IQueryable<int> source = Enumerable.Empty<int>().AsQueryable();
Expression<Func<int, int>> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.ExceptBy(source, keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.ExceptBy(source, keySelector, EqualityComparer<int>.Default));
}
[Fact]
public void ExceptBy()
{
var expected = Enumerable.Range(5, 5);
var actual = Enumerable.Range(0, 10).AsQueryable().ExceptBy(Enumerable.Range(0, 5), x => x).ToArray();
Assert.Equal(expected, actual);
}
[Fact]
public void ExceptBy_CustomComparison()
{
var expected = Enumerable.Range(5, 5);
var actual = Enumerable.Range(0, 10).AsQueryable().ExceptBy(Enumerable.Range(0, 5), x => x, EqualityComparer<int>.Default).ToArray();
Assert.Equal(expected, actual);
}
}
}
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq.Expressions;
using Xunit;
namespace System.Linq.Tests
......@@ -83,5 +84,50 @@ public void Intersect2()
var count = (new int[] { 0, 1, 2 }).AsQueryable().Intersect((new int[] { 1, 2, 3 }).AsQueryable(), EqualityComparer<int>.Default).Count();
Assert.Equal(2, count);
}
[Fact]
public void IntersectBy_NullSource1_ThrowsArgumentNullException()
{
IQueryable<int> source1 = null;
AssertExtensions.Throws<ArgumentNullException>("source1", () => source1.IntersectBy(Enumerable.Empty<int>(), x => x));
AssertExtensions.Throws<ArgumentNullException>("source1", () => source1.IntersectBy(Enumerable.Empty<int>(), x => x, EqualityComparer<int>.Default));
}
[Fact]
public void IntersectBy_NullSource2_ThrowsArgumentNullException()
{
IQueryable<int> source1 = Enumerable.Empty<int>().AsQueryable();
IQueryable<int> source2 = null;
AssertExtensions.Throws<ArgumentNullException>("source2", () => source1.IntersectBy(source2, x => x));
AssertExtensions.Throws<ArgumentNullException>("source2", () => source1.IntersectBy(source2, x => x, EqualityComparer<int>.Default));
}
[Fact]
public void IntersectBy_NullKeySelector_ThrowsArgumentNullException()
{
IQueryable<int> source = Enumerable.Empty<int>().AsQueryable();
Expression<Func<int, int>> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.IntersectBy(source, keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.IntersectBy(source, keySelector, EqualityComparer<int>.Default));
}
[Fact]
public void IntersectBy()
{
var expected = Enumerable.Range(5, 5);
var actual = Enumerable.Range(0, 10).AsQueryable().IntersectBy(Enumerable.Range(5, 20), x => x).ToArray();
Assert.Equal(expected, actual);
}
[Fact]
public void IntersectBy_CustomComparison()
{
var expected = Enumerable.Range(5, 5);
var actual = Enumerable.Range(0, 10).AsQueryable().IntersectBy(Enumerable.Range(5, 20), x => x, EqualityComparer<int>.Default).ToArray();
Assert.Equal(expected, actual);
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq.Expressions;
using Xunit;
......@@ -589,5 +590,53 @@ public void Max2()
var val = (new int[] { 0, 2, 1 }).AsQueryable().Max(n => n);
Assert.Equal(2, val);
}
[Fact]
public void Max_CustomComparer_NullSource_ThrowsArgumentNullException()
{
IQueryable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Max(Comparer<int>.Default));
}
[Fact]
public void Max_CustomComparer()
{
IComparer<int> comparer = Comparer<int>.Create((x, y) => -x.CompareTo(y));
IQueryable<int> source = Enumerable.Range(1, 10).AsQueryable();
Assert.Equal(1, source.Max(comparer));
}
[Fact]
public void MaxBy_NullSource_ThrowsArgumentNullException()
{
IQueryable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MaxBy(x => x));
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MaxBy(x => x, Comparer<int>.Default));
}
[Fact]
public void MaxBy_NullKeySelector_ThrowsArgumentNullException()
{
IQueryable<int> source = Enumerable.Empty<int>().AsQueryable();
Expression<Func<int, int>> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MaxBy(keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MaxBy(keySelector, Comparer<int>.Default));
}
[Fact]
public void MaxBy()
{
IQueryable<int> source = Enumerable.Range(1, 20).AsQueryable();
Assert.Equal(1, source.MaxBy(x => -x));
}
[Fact]
public void MaxBy_CustomComparer()
{
IQueryable<int> source = Enumerable.Range(1, 20).AsQueryable();
Assert.Equal(20, source.MaxBy(x => -x, Comparer<int>.Create((x, y) => -x.CompareTo(y))));
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq.Expressions;
using Xunit;
......@@ -557,5 +558,53 @@ public void Min2()
var val = (new int[] { 0, 2, 1 }).AsQueryable().Min(n => n);
Assert.Equal(0, val);
}
[Fact]
public void Min_CustomComparer_NullSource_ThrowsArgumentNullException()
{
IQueryable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Min(Comparer<int>.Default));
}
[Fact]
public void Min_CustomComparer()
{
IComparer<int> comparer = Comparer<int>.Create((x, y) => -x.CompareTo(y));
IQueryable<int> source = Enumerable.Range(1, 10).AsQueryable();
Assert.Equal(10, source.Min(comparer));
}
[Fact]
public void MinBy_NullSource_ThrowsArgumentNullException()
{
IQueryable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MinBy(x => x));
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MinBy(x => x, Comparer<int>.Default));
}
[Fact]
public void MinBy_NullKeySelector_ThrowsArgumentNullException()
{
IQueryable<int> source = Enumerable.Empty<int>().AsQueryable();
Expression<Func<int, int>> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MinBy(keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MinBy(keySelector, Comparer<int>.Default));
}
[Fact]
public void MinBy()
{
IQueryable<int> source = Enumerable.Range(1, 20).AsQueryable();
Assert.Equal(20, source.MinBy(x => -x));
}
[Fact]
public void MinBy_CustomComparer()
{
IQueryable<int> source = Enumerable.Range(1, 20).AsQueryable();
Assert.Equal(1, source.MinBy(x => -x, Comparer<int>.Create((x, y) => -x.CompareTo(y))));
}
}
}
......@@ -61,7 +61,7 @@ public static void CachedReflectionInfoMethodsNoAnnotations()
.Where(m => m.GetParameters().Length > 0);
// If you are adding a new method to this class, ensure the method meets these requirements
Assert.Equal(117, methods.Count());
Assert.Equal(131, methods.Count());
foreach (MethodInfo method in methods)
{
ParameterInfo[] parameters = method.GetParameters();
......
......@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq.Expressions;
using Xunit;
namespace System.Linq.Tests
......@@ -79,5 +80,50 @@ public void Union2()
var count = (new int[] { 0, 1, 2 }).AsQueryable().Union((new int[] { 1, 2, 3 }).AsQueryable(), EqualityComparer<int>.Default).Count();
Assert.Equal(4, count);
}
[Fact]
public void UnionBy_NullSource1_ThrowsArgumentNullException()
{
IQueryable<int> source1 = null;
AssertExtensions.Throws<ArgumentNullException>("source1", () => source1.UnionBy(Enumerable.Empty<int>(), x => x));
AssertExtensions.Throws<ArgumentNullException>("source1", () => source1.UnionBy(Enumerable.Empty<int>(), x => x, EqualityComparer<int>.Default));
}
[Fact]
public void UnionBy_NullSource2_ThrowsArgumentNullException()
{
IQueryable<int> source1 = Enumerable.Empty<int>().AsQueryable();
IQueryable<int> source2 = null;
AssertExtensions.Throws<ArgumentNullException>("source2", () => source1.UnionBy(source2, x => x));
AssertExtensions.Throws<ArgumentNullException>("source2", () => source1.UnionBy(source2, x => x, EqualityComparer<int>.Default));
}
[Fact]
public void UnionBy_NullKeySelector_ThrowsArgumentNullException()
{
IQueryable<int> source = Enumerable.Empty<int>().AsQueryable();
Expression<Func<int, int>> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.UnionBy(source, keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.UnionBy(source, keySelector, EqualityComparer<int>.Default));
}
[Fact]
public void UnionBy()
{
var expected = Enumerable.Range(0, 10);
var actual = Enumerable.Range(0, 5).AsQueryable().UnionBy(Enumerable.Range(5, 5), x => x).ToArray();
Assert.Equal(expected, actual);
}
[Fact]
public void UnionBy_CustomComparison()
{
var expected = Enumerable.Range(0, 10);
var actual = Enumerable.Range(0, 5).AsQueryable().UnionBy(Enumerable.Range(5, 5), x => x, EqualityComparer<int>.Default).ToArray();
Assert.Equal(expected, actual);
}
}
}
......@@ -49,13 +49,17 @@ public static partial class Enumerable
public static int Count<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource?> DefaultIfEmpty<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> DefaultIfEmpty<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, TSource defaultValue) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> DistinctBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> DistinctBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Distinct<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Distinct<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static TSource? ElementAtOrDefault<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, int index) { throw null; }
public static TSource? ElementAtOrDefault<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Index index) { throw null; }
public static TSource ElementAt<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, int index) { throw null; }
public static TSource? ElementAtOrDefault<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, int index) { throw null; }
public static TSource ElementAt<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Index index) { throw null; }
public static TSource ElementAt<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, int index) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Empty<TResult>() { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> ExceptBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TKey> second, System.Func<TSource, TKey> keySelector) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> ExceptBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TKey> second, System.Func<TSource, TKey> keySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Except<TSource>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Except<TSource>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static TSource? FirstOrDefault<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
......@@ -74,6 +78,8 @@ public static partial class Enumerable
public static System.Collections.Generic.IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Func<TSource, TElement> elementSelector, System.Func<TKey, System.Collections.Generic.IEnumerable<TElement>, TResult> resultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this System.Collections.Generic.IEnumerable<TOuter> outer, System.Collections.Generic.IEnumerable<TInner> inner, System.Func<TOuter, TKey> outerKeySelector, System.Func<TInner, TKey> innerKeySelector, System.Func<TOuter, System.Collections.Generic.IEnumerable<TInner>, TResult> resultSelector) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(this System.Collections.Generic.IEnumerable<TOuter> outer, System.Collections.Generic.IEnumerable<TInner> inner, System.Func<TOuter, TKey> outerKeySelector, System.Func<TInner, TKey> innerKeySelector, System.Func<TOuter, System.Collections.Generic.IEnumerable<TInner>, TResult> resultSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> IntersectBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TKey> second, System.Func<TSource, TKey> keySelector) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> IntersectBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TKey> second, System.Func<TSource, TKey> keySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Intersect<TSource>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Intersect<TSource>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this System.Collections.Generic.IEnumerable<TOuter> outer, System.Collections.Generic.IEnumerable<TInner> inner, System.Func<TOuter, TKey> outerKeySelector, System.Func<TInner, TKey> innerKeySelector, System.Func<TOuter, TInner, TResult> resultSelector) { throw null; }
......@@ -96,7 +102,10 @@ public static partial class Enumerable
public static long? Max(this System.Collections.Generic.IEnumerable<long?> source) { throw null; }
public static float? Max(this System.Collections.Generic.IEnumerable<float?> source) { throw null; }
public static float Max(this System.Collections.Generic.IEnumerable<float> source) { throw null; }
public static TSource? MaxBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector) { throw null; }
public static TSource? MaxBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Collections.Generic.IComparer<TKey>? comparer) { throw null; }
public static TSource? Max<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
public static TSource? Max<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
public static decimal Max<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, decimal> selector) { throw null; }
public static double Max<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, double> selector) { throw null; }
public static int Max<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int> selector) { throw null; }
......@@ -118,7 +127,10 @@ public static partial class Enumerable
public static long? Min(this System.Collections.Generic.IEnumerable<long?> source) { throw null; }
public static float? Min(this System.Collections.Generic.IEnumerable<float?> source) { throw null; }
public static float Min(this System.Collections.Generic.IEnumerable<float> source) { throw null; }
public static TSource? MinBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector) { throw null; }
public static TSource? MinBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Collections.Generic.IComparer<TKey>? comparer) { throw null; }
public static TSource? Min<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
public static TSource? Min<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Collections.Generic.IComparer<TSource>? comparer) { throw null; }
public static decimal Min<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, decimal> selector) { throw null; }
public static double Min<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, double> selector) { throw null; }
public static int Min<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int> selector) { throw null; }
......@@ -199,6 +211,8 @@ public static partial class Enumerable
public static System.Linq.ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Func<TSource, TElement> elementSelector) { throw null; }
public static System.Linq.ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Func<TSource, TElement> elementSelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static bool TryGetNonEnumeratedCount<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, out int count) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> UnionBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second, System.Func<TSource, TKey> keySelector) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> UnionBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second, System.Func<TSource, TKey> keySelector, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Union<TSource>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Union<TSource>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
......
......@@ -20,6 +20,41 @@ public static IEnumerable<TSource> Distinct<TSource>(this IEnumerable<TSource> s
return new DistinctIterator<TSource>(source, comparer);
}
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => DistinctBy(source, keySelector, null);
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
if (source is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
if (keySelector is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector);
}
return DistinctByIterator(source, keySelector, comparer);
}
private static IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
using IEnumerator<TSource> enumerator = source.GetEnumerator();
if (enumerator.MoveNext())
{
var set = new HashSet<TKey>(DefaultInternalSetCapacity, comparer);
do
{
TSource element = enumerator.Current;
if (set.Add(keySelector(element)))
{
yield return element;
}
}
while (enumerator.MoveNext());
}
}
/// <summary>
/// An iterator that yields the distinct values in an <see cref="IEnumerable{TSource}"/>.
/// </summary>
......
......@@ -37,6 +37,26 @@ public static IEnumerable<TSource> Except<TSource>(this IEnumerable<TSource> fir
return ExceptIterator(first, second, comparer);
}
public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector) => ExceptBy(first, second, keySelector, null);
public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}
if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}
if (keySelector is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector);
}
return ExceptByIterator(first, second, keySelector, comparer);
}
private static IEnumerable<TSource> ExceptIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource>? comparer)
{
var set = new HashSet<TSource>(second, comparer);
......@@ -49,5 +69,18 @@ private static IEnumerable<TSource> ExceptIterator<TSource>(IEnumerable<TSource>
}
}
}
private static IEnumerable<TSource> ExceptByIterator<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
var set = new HashSet<TKey>(second, comparer);
foreach (TSource element in first)
{
if (set.Add(keySelector(element)))
{
yield return element;
}
}
}
}
}
......@@ -7,7 +7,9 @@ namespace System.Linq
{
public static partial class Enumerable
{
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) => Intersect(first, second, null);
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource>? comparer)
{
if (first == null)
{
......@@ -19,22 +21,27 @@ public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource>
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}
return IntersectIterator(first, second, null);
return IntersectIterator(first, second, comparer);
}
public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource>? comparer)
public static IEnumerable<TSource> IntersectBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector) => IntersectBy(first, second, keySelector, null);
public static IEnumerable<TSource> IntersectBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
if (first == null)
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}
if (second == null)
if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}
if (keySelector is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector);
}
return IntersectIterator(first, second, comparer);
return IntersectByIterator(first, second, keySelector, comparer);
}
private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource>? comparer)
......@@ -49,5 +56,18 @@ private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSour
}
}
}
private static IEnumerable<TSource> IntersectByIterator<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
var set = new HashSet<TKey>(second, comparer);
foreach (TSource element in first)
{
if (set.Remove(keySelector(element)))
{
yield return element;
}
}
}
}
}
......@@ -441,13 +441,16 @@ public static decimal Max(this IEnumerable<decimal> source)
return value;
}
public static TSource? Max<TSource>(this IEnumerable<TSource> source)
public static TSource? Max<TSource>(this IEnumerable<TSource> source) => Max(source, comparer: null);
public static TSource? Max<TSource>(this IEnumerable<TSource> source, IComparer<TSource>? comparer)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
comparer ??= Comparer<TSource>.Default;
TSource? value = default;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
......@@ -464,13 +467,12 @@ public static decimal Max(this IEnumerable<decimal> source)
}
while (value == null);
Comparer<TSource> comparer = Comparer<TSource>.Default;
while (e.MoveNext())
{
TSource x = e.Current;
if (x != null && comparer.Compare(x, value) > 0)
TSource next = e.Current;
if (next != null && comparer.Compare(next, value) > 0)
{
value = x;
value = next;
}
}
}
......@@ -482,12 +484,111 @@ public static decimal Max(this IEnumerable<decimal> source)
}
value = e.Current;
if (comparer == Comparer<TSource>.Default)
{
while (e.MoveNext())
{
TSource next = e.Current;
if (Comparer<TSource>.Default.Compare(next, value) > 0)
{
value = next;
}
}
}
else
{
while (e.MoveNext())
{
TSource next = e.Current;
if (comparer.Compare(next, value) > 0)
{
value = next;
}
}
}
}
}
return value;
}
public static TSource? MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MaxBy(source, keySelector, null);
public static TSource? MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey>? comparer)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
if (keySelector == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector);
}
comparer ??= Comparer<TKey>.Default;
TKey? key = default;
TSource? value = default;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
if (key == null)
{
do
{
if (!e.MoveNext())
{
return value;
}
value = e.Current;
key = keySelector(value);
}
while (key == null);
while (e.MoveNext())
{
TSource x = e.Current;
if (Comparer<TSource>.Default.Compare(x, value) > 0)
TSource nextValue = e.Current;
TKey nextKey = keySelector(nextValue);
if (nextKey != null && comparer.Compare(nextKey, key) > 0)
{
value = x;
key = nextKey;
value = nextValue;
}
}
}
else
{
if (!e.MoveNext())
{
ThrowHelper.ThrowNoElementsException();
}
value = e.Current;
key = keySelector(value);
if (comparer == Comparer<TSource>.Default)
{
while (e.MoveNext())
{
TSource nextValue = e.Current;
TKey nextKey = keySelector(nextValue);
if (Comparer<TKey>.Default.Compare(nextKey, key) > 0)
{
key = nextKey;
value = nextValue;
}
}
}
else
{
while (e.MoveNext())
{
TSource nextValue = e.Current;
TKey nextKey = keySelector(nextValue);
if (comparer.Compare(nextKey, key) > 0)
{
key = nextKey;
value = nextValue;
}
}
}
}
......
......@@ -399,13 +399,16 @@ public static decimal Min(this IEnumerable<decimal> source)
return value;
}
public static TSource? Min<TSource>(this IEnumerable<TSource> source)
public static TSource? Min<TSource>(this IEnumerable<TSource> source) => Min(source, comparer: null);
public static TSource? Min<TSource>(this IEnumerable<TSource> source, IComparer<TSource>? comparer)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
comparer ??= Comparer<TSource>.Default;
TSource? value = default;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
......@@ -422,13 +425,12 @@ public static decimal Min(this IEnumerable<decimal> source)
}
while (value == null);
Comparer<TSource> comparer = Comparer<TSource>.Default;
while (e.MoveNext())
{
TSource x = e.Current;
if (x != null && comparer.Compare(x, value) < 0)
TSource next = e.Current;
if (next != null && comparer.Compare(next, value) < 0)
{
value = x;
value = next;
}
}
}
......@@ -440,12 +442,111 @@ public static decimal Min(this IEnumerable<decimal> source)
}
value = e.Current;
if (comparer == Comparer<TSource>.Default)
{
while (e.MoveNext())
{
TSource next = e.Current;
if (Comparer<TSource>.Default.Compare(next, value) < 0)
{
value = next;
}
}
}
else
{
while (e.MoveNext())
{
TSource next = e.Current;
if (comparer.Compare(next, value) < 0)
{
value = next;
}
}
}
}
}
return value;
}
public static TSource? MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MinBy(source, keySelector, comparer: null);
public static TSource? MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey>? comparer)
{
if (source == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
}
if (keySelector == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector);
}
comparer ??= Comparer<TKey>.Default;
TKey? key = default;
TSource? value = default;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
if (key == null)
{
do
{
if (!e.MoveNext())
{
return value;
}
value = e.Current;
key = keySelector(value);
}
while (key == null);
while (e.MoveNext())
{
TSource x = e.Current;
if (Comparer<TSource>.Default.Compare(x, value) < 0)
TSource nextValue = e.Current;
TKey nextKey = keySelector(nextValue);
if (nextKey != null && comparer.Compare(nextKey, key) < 0)
{
value = x;
key = nextKey;
value = nextValue;
}
}
}
else
{
if (!e.MoveNext())
{
ThrowHelper.ThrowNoElementsException();
}
value = e.Current;
key = keySelector(value);
if (comparer == Comparer<TKey>.Default)
{
while (e.MoveNext())
{
TSource nextValue = e.Current;
TKey nextKey = keySelector(nextValue);
if (Comparer<TKey>.Default.Compare(nextKey, key) < 0)
{
key = nextKey;
value = nextValue;
}
}
}
else
{
while (e.MoveNext())
{
TSource nextValue = e.Current;
TKey nextKey = keySelector(nextValue);
if (comparer.Compare(nextKey, key) < 0)
{
key = nextKey;
value = nextValue;
}
}
}
}
......
......@@ -26,6 +26,47 @@ public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> firs
return first is UnionIterator<TSource> union && AreEqualityComparersEqual(comparer, union._comparer) ? union.Union(second) : new UnionIterator2<TSource>(first, second, comparer);
}
public static IEnumerable<TSource> UnionBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector) => UnionBy(first, second, keySelector, null);
public static IEnumerable<TSource> UnionBy<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}
if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}
if (keySelector is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keySelector);
}
return UnionByIterator(first, second, keySelector, comparer);
}
private static IEnumerable<TSource> UnionByIterator<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
var set = new HashSet<TKey>(DefaultInternalSetCapacity, comparer);
foreach (TSource element in first)
{
if (set.Add(keySelector(element)))
{
yield return element;
}
}
foreach (TSource element in second)
{
if (set.Add(keySelector(element)))
{
yield return element;
}
}
}
/// <summary>
/// An iterator that yields distinct values from two or more <see cref="IEnumerable{TSource}"/>.
/// </summary>
......
......@@ -267,5 +267,104 @@ public void RepeatEnumerating()
Assert.Equal(result, result);
}
[Fact]
public void DistinctBy_SourceNull_ThrowsArgumentNullException()
{
string[] first = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => first.DistinctBy(x => x));
AssertExtensions.Throws<ArgumentNullException>("source", () => first.DistinctBy(x => x, new AnagramEqualityComparer()));
}
[Fact]
public void DistinctBy_KeySelectorNull_ThrowsArgumentNullException()
{
string[] source = { "Bob", "Tim", "Robert", "Chris" };
Func<string, string> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.DistinctBy(keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.DistinctBy(keySelector, new AnagramEqualityComparer()));
}
[Theory]
[MemberData(nameof(DistinctBy_TestData))]
public static void DistinctBy_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
{
Assert.Equal(expected, source.DistinctBy(keySelector, comparer));
}
[Theory]
[MemberData(nameof(DistinctBy_TestData))]
public static void DistinctBy_RunOnce_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
{
Assert.Equal(expected, source.RunOnce().DistinctBy(keySelector, comparer));
}
public static IEnumerable<object[]> DistinctBy_TestData()
{
yield return WrapArgs(
source: Enumerable.Range(0, 10),
keySelector: x => x,
comparer: null,
expected: Enumerable.Range(0, 10));
yield return WrapArgs(
source: Enumerable.Range(5, 10),
keySelector: x => true,
comparer: null,
expected: new int[] { 5 });
yield return WrapArgs(
source: Enumerable.Range(0, 20),
keySelector: x => x % 5,
comparer: null,
expected: Enumerable.Range(0, 5));
yield return WrapArgs(
source: Enumerable.Repeat(5, 20),
keySelector: x => x,
comparer: null,
expected: Enumerable.Repeat(5, 1));
yield return WrapArgs(
source: new string[] { "Bob", "bob", "tim", "Bob", "Tim" },
keySelector: x => x,
null,
expected: new string[] { "Bob", "bob", "tim", "Tim" });
yield return WrapArgs(
source: new string[] { "Bob", "bob", "tim", "Bob", "Tim" },
keySelector: x => x,
StringComparer.OrdinalIgnoreCase,
expected: new string[] { "Bob", "tim" });
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) },
keySelector: x => x.Age,
comparer: null,
expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) });
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 20), ("Harry", 40) },
keySelector: x => x.Age,
comparer: null,
expected: new (string Name, int Age)[] { ("Tom", 20), ("Harry", 40) });
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Bob", 20), ("bob", 30), ("Harry", 40) },
keySelector: x => x.Name,
comparer: null,
expected: new (string Name, int Age)[] { ("Bob", 20), ("bob", 30), ("Harry", 40) });
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Bob", 20), ("bob", 30), ("Harry", 40) },
keySelector: x => x.Name,
comparer: StringComparer.OrdinalIgnoreCase,
expected: new (string Name, int Age)[] { ("Bob", 20), ("Harry", 40) });
object[] WrapArgs<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
=> new object[] { source, keySelector, comparer, expected };
}
}
}
......@@ -27,9 +27,6 @@ public void SameResultsRepeatCallsStringQuery()
var q2 = from x2 in new[] { "!@#$%^", "C", "AAA", "", "Calling Twice", "SoS" }
select x2;
var rst1 = q1.Except(q2);
var rst2 = q1.Except(q2);
Assert.Equal(q1.Except(q2), q1.Except(q2));
}
......@@ -141,5 +138,112 @@ public void HashSetWithBuiltInComparer_HashSetContainsNotUsed()
Assert.Equal(new[] { "A" }, input2.Except(input1, EqualityComparer<string>.Default));
Assert.Equal(Enumerable.Empty<string>(), input2.Except(input1, StringComparer.OrdinalIgnoreCase));
}
[Fact]
public void ExceptBy_FirstNull_ThrowsArgumentNullException()
{
string[] first = null;
string[] second = { "bBo", "shriC" };
AssertExtensions.Throws<ArgumentNullException>("first", () => first.ExceptBy(second, x => x));
AssertExtensions.Throws<ArgumentNullException>("first", () => first.ExceptBy(second, x => x, new AnagramEqualityComparer()));
}
[Fact]
public void ExceptBy_SecondNull_ThrowsArgumentNullException()
{
string[] first = { "Bob", "Tim", "Robert", "Chris" };
string[] second = null;
AssertExtensions.Throws<ArgumentNullException>("second", () => first.ExceptBy(second, x => x));
AssertExtensions.Throws<ArgumentNullException>("second", () => first.ExceptBy(second, x => x, new AnagramEqualityComparer()));
}
[Fact]
public void ExceptBy_KeySelectorNull_ThrowsArgumentNullException()
{
string[] first = { "Bob", "Tim", "Robert", "Chris" };
string[] second = { "bBo", "shriC" };
Func<string, string> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => first.ExceptBy(second, keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => first.ExceptBy(second, keySelector, new AnagramEqualityComparer()));
}
[Theory]
[MemberData(nameof(ExceptBy_TestData))]
public static void ExceptBy_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
{
Assert.Equal(expected, first.ExceptBy(second, keySelector, comparer));
}
[Theory]
[MemberData(nameof(ExceptBy_TestData))]
public static void ExceptBy_RunOnce_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
{
Assert.Equal(expected, first.RunOnce().ExceptBy(second.RunOnce(), keySelector, comparer));
}
public static IEnumerable<object[]> ExceptBy_TestData()
{
yield return WrapArgs(
first: Enumerable.Range(0, 10),
second: Enumerable.Range(0, 5),
keySelector: x => x,
comparer: null,
expected: Enumerable.Range(5, 5));
yield return WrapArgs(
first: Enumerable.Repeat(5, 20),
second: Enumerable.Empty<int>(),
keySelector: x => x,
comparer: null,
expected: Enumerable.Repeat(5, 1));
yield return WrapArgs(
first: Enumerable.Repeat(5, 20),
second: Enumerable.Repeat(5, 3),
keySelector: x => x,
comparer: null,
expected: Enumerable.Empty<int>());
yield return WrapArgs(
first: new string[] { "Bob", "Tim", "Robert", "Chris" },
second: new string[] { "bBo", "shriC" },
keySelector: x => x,
null,
expected: new string[] { "Bob", "Tim", "Robert", "Chris" });
yield return WrapArgs(
first: new string[] { "Bob", "Tim", "Robert", "Chris" },
second: new string[] { "bBo", "shriC" },
keySelector: x => x,
new AnagramEqualityComparer(),
expected: new string[] { "Tim", "Robert" });
yield return WrapArgs(
first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) },
second: new int[] { 15, 20, 40 },
keySelector: x => x.Age,
comparer: null,
expected: new (string Name, int Age)[] { ("Dick", 30) });
yield return WrapArgs(
first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) },
second: new string[] { "moT" },
keySelector: x => x.Name,
comparer: null,
expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) });
yield return WrapArgs(
first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) },
second: new string[] { "moT" },
keySelector: x => x.Name,
comparer: new AnagramEqualityComparer(),
expected: new (string Name, int Age)[] { ("Dick", 30), ("Harry", 40) });
object[] WrapArgs<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
=> new object[] { first, second, keySelector, comparer, expected };
}
}
}
......@@ -135,5 +135,119 @@ public void HashSetWithBuiltInComparer_HashSetContainsNotUsed()
Assert.Equal(Enumerable.Empty<string>(), input2.Intersect(input1, EqualityComparer<string>.Default));
Assert.Equal(new[] { "A" }, input2.Intersect(input1, StringComparer.OrdinalIgnoreCase));
}
[Fact]
public void IntersectBy_FirstNull_ThrowsArgumentNullException()
{
string[] first = null;
string[] second = { "bBo", "shriC" };
AssertExtensions.Throws<ArgumentNullException>("first", () => first.IntersectBy(second, x => x));
AssertExtensions.Throws<ArgumentNullException>("first", () => first.IntersectBy(second, x => x, new AnagramEqualityComparer()));
}
[Fact]
public void IntersectBy_SecondNull_ThrowsArgumentNullException()
{
string[] first = { "Bob", "Tim", "Robert", "Chris" };
string[] second = null;
AssertExtensions.Throws<ArgumentNullException>("second", () => first.IntersectBy(second, x => x));
AssertExtensions.Throws<ArgumentNullException>("second", () => first.IntersectBy(second, x => x, new AnagramEqualityComparer()));
}
[Fact]
public void IntersectBy_KeySelectorNull_ThrowsArgumentNullException()
{
string[] first = { "Bob", "Tim", "Robert", "Chris" };
string[] second = { "bBo", "shriC" };
Func<string, string> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => first.IntersectBy(second, keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => first.IntersectBy(second, keySelector, new AnagramEqualityComparer()));
}
[Theory]
[MemberData(nameof(IntersectBy_TestData))]
public static void IntersectBy_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
{
Assert.Equal(expected, first.IntersectBy(second, keySelector, comparer));
}
[Theory]
[MemberData(nameof(IntersectBy_TestData))]
public static void IntersectBy_RunOnce_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
{
Assert.Equal(expected, first.RunOnce().IntersectBy(second.RunOnce(), keySelector, comparer));
}
public static IEnumerable<object[]> IntersectBy_TestData()
{
yield return WrapArgs(
first: Enumerable.Range(0, 10),
second: Enumerable.Range(0, 5),
keySelector: x => x,
comparer: null,
expected: Enumerable.Range(0, 5));
yield return WrapArgs(
first: Enumerable.Range(0, 10),
second: Enumerable.Range(10, 10),
keySelector: x => x,
comparer: null,
expected: Enumerable.Empty<int>());
yield return WrapArgs(
first: Enumerable.Repeat(5, 20),
second: Enumerable.Empty<int>(),
keySelector: x => x,
comparer: null,
expected: Enumerable.Empty<int>());
yield return WrapArgs(
first: Enumerable.Repeat(5, 20),
second: Enumerable.Repeat(5, 3),
keySelector: x => x,
comparer: null,
expected: Enumerable.Repeat(5, 1));
yield return WrapArgs(
first: new string[] { "Bob", "Tim", "Robert", "Chris" },
second: new string[] { "bBo", "shriC" },
keySelector: x => x,
null,
expected: Array.Empty<string>());
yield return WrapArgs(
first: new string[] { "Bob", "Tim", "Robert", "Chris" },
second: new string[] { "bBo", "shriC" },
keySelector: x => x,
new AnagramEqualityComparer(),
expected: new string[] { "Bob", "Chris" });
yield return WrapArgs(
first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) },
second: new int[] { 15, 20, 40 },
keySelector: x => x.Age,
comparer: null,
expected: new (string Name, int Age)[] { ("Tom", 20), ("Harry", 40) });
yield return WrapArgs(
first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) },
second: new string[] { "moT" },
keySelector: x => x.Name,
comparer: null,
expected: Array.Empty<(string Name, int Age)>());
yield return WrapArgs(
first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40) },
second: new string[] { "moT" },
keySelector: x => x.Name,
comparer: new AnagramEqualityComparer(),
expected: new (string Name, int Age)[] { ("Tom", 20) });
object[] WrapArgs<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TKey> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
=> new object[] { first, second, keySelector, comparer, expected };
}
}
}
......@@ -770,5 +770,193 @@ public void Max_Boolean_EmptySource_ThrowsInvalidOperationException()
{
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<bool>().Max());
}
[Fact]
public static void Max_Generic_NullSource_ThrowsArgumentNullException()
{
IEnumerable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Max());
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Max(comparer: null));
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Max(Comparer<int>.Create((_, _) => 0)));
}
[Fact]
public static void Max_Generic_EmptyStructSource_ThrowsInvalidOperationException()
{
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Max());
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Max(comparer: null));
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Max(Comparer<int>.Create((_,_) => 0)));
}
[Theory]
[MemberData(nameof(Max_Generic_TestData))]
public static void Max_Generic_HasExpectedOutput<TSource>(IEnumerable<TSource> source, IComparer<TSource>? comparer, TSource? expected)
{
Assert.Equal(expected, source.Max(comparer));
}
[Theory]
[MemberData(nameof(Max_Generic_TestData))]
public static void Max_Generic_RunOnce_HasExpectedOutput<TSource>(IEnumerable<TSource> source, IComparer<TSource>? comparer, TSource? expected)
{
Assert.Equal(expected, source.RunOnce().Max(comparer));
}
public static IEnumerable<object[]> Max_Generic_TestData()
{
yield return WrapArgs(
source: Enumerable.Empty<int?>(),
comparer: null,
expected: null);
yield return WrapArgs(
source: Enumerable.Empty<int?>(),
comparer: Comparer<int?>.Create((_,_) => 0),
expected: null);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
comparer: null,
expected: 9);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
comparer: Comparer<int>.Create((x, y) => -x.CompareTo(y)),
expected: 0);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
comparer: Comparer<int>.Create((x,y) => 0),
expected: 0);
yield return WrapArgs(
source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" },
comparer: null,
expected: "Zyzzyva");
yield return WrapArgs(
source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" },
comparer: Comparer<string>.Create((x, y) => -x.CompareTo(y)),
expected: "Aardvark");
object[] WrapArgs<TSource>(IEnumerable<TSource> source, IComparer<TSource>? comparer, TSource? expected)
=> new object[] { source, comparer, expected };
}
[Fact]
public static void MaxBy_Generic_NullSource_ThrowsArgumentNullException()
{
IEnumerable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MaxBy(x => x));
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MaxBy(x => x, comparer: null));
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MaxBy(x => x, Comparer<int>.Create((_, _) => 0)));
}
[Fact]
public static void MaxBy_Generic_NullKeySelector_ThrowsArgumentNullException()
{
IEnumerable<int> source = Enumerable.Empty<int>();
Func<int, int> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MaxBy(keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MaxBy(keySelector, comparer: null));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MaxBy(keySelector, Comparer<int>.Create((_, _) => 0)));
}
[Fact]
public static void MaxBy_Generic_EmptyStructSource_ThrowsInvalidOperationException()
{
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().MaxBy(x => x));
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().MaxBy(x => x, comparer: null));
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().MaxBy(x => x, Comparer<int>.Create((_, _) => 0)));
}
[Theory]
[MemberData(nameof(MaxBy_Generic_TestData))]
public static void MaxBy_Generic_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey>? comparer, TSource? expected)
{
Assert.Equal(expected, source.MaxBy(keySelector, comparer));
}
[Theory]
[MemberData(nameof(MaxBy_Generic_TestData))]
public static void MaxBy_Generic_RunOnce_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey>? comparer, TSource? expected)
{
Assert.Equal(expected, source.RunOnce().MaxBy(keySelector, comparer));
}
public static IEnumerable<object[]> MaxBy_Generic_TestData()
{
yield return WrapArgs(
source: Enumerable.Empty<int?>(),
keySelector: x => x,
comparer: null,
expected: null);
yield return WrapArgs(
source: Enumerable.Empty<int?>(),
keySelector: x => x,
comparer: Comparer<int?>.Create((_, _) => 0),
expected: null);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
keySelector: x => x,
comparer: null,
expected: 9);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
keySelector: x => x,
comparer: Comparer<int>.Create((x, y) => -x.CompareTo(y)),
expected: 0);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
keySelector: x => x,
comparer: Comparer<int>.Create((x, y) => 0),
expected: 0);
yield return WrapArgs(
source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" },
keySelector: x => x,
comparer: null,
expected: "Zyzzyva");
yield return WrapArgs(
source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" },
keySelector: x => x,
comparer: Comparer<string>.Create((x, y) => -x.CompareTo(y)),
expected: "Aardvark");
yield return WrapArgs(
source: new (string Name, int Age) [] { ("Tom", 43), ("Dick", 55), ("Harry", 20) },
keySelector: x => x.Age,
comparer: null,
expected: (Name: "Dick", Age: 55));
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) },
keySelector: x => x.Age,
comparer: Comparer<int>.Create((x, y) => -x.CompareTo(y)),
expected: (Name: "Harry", Age: 20));
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) },
keySelector: x => x.Name,
comparer: null,
expected: (Name: "Tom", Age: 43));
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) },
keySelector: x => x.Name,
comparer: Comparer<string>.Create((x, y) => -x.CompareTo(y)),
expected: (Name: "Dick", Age: 55));
object[] WrapArgs<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey>? comparer, TSource? expected)
=> new object[] { source, keySelector, comparer, expected };
}
}
}
......@@ -748,5 +748,193 @@ public void Min_Bool_EmptySource_ThrowsInvalodOperationException()
{
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<bool>().Min());
}
[Fact]
public static void Min_Generic_NullSource_ThrowsArgumentNullException()
{
IEnumerable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Min());
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Min(comparer: null));
AssertExtensions.Throws<ArgumentNullException>("source", () => source.Min(Comparer<int>.Create((_, _) => 0)));
}
[Fact]
public static void Min_Generic_EmptyStructSource_ThrowsInvalidOperationException()
{
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Min());
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Min(comparer: null));
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Min(Comparer<int>.Create((_, _) => 0)));
}
[Theory]
[MemberData(nameof(Min_Generic_TestData))]
public static void Min_Generic_HasExpectedOutput<TSource>(IEnumerable<TSource> source, IComparer<TSource>? comparer, TSource? expected)
{
Assert.Equal(expected, source.Min(comparer));
}
[Theory]
[MemberData(nameof(Min_Generic_TestData))]
public static void Min_Generic_RunOnce_HasExpectedOutput<TSource>(IEnumerable<TSource> source, IComparer<TSource>? comparer, TSource? expected)
{
Assert.Equal(expected, source.RunOnce().Min(comparer));
}
public static IEnumerable<object[]> Min_Generic_TestData()
{
yield return WrapArgs(
source: Enumerable.Empty<int?>(),
comparer: null,
expected: null);
yield return WrapArgs(
source: Enumerable.Empty<int?>(),
comparer: Comparer<int?>.Create((_, _) => 0),
expected: null);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
comparer: null,
expected: 0);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
comparer: Comparer<int>.Create((x, y) => -x.CompareTo(y)),
expected: 9);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
comparer: Comparer<int>.Create((x, y) => 0),
expected: 0);
yield return WrapArgs(
source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" },
comparer: null,
expected: "Aardvark");
yield return WrapArgs(
source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" },
comparer: Comparer<string>.Create((x, y) => -x.CompareTo(y)),
expected: "Zyzzyva");
object[] WrapArgs<TSource>(IEnumerable<TSource> source, IComparer<TSource>? comparer, TSource? expected)
=> new object[] { source, comparer, expected };
}
[Fact]
public static void MinBy_Generic_NullSource_ThrowsArgumentNullException()
{
IEnumerable<int> source = null;
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MinBy(x => x));
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MinBy(x => x, comparer: null));
AssertExtensions.Throws<ArgumentNullException>("source", () => source.MinBy(x => x, Comparer<int>.Create((_, _) => 0)));
}
[Fact]
public static void MinBy_Generic_NullKeySelector_ThrowsArgumentNullException()
{
IEnumerable<int> source = Enumerable.Empty<int>();
Func<int, int> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MinBy(keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MinBy(keySelector, comparer: null));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => source.MinBy(keySelector, Comparer<int>.Create((_, _) => 0)));
}
[Fact]
public static void MinBy_Generic_EmptyStructSource_ThrowsInvalidOperationException()
{
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().MinBy(x => x));
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().MinBy(x => x, comparer: null));
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().MinBy(x => x, Comparer<int>.Create((_, _) => 0)));
}
[Theory]
[MemberData(nameof(MinBy_Generic_TestData))]
public static void MinBy_Generic_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey>? comparer, TSource? expected)
{
Assert.Equal(expected, source.MinBy(keySelector, comparer));
}
[Theory]
[MemberData(nameof(MinBy_Generic_TestData))]
public static void MinBy_Generic_RunOnce_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey>? comparer, TSource? expected)
{
Assert.Equal(expected, source.RunOnce().MinBy(keySelector, comparer));
}
public static IEnumerable<object[]> MinBy_Generic_TestData()
{
yield return WrapArgs(
source: Enumerable.Empty<int?>(),
keySelector: x => x,
comparer: null,
expected: null);
yield return WrapArgs(
source: Enumerable.Empty<int?>(),
keySelector: x => x,
comparer: Comparer<int?>.Create((_, _) => 0),
expected: null);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
keySelector: x => x,
comparer: null,
expected: 0);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
keySelector: x => x,
comparer: Comparer<int>.Create((x, y) => -x.CompareTo(y)),
expected: 9);
yield return WrapArgs(
source: Enumerable.Range(0, 10),
keySelector: x => x,
comparer: Comparer<int>.Create((x, y) => 0),
expected: 0);
yield return WrapArgs(
source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" },
keySelector: x => x,
comparer: null,
expected: "Aardvark");
yield return WrapArgs(
source: new string[] { "Aardvark", "Zyzzyva", "Zebra", "Antelope" },
keySelector: x => x,
comparer: Comparer<string>.Create((x, y) => -x.CompareTo(y)),
expected: "Zyzzyva");
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) },
keySelector: x => x.Age,
comparer: null,
expected: (Name: "Harry", Age: 20));
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) },
keySelector: x => x.Age,
comparer: Comparer<int>.Create((x, y) => -x.CompareTo(y)),
expected: (Name: "Dick", Age: 55));
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) },
keySelector: x => x.Name,
comparer: null,
expected: (Name: "Dick", Age: 55));
yield return WrapArgs(
source: new (string Name, int Age)[] { ("Tom", 43), ("Dick", 55), ("Harry", 20) },
keySelector: x => x.Name,
comparer: Comparer<string>.Create((x, y) => -x.CompareTo(y)),
expected: (Name: "Tom", Age: 43));
object[] WrapArgs<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey>? comparer, TSource? expected)
=> new object[] { source, keySelector, comparer, expected };
}
}
}
......@@ -413,5 +413,126 @@ public void HashSetWithBuiltInComparer_HashSetContainsNotUsed()
Assert.Equal(new[] { "A", "a" }, input2.Union(input1, EqualityComparer<string>.Default));
Assert.Equal(new[] { "A" }, input2.Union(input1, StringComparer.OrdinalIgnoreCase));
}
[Fact]
public void UnionBy_FirstNull_ThrowsArgumentNullException()
{
string[] first = null;
string[] second = { "bBo", "shriC" };
AssertExtensions.Throws<ArgumentNullException>("first", () => first.UnionBy(second, x => x));
AssertExtensions.Throws<ArgumentNullException>("first", () => first.UnionBy(second, x => x, new AnagramEqualityComparer()));
}
[Fact]
public void UnionBy_SecondNull_ThrowsArgumentNullException()
{
string[] first = { "Bob", "Tim", "Robert", "Chris" };
string[] second = null;
AssertExtensions.Throws<ArgumentNullException>("second", () => first.UnionBy(second, x => x));
AssertExtensions.Throws<ArgumentNullException>("second", () => first.UnionBy(second, x => x, new AnagramEqualityComparer()));
}
[Fact]
public void UnionBy_KeySelectorNull_ThrowsArgumentNullException()
{
string[] first = { "Bob", "Tim", "Robert", "Chris" };
string[] second = { "bBo", "shriC" };
Func<string, string> keySelector = null;
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => first.UnionBy(second, keySelector));
AssertExtensions.Throws<ArgumentNullException>("keySelector", () => first.UnionBy(second, keySelector, new AnagramEqualityComparer()));
}
[Theory]
[MemberData(nameof(UnionBy_TestData))]
public static void UnionBy_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
{
Assert.Equal(expected, first.UnionBy(second, keySelector, comparer));
}
[Theory]
[MemberData(nameof(UnionBy_TestData))]
public static void UnionBy_RunOnce_HasExpectedOutput<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
{
Assert.Equal(expected, first.RunOnce().UnionBy(second.RunOnce(), keySelector, comparer));
}
public static IEnumerable<object[]> UnionBy_TestData()
{
yield return WrapArgs(
first: Enumerable.Range(0, 7),
second: Enumerable.Range(3, 7),
keySelector: x => x,
comparer: null,
expected: Enumerable.Range(0, 10));
yield return WrapArgs(
first: Enumerable.Range(0, 10),
second: Enumerable.Range(10, 10),
keySelector: x => x,
comparer: null,
expected: Enumerable.Range(0, 20));
yield return WrapArgs(
first: Enumerable.Empty<int>(),
second: Enumerable.Range(0, 5),
keySelector: x => x,
comparer: null,
expected: Enumerable.Range(0, 5));
yield return WrapArgs(
first: Enumerable.Repeat(5, 20),
second: Enumerable.Empty<int>(),
keySelector: x => x,
comparer: null,
expected: Enumerable.Repeat(5, 1));
yield return WrapArgs(
first: Enumerable.Repeat(5, 20),
second: Enumerable.Repeat(5, 3),
keySelector: x => x,
comparer: null,
expected: Enumerable.Repeat(5, 1));
yield return WrapArgs(
first: new string[] { "Bob", "Tim", "Robert", "Chris" },
second: new string[] { "bBo", "shriC" },
keySelector: x => x,
null,
expected: new string[] { "Bob", "Tim", "Robert", "Chris", "bBo", "shriC" });
yield return WrapArgs(
first: new string[] { "Bob", "Tim", "Robert", "Chris" },
second: new string[] { "bBo", "shriC" },
keySelector: x => x,
new AnagramEqualityComparer(),
expected: new string[] { "Bob", "Tim", "Robert", "Chris" });
yield return WrapArgs(
first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20) },
second: new (string Name, int Age)[] { ("Peter", 21), ("John", 30), ("Toby", 33) },
keySelector: x => x.Age,
comparer: null,
expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Peter", 21), ("Toby", 33) });
yield return WrapArgs(
first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20) },
second: new (string Name, int Age)[] { ("Toby", 33), ("Harry", 35), ("tom", 67) },
keySelector: x => x.Name,
comparer: null,
expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20), ("Toby", 33), ("tom", 67) });
yield return WrapArgs(
first: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20) },
second: new (string Name, int Age)[] { ("Toby", 33), ("Harry", 35), ("tom", 67) },
keySelector: x => x.Name,
comparer: StringComparer.OrdinalIgnoreCase,
expected: new (string Name, int Age)[] { ("Tom", 20), ("Dick", 30), ("Harry", 40), ("Martin", 20), ("Toby", 33) });
object[] WrapArgs<TSource, TKey>(IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer, IEnumerable<TSource> expected)
=> new object[] { first, second, keySelector, comparer, expected };
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册