Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Enumerable.Concat & Enumerable.Flatten methods #61230

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public static partial class Queryable
public static System.Linq.IQueryable<TResult> Cast<TResult>(this System.Linq.IQueryable source) { throw null; }
public static System.Linq.IQueryable<TSource[]> Chunk<TSource>(this System.Linq.IQueryable<TSource> source, int size) { throw null; }
public static System.Linq.IQueryable<TSource> Concat<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2) { throw null; }
public static System.Linq.IQueryable<TSource> Concat<TSource>(this System.Linq.IQueryable<TSource> source1, System.Collections.Generic.IEnumerable<TSource> source2, params System.Collections.Generic.IEnumerable<TSource>[] rest) { throw null; }
public static bool Contains<TSource>(this System.Linq.IQueryable<TSource> source, TSource item) { throw null; }
public static bool Contains<TSource>(this System.Linq.IQueryable<TSource> source, TSource item, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static int Count<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
Expand All @@ -95,6 +96,7 @@ public static partial class Queryable
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<TSource> Flatten<TSource>(this System.Linq.IQueryable<System.Collections.Generic.IEnumerable<TSource>> sources) { 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; }
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, System.Collections.Generic.IEqualityComparer<TKey>? comparer) { throw null; }
public static System.Linq.IQueryable<System.Linq.IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector, System.Linq.Expressions.Expression<System.Func<TSource, TElement>> elementSelector) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ public static MethodInfo Concat_TSource_2(Type TSource) =>
(s_Concat_TSource_2 ??= new Func<IQueryable<object>, IEnumerable<object>, IQueryable<object>>(Queryable.Concat).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);

private static MethodInfo? s_Concat_TSource_3;

public static MethodInfo Concat_TSource_3(Type TSource) =>
(s_Concat_TSource_3 ??= new Func<IQueryable<object>, IEnumerable<object>, IEnumerable<object>[], IQueryable<object>>(Queryable.Concat).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);

private static MethodInfo? s_Contains_TSource_2;

public static MethodInfo Contains_TSource_2(Type TSource) =>
Expand Down Expand Up @@ -311,15 +317,19 @@ public static MethodInfo FirstOrDefault_TSource_2(Type TSource) =>
private static MethodInfo? s_FirstOrDefault_TSource_3;

public static MethodInfo FirstOrDefault_TSource_3(Type TSource) =>
(s_FirstOrDefault_TSource_3 ??
(s_FirstOrDefault_TSource_3 = new Func<IQueryable<object>, object, object>(Queryable.FirstOrDefault).GetMethodInfo().GetGenericMethodDefinition()))
(s_FirstOrDefault_TSource_3 ??= new Func<IQueryable<object>, object, object>(Queryable.FirstOrDefault).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);

private static MethodInfo? s_FirstOrDefault_TSource_4;

public static MethodInfo FirstOrDefault_TSource_4(Type TSource) =>
(s_FirstOrDefault_TSource_4 ??
(s_FirstOrDefault_TSource_4 = new Func<IQueryable<object>, Expression<Func<object, bool>>, object, object>(Queryable.FirstOrDefault).GetMethodInfo().GetGenericMethodDefinition()))
(s_FirstOrDefault_TSource_4 ??= new Func<IQueryable<object>, Expression<Func<object, bool>>, object, object>(Queryable.FirstOrDefault).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);

private static MethodInfo? s_Flatten_TSource_1;

public static MethodInfo Flatten_TSource_1(Type TSource) =>
(s_Flatten_TSource_1 ??= new Func<IQueryable<IEnumerable<object>>, IQueryable<object>>(Queryable.Flatten).GetMethodInfo().GetGenericMethodDefinition())
.MakeGenericMethod(TSource);

private static MethodInfo? s_GroupBy_TSource_TKey_2;
Expand Down
44 changes: 44 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,31 @@ public static IQueryable<TSource> Concat<TSource>(this IQueryable<TSource> sourc
));
}

/// <summary>
/// Concatenates two or more sequences.
/// </summary>
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
/// <param name="source1">The first sequence to concatenate.</param>
/// <param name="source2">The sequence to concatenate to the first sequence.</param>
/// <param name="rest">Any remaining sequences to concatenate.</param>
/// <returns>An <see cref="IQueryable{T}"/> that contains the concatenated elements of the input sequences.</returns>
[DynamicDependency("Concat`1", typeof(Enumerable))]
public static IQueryable<TSource> Concat<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2, params IEnumerable<TSource>[] rest)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (rest == null)
throw Error.ArgumentNull(nameof(rest));
return source1.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.Concat_TSource_3(typeof(TSource)),
source1.Expression, GetSourceExpression(source2), Expression.Constant(rest, typeof(IEnumerable<TSource>[]))
));
}

[DynamicDependency("Zip`2", typeof(Enumerable))]
public static IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2)
{
Expand Down Expand Up @@ -1141,6 +1166,25 @@ public static TSource FirstOrDefault<TSource>(this IQueryable<TSource> source, E
));
}

/// <summary>
/// Flattens a queryable of enumerables into a single concatenated queryable.
/// </summary>
/// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
/// <param name="sources">The source of queryables to flatten.</param>
/// <returns>An <see cref="IQueryable{T}"/> enumerates all inner enumerates in a concatenated view.</returns>
[DynamicDependency("Flatten`1", typeof(Enumerable))]
public static IQueryable<TSource> Flatten<TSource>(this IQueryable<IEnumerable<TSource>> sources)
{
if (sources == null)
throw Error.ArgumentNull(nameof(sources));
return sources.Provider.CreateQuery<TSource>(
Expression.Call(
null,
CachedReflectionInfo.Flatten_TSource_1(typeof(TSource)),
sources.Expression
));
}

[DynamicDependency("Last`1", typeof(Enumerable))]
public static TSource Last<TSource>(this IQueryable<TSource> source)
{
Expand Down
35 changes: 35 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/ConcatTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ public void BothEmpty()
Assert.Empty(first.AsQueryable().Concat(second.AsQueryable()));
}

[Fact]
public void AllEmpty()
{
int[] first = { };
int[] second = { };
int[] third = { };
Assert.Empty(first.AsQueryable().Concat(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void NonEmptyAndNonEmpty()
{
Expand All @@ -25,16 +34,35 @@ public void NonEmptyAndNonEmpty()
Assert.Equal(expected, first.AsQueryable().Concat(second.AsQueryable()));
}

[Fact]
public void AllNonEmpty()
{
int?[] first = { 2, null, 3, 5, 9 };
int?[] second = { null, 8, 10 };
int?[] third = { 42, null, 42 };
int?[] expected = { 2, null, 3, 5, 9, null, 8, 10, 42, null, 42 };

Assert.Equal(expected, first.AsQueryable().Concat(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void FirstNull()
{
AssertExtensions.Throws<ArgumentNullException>("source1", () => ((IQueryable<int>)null).Concat(Enumerable.Range(0, 0).AsQueryable()));
AssertExtensions.Throws<ArgumentNullException>("source1", () => ((IQueryable<int>)null).Concat(Enumerable.Range(0, 0).AsQueryable(), rest: null));
}

[Fact]
public void SecondNull()
{
AssertExtensions.Throws<ArgumentNullException>("source2", () => Enumerable.Range(0, 0).AsQueryable().Concat(null));
AssertExtensions.Throws<ArgumentNullException>("source2", () => Enumerable.Range(0, 0).AsQueryable().Concat(null, rest: null));
}

[Fact]
public void RestNull()
{
AssertExtensions.Throws<ArgumentNullException>("rest", () => Enumerable.Range(0, 0).AsQueryable().Concat(Enumerable.Range(0, 0).AsQueryable(), rest: null));
}

[Fact]
Expand All @@ -43,5 +71,12 @@ public void Concat()
var count = (new int[] { 0, 1, 2 }).AsQueryable().Concat((new int[] { 10, 11, 12 }).AsQueryable()).Count();
Assert.Equal(6, count);
}

[Fact]
public void ConcatMany()
{
var count = (new int[] { 0, 1, 2 }).AsQueryable().Concat(new int[] { 10, 11, 12 }.AsQueryable(), new int[] { 5, 6, 7 }.AsQueryable()).Count();
Assert.Equal(9, count);
}
}
}
32 changes: 32 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/FlattenTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// 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;

namespace System.Linq.Tests
{
public class FlattenTests : EnumerableBasedTests
{
[Theory]
[MemberData(nameof(FlattenHasExpectedOutput_Data))]
public void FlattenHasExpectedOutput<T>(IQueryable<IQueryable<T>> sources, IQueryable<T> expected)
{
Assert.Equal(expected, sources.Flatten());
}

public static IEnumerable<object[]> FlattenHasExpectedOutput_Data()
{
yield return Wrap(new IQueryable<int>[] { }.AsQueryable(), new int[] { }.AsQueryable());
yield return Wrap(new[] { new [] { 0 }.AsQueryable(), new [] { 1, 2, 3 }.AsQueryable() }.AsQueryable(), new [] { 0, 1, 2, 3 }.AsQueryable());
static object[] Wrap<T>(IQueryable<IQueryable<T>> sources, IQueryable<int> expected) => new object[] { sources, expected };
}

[Fact]
public void SourcesNull()
{
AssertExtensions.Throws<ArgumentNullException>("sources", () => ((IQueryable<IQueryable<int>>)null).Flatten());
}
}
}
5 changes: 5 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/Queryable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,11 @@ public bool Equals(MethodInfo a, MethodInfo b)

private Type Strip(Type t)
{
if (t.IsArray)
{
return Strip(t.GetElementType()).MakeArrayType();
}

if (t.IsGenericType)
{
Type g = t;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<Compile Include="ExceptTests.cs" />
<Compile Include="FirstOrDefaultTests.cs" />
<Compile Include="FirstTests.cs" />
<Compile Include="FlattenTests.cs" />
<Compile Include="GroupByTests.cs" />
<Compile Include="GroupJoinTests.cs" />
<Compile Include="IntersectTests.cs" />
Expand Down Expand Up @@ -55,7 +56,6 @@
<Compile Include="UnionTests.cs" />
<Compile Include="WhereTests.cs" />
<Compile Include="ZipTests.cs" />
<Compile Include="$(CommonTestPath)System\Linq\SkipTakeData.cs"
Link="Common\System\Linq\SkipTakeData.cs" />
<Compile Include="$(CommonTestPath)System\Linq\SkipTakeData.cs" Link="Common\System\Linq\SkipTakeData.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,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(131, methods.Count());
Assert.Equal(133, methods.Count());
foreach (MethodInfo method in methods)
{
ParameterInfo[] parameters = method.GetParameters();
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Linq/ref/System.Linq.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public static System.Collections.Generic.IEnumerable<
> Cast<TResult>(this System.Collections.IEnumerable source) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource[]> Chunk<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, int size) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Concat<TSource>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Concat<TSource>(this System.Collections.Generic.IEnumerable<TSource> first, System.Collections.Generic.IEnumerable<TSource> second, params System.Collections.Generic.IEnumerable<TSource>[] rest) { throw null; }
public static bool Contains<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, TSource value) { throw null; }
public static bool Contains<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, TSource value, System.Collections.Generic.IEqualityComparer<TSource>? comparer) { throw null; }
public static int Count<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
Expand All @@ -68,6 +69,7 @@ public static System.Collections.Generic.IEnumerable<
public static TSource FirstOrDefault<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate, TSource defaultValue) { throw null; }
public static TSource First<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
public static TSource First<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Flatten<TSource>(this System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<TSource>> sources) { throw null; }
public static System.Collections.Generic.IEnumerable<System.Linq.IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector) { throw null; }
public static System.Collections.Generic.IEnumerable<System.Linq.IGrouping<TKey, TSource>> GroupBy<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<System.Linq.IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, TKey> keySelector, System.Func<TSource, TElement> elementSelector) { throw null; }
Expand Down
22 changes: 8 additions & 14 deletions src/libraries/System.Linq/src/System.Linq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
<Compile Include="System\Linq\Enumerable.SizeOpt.cs" />
<Compile Include="System\Linq\Skip.SizeOpt.cs" />
<Compile Include="System\Linq\Take.SizeOpt.cs" />
<Compile Include="$(CommonPath)\System\Collections\Generic\LargeArrayBuilder.SizeOpt.cs"
Link="System\Collections\Generic\LargeArrayBuilder.SizeOpt.cs" />
<Compile Include="$(CommonPath)\System\Collections\Generic\LargeArrayBuilder.SizeOpt.cs" Link="System\Collections\Generic\LargeArrayBuilder.SizeOpt.cs" />
</ItemGroup>
<ItemGroup Condition="'$(OptimizeForSize)' != true">
<Compile Include="System\Linq\AppendPrepend.SpeedOpt.cs" />
Expand All @@ -30,20 +29,14 @@
<Compile Include="System\Linq\Take.SpeedOpt.cs" />
<Compile Include="System\Linq\Union.SpeedOpt.cs" />
<Compile Include="System\Linq\Where.SpeedOpt.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\LargeArrayBuilder.SpeedOpt.cs"
Link="System\Collections\Generic\LargeArrayBuilder.SpeedOpt.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\LargeArrayBuilder.SpeedOpt.cs" Link="System\Collections\Generic\LargeArrayBuilder.SpeedOpt.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(CommonPath)System\Collections\Generic\ArrayBuilder.cs"
Link="System\Collections\Generic\ArrayBuilder.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\EnumerableHelpers.cs"
Link="System\Collections\Generic\EnumerableHelpers.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\EnumerableHelpers.Linq.cs"
Link="System\Collections\Generic\EnumerableHelpers.Linq.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\LargeArrayBuilder.cs"
Link="System\Collections\Generic\LargeArrayBuilder.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\SparseArrayBuilder.cs"
Link="System\Collections\Generic\SparseArrayBuilder.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\ArrayBuilder.cs" Link="System\Collections\Generic\ArrayBuilder.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\EnumerableHelpers.cs" Link="System\Collections\Generic\EnumerableHelpers.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\EnumerableHelpers.Linq.cs" Link="System\Collections\Generic\EnumerableHelpers.Linq.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\LargeArrayBuilder.cs" Link="System\Collections\Generic\LargeArrayBuilder.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\SparseArrayBuilder.cs" Link="System\Collections\Generic\SparseArrayBuilder.cs" />
<Compile Include="System\Linq\Aggregate.cs" />
<Compile Include="System\Linq\AnyAll.cs" />
<Compile Include="System\Linq\AppendPrepend.cs" />
Expand All @@ -61,6 +54,7 @@
<Compile Include="System\Linq\Enumerable.cs" />
<Compile Include="System\Linq\Except.cs" />
<Compile Include="System\Linq\First.cs" />
<Compile Include="System\Linq\Flatten.cs" />
<Compile Include="System\Linq\Grouping.cs" />
<Compile Include="System\Linq\GroupJoin.cs" />
<Compile Include="System\Linq\Intersect.cs" />
Expand Down
Loading