Merged
Show file tree
Hide file tree
Changes from all 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 numberDiff line numberDiff line change
Expand Up@@ -162,6 +162,7 @@ public static partial class Queryable
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; }
public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Collections.Generic.IEnumerable<TThird> source3) { throw null; }
public static System.Linq.IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Linq.Expressions.Expression<System.Func<TFirst, TSecond, TResult>> resultSelector) { throw null; }
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -637,6 +637,12 @@
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TResult_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
Expand All@@ -662,4 +668,4 @@
<property name="Target">M:System.Linq.TypeHelper.GetStaticMethods(System.Type)</property>
</attribute>
</assembly>
</linker>
</linker>
Original file line numberDiff line numberDiff line change
Expand Up@@ -843,6 +843,13 @@ public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond,
(s_Zip_TFirst_TSecond_TResult_3 = new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object, object>>, IQueryable<object>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TResult);

private static MethodInfo? s_Zip_TFirst_TSecond_TThird_3;

public static MethodInfo Zip_TFirst_TSecond_TThird_3(Type TFirst, Type TSecond, Type TThird) =>
(s_Zip_TFirst_TSecond_TThird_3 ??
(s_Zip_TFirst_TSecond_TThird_3 = new Func<IQueryable<object>, IEnumerable<object>, IEnumerable<object>, IQueryable<(object, object, object)>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TThird);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to comment saying ??= could be used to simplify this, but I see you're just following the pattern used in a bunch of other cases above. We can clean it up later.



private static MethodInfo? s_SkipLast_TSource_2;

Expand Down
27 changes: 27 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -666,6 +666,33 @@ public static IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IQueryable<
));
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="source1">The first sequence to merge.</param>
/// <param name="source2">The second sequence to merge.</param>
/// <param name="source3">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second and third sequences, in that order.</returns>
[DynamicDependency("Zip`3", typeof(Enumerable))]
public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (source3 == null)
throw Error.ArgumentNull(nameof(source3));
return source1.Provider.CreateQuery<(TFirst, TSecond, TThird)>(
Expression.Call(
null,
CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(typeof(TFirst), typeof(TSecond), typeof(TThird)),
source1.Expression, GetSourceExpression(source2), GetSourceExpression(source3)
));
}

[DynamicDependency("Union`1", typeof(Enumerable))]
public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
Expand Down
50 changes: 50 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,5 +90,55 @@ public void TupleNames()
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
}

[Fact]
public void Zip3_CorrectResults()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
var expected = new (int, int, int)[] { (1, 2, 1), (3, 6, 7), (5, 8, 2) };
Assert.Equal(expected, first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()));
}


[Fact]
public void Zip3_FirstIsNull()
{
IQueryable<int> first = null;
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source1", () => first.Zip(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void Zip3_SecondIsNull()
{
int[] first = new int[] { 1, 3, 5 };
IQueryable<int> second = null;
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source2", () => first.AsQueryable().Zip(second, third.AsQueryable()));
}

[Fact]
public void Zip3_ThirdIsNull()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
IQueryable<int> third = null;
AssertExtensions.Throws<ArgumentNullException>("source3", () => first.AsQueryable().Zip(second.AsQueryable(), third));
}

[Fact]
public void Zip3_TupleNames()
{
int[] first = new int[] { 1 };
int[] second = new int[] { 2 };
int[] third = new int[] { 3 };
var tuple = first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()).First();
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
Assert.Equal(tuple.Item3, tuple.Third);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Linq/ref/System.Linq.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,7 @@ public static System.Collections.Generic.IEnumerable<
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Collections.Generic.IEnumerable<TThird> third) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Func<TFirst, TSecond, TResult> resultSelector) { throw null; }
}
public partial interface IGrouping<out TKey, out TElement> : System.Collections.Generic.IEnumerable<TElement>, System.Collections.IEnumerable
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/ThrowHelper.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@ private static string GetArgumentString(ExceptionArgument argument)
case ExceptionArgument.second: return nameof(ExceptionArgument.second);
case ExceptionArgument.selector: return nameof(ExceptionArgument.selector);
case ExceptionArgument.source: return nameof(ExceptionArgument.source);
case ExceptionArgument.third: return nameof(ExceptionArgument.third);
default:
Debug.Fail("The ExceptionArgument value is not defined.");
return string.Empty;
Expand All@@ -76,5 +77,6 @@ internal enum ExceptionArgument
second,
selector,
source,
third,
}
}
43 changes: 43 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/Zip.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,36 @@ public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerabl
return ZipIterator(first, second);
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="first">The first sequence to merge.</param>
/// <param name="second">The second sequence to merge.</param>
/// <param name="third">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second, and third sequences, in that order.</returns>
public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}

if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}

if (third is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.third);
}

return ZipIterator(first, second, third);
}

private static IEnumerable<(TFirst First, TSecond Second)> ZipIterator<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
Expand All@@ -65,5 +95,18 @@ private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnume
}
}
}

private static IEnumerable<(TFirst First, TSecond Second, TThird Third)> ZipIterator<TFirst, TSecond, TThird>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
using (IEnumerator<TThird> e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
{
yield return (e1.Current, e2.Current, e3.Current);
}
}
}
}
}
126 changes: 122 additions & 4 deletions src/libraries/System.Linq/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -393,7 +393,7 @@ public void Zip2_ImplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip(second));
}
Expand All@@ -403,7 +403,7 @@ public void Zip2_ExplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip<int, int>(second));
}
Expand DownExpand Up@@ -431,7 +431,7 @@ public void Zip2_ExceptionThrownFromFirstsEnumerator()
{
ThrowsOnMatchEnumerable<int> first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> second = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };

Assert.Equal(expected, first.Zip(second));

Expand All@@ -447,7 +447,7 @@ public void Zip2_ExceptionThrownFromSecondsEnumerator()
{
ThrowsOnMatchEnumerable<int> second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> first = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };

Assert.Equal(expected, first.Zip(second));

Expand DownExpand Up@@ -601,5 +601,123 @@ public void Zip2_TupleNames()
Assert.Equal(t.Item1, t.First);
Assert.Equal(t.Item2, t.Second);
}

[Fact]
public void Zip3_FirstIsNull()
{
IEnumerable<int> first = null;
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new[] { 7, 8, 9 };

AssertExtensions.Throws<ArgumentNullException>("first", () => first.Zip(second, third));
}

[Fact]
public void Zip3_SecondIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = null;
IEnumerable<int> third = new[] { 4, 5, 6 };

AssertExtensions.Throws<ArgumentNullException>("second", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = null;

AssertExtensions.Throws<ArgumentNullException>("third", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdEmpty()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new int[] { };
IEnumerable<(int, int, int)> expected = new (int, int, int)[] { };

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

[Fact]
public void Zip3_ImplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ExplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.Zip<int, int, int>(second, third));
}

[Fact]
public void Zip3_ThirdOneMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdManyMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7, 8 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdOneLess()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_ThirdManyLess()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 3, 4, 5 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_RunOnce()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.RunOnce().Zip(second.RunOnce(), third.RunOnce()));
}
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 numberDiff line numberDiff line change
Expand Up@@ -162,6 +162,7 @@ public static partial class Queryable
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; }
public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Collections.Generic.IEnumerable<TThird> source3) { throw null; }
public static System.Linq.IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Linq.Expressions.Expression<System.Func<TFirst, TSecond, TResult>> resultSelector) { throw null; }
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -637,6 +637,12 @@
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TResult_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
Expand All@@ -662,4 +668,4 @@
<property name="Target">M:System.Linq.TypeHelper.GetStaticMethods(System.Type)</property>
</attribute>
</assembly>
</linker>
</linker>
Original file line numberDiff line numberDiff line change
Expand Up@@ -843,6 +843,13 @@ public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond,
(s_Zip_TFirst_TSecond_TResult_3 = new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object, object>>, IQueryable<object>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TResult);

private static MethodInfo? s_Zip_TFirst_TSecond_TThird_3;

public static MethodInfo Zip_TFirst_TSecond_TThird_3(Type TFirst, Type TSecond, Type TThird) =>
(s_Zip_TFirst_TSecond_TThird_3 ??
(s_Zip_TFirst_TSecond_TThird_3 = new Func<IQueryable<object>, IEnumerable<object>, IEnumerable<object>, IQueryable<(object, object, object)>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TThird);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to comment saying ??= could be used to simplify this, but I see you're just following the pattern used in a bunch of other cases above. We can clean it up later.



private static MethodInfo? s_SkipLast_TSource_2;

Expand Down
27 changes: 27 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -666,6 +666,33 @@ public static IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IQueryable<
));
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="source1">The first sequence to merge.</param>
/// <param name="source2">The second sequence to merge.</param>
/// <param name="source3">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second and third sequences, in that order.</returns>
[DynamicDependency("Zip`3", typeof(Enumerable))]
public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (source3 == null)
throw Error.ArgumentNull(nameof(source3));
return source1.Provider.CreateQuery<(TFirst, TSecond, TThird)>(
Expression.Call(
null,
CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(typeof(TFirst), typeof(TSecond), typeof(TThird)),
source1.Expression, GetSourceExpression(source2), GetSourceExpression(source3)
));
}

[DynamicDependency("Union`1", typeof(Enumerable))]
public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
Expand Down
50 changes: 50 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,5 +90,55 @@ public void TupleNames()
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
}

[Fact]
public void Zip3_CorrectResults()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
var expected = new (int, int, int)[] { (1, 2, 1), (3, 6, 7), (5, 8, 2) };
Assert.Equal(expected, first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()));
}


[Fact]
public void Zip3_FirstIsNull()
{
IQueryable<int> first = null;
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source1", () => first.Zip(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void Zip3_SecondIsNull()
{
int[] first = new int[] { 1, 3, 5 };
IQueryable<int> second = null;
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source2", () => first.AsQueryable().Zip(second, third.AsQueryable()));
}

[Fact]
public void Zip3_ThirdIsNull()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
IQueryable<int> third = null;
AssertExtensions.Throws<ArgumentNullException>("source3", () => first.AsQueryable().Zip(second.AsQueryable(), third));
}

[Fact]
public void Zip3_TupleNames()
{
int[] first = new int[] { 1 };
int[] second = new int[] { 2 };
int[] third = new int[] { 3 };
var tuple = first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()).First();
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
Assert.Equal(tuple.Item3, tuple.Third);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Linq/ref/System.Linq.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,7 @@ public static System.Collections.Generic.IEnumerable<
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Collections.Generic.IEnumerable<TThird> third) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Func<TFirst, TSecond, TResult> resultSelector) { throw null; }
}
public partial interface IGrouping<out TKey, out TElement> : System.Collections.Generic.IEnumerable<TElement>, System.Collections.IEnumerable
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/ThrowHelper.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@ private static string GetArgumentString(ExceptionArgument argument)
case ExceptionArgument.second: return nameof(ExceptionArgument.second);
case ExceptionArgument.selector: return nameof(ExceptionArgument.selector);
case ExceptionArgument.source: return nameof(ExceptionArgument.source);
case ExceptionArgument.third: return nameof(ExceptionArgument.third);
default:
Debug.Fail("The ExceptionArgument value is not defined.");
return string.Empty;
Expand All@@ -76,5 +77,6 @@ internal enum ExceptionArgument
second,
selector,
source,
third,
}
}
43 changes: 43 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/Zip.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,36 @@ public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerabl
return ZipIterator(first, second);
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="first">The first sequence to merge.</param>
/// <param name="second">The second sequence to merge.</param>
/// <param name="third">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second, and third sequences, in that order.</returns>
public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}

if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}

if (third is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.third);
}

return ZipIterator(first, second, third);
}

private static IEnumerable<(TFirst First, TSecond Second)> ZipIterator<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
Expand All@@ -65,5 +95,18 @@ private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnume
}
}
}

private static IEnumerable<(TFirst First, TSecond Second, TThird Third)> ZipIterator<TFirst, TSecond, TThird>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
using (IEnumerator<TThird> e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
{
yield return (e1.Current, e2.Current, e3.Current);
}
}
}
}
}
126 changes: 122 additions & 4 deletions src/libraries/System.Linq/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -393,7 +393,7 @@ public void Zip2_ImplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip(second));
}
Expand All@@ -403,7 +403,7 @@ public void Zip2_ExplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip<int, int>(second));
}
Expand DownExpand Up@@ -431,7 +431,7 @@ public void Zip2_ExceptionThrownFromFirstsEnumerator()
{
ThrowsOnMatchEnumerable<int> first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> second = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };

Assert.Equal(expected, first.Zip(second));

Expand All@@ -447,7 +447,7 @@ public void Zip2_ExceptionThrownFromSecondsEnumerator()
{
ThrowsOnMatchEnumerable<int> second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> first = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };

Assert.Equal(expected, first.Zip(second));

Expand DownExpand Up@@ -601,5 +601,123 @@ public void Zip2_TupleNames()
Assert.Equal(t.Item1, t.First);
Assert.Equal(t.Item2, t.Second);
}

[Fact]
public void Zip3_FirstIsNull()
{
IEnumerable<int> first = null;
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new[] { 7, 8, 9 };

AssertExtensions.Throws<ArgumentNullException>("first", () => first.Zip(second, third));
}

[Fact]
public void Zip3_SecondIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = null;
IEnumerable<int> third = new[] { 4, 5, 6 };

AssertExtensions.Throws<ArgumentNullException>("second", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = null;

AssertExtensions.Throws<ArgumentNullException>("third", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdEmpty()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new int[] { };
IEnumerable<(int, int, int)> expected = new (int, int, int)[] { };

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

[Fact]
public void Zip3_ImplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ExplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.Zip<int, int, int>(second, third));
}

[Fact]
public void Zip3_ThirdOneMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdManyMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7, 8 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdOneLess()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_ThirdManyLess()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 3, 4, 5 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_RunOnce()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.RunOnce().Zip(second.RunOnce(), third.RunOnce()));
}
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 numberDiff line numberDiff line change
Expand Up@@ -162,6 +162,7 @@ public static partial class Queryable
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; }
public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Collections.Generic.IEnumerable<TThird> source3) { throw null; }
public static System.Linq.IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Linq.Expressions.Expression<System.Func<TFirst, TSecond, TResult>> resultSelector) { throw null; }
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -637,6 +637,12 @@
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TResult_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
Expand All@@ -662,4 +668,4 @@
<property name="Target">M:System.Linq.TypeHelper.GetStaticMethods(System.Type)</property>
</attribute>
</assembly>
</linker>
</linker>
Original file line numberDiff line numberDiff line change
Expand Up@@ -843,6 +843,13 @@ public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond,
(s_Zip_TFirst_TSecond_TResult_3 = new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object, object>>, IQueryable<object>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TResult);

private static MethodInfo? s_Zip_TFirst_TSecond_TThird_3;

public static MethodInfo Zip_TFirst_TSecond_TThird_3(Type TFirst, Type TSecond, Type TThird) =>
(s_Zip_TFirst_TSecond_TThird_3 ??
(s_Zip_TFirst_TSecond_TThird_3 = new Func<IQueryable<object>, IEnumerable<object>, IEnumerable<object>, IQueryable<(object, object, object)>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TThird);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to comment saying ??= could be used to simplify this, but I see you're just following the pattern used in a bunch of other cases above. We can clean it up later.



private static MethodInfo? s_SkipLast_TSource_2;

Expand Down
27 changes: 27 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -666,6 +666,33 @@ public static IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IQueryable<
));
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="source1">The first sequence to merge.</param>
/// <param name="source2">The second sequence to merge.</param>
/// <param name="source3">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second and third sequences, in that order.</returns>
[DynamicDependency("Zip`3", typeof(Enumerable))]
public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (source3 == null)
throw Error.ArgumentNull(nameof(source3));
return source1.Provider.CreateQuery<(TFirst, TSecond, TThird)>(
Expression.Call(
null,
CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(typeof(TFirst), typeof(TSecond), typeof(TThird)),
source1.Expression, GetSourceExpression(source2), GetSourceExpression(source3)
));
}

[DynamicDependency("Union`1", typeof(Enumerable))]
public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
Expand Down
50 changes: 50 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,5 +90,55 @@ public void TupleNames()
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
}

[Fact]
public void Zip3_CorrectResults()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
var expected = new (int, int, int)[] { (1, 2, 1), (3, 6, 7), (5, 8, 2) };
Assert.Equal(expected, first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()));
}


[Fact]
public void Zip3_FirstIsNull()
{
IQueryable<int> first = null;
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source1", () => first.Zip(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void Zip3_SecondIsNull()
{
int[] first = new int[] { 1, 3, 5 };
IQueryable<int> second = null;
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source2", () => first.AsQueryable().Zip(second, third.AsQueryable()));
}

[Fact]
public void Zip3_ThirdIsNull()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
IQueryable<int> third = null;
AssertExtensions.Throws<ArgumentNullException>("source3", () => first.AsQueryable().Zip(second.AsQueryable(), third));
}

[Fact]
public void Zip3_TupleNames()
{
int[] first = new int[] { 1 };
int[] second = new int[] { 2 };
int[] third = new int[] { 3 };
var tuple = first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()).First();
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
Assert.Equal(tuple.Item3, tuple.Third);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Linq/ref/System.Linq.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,7 @@ public static System.Collections.Generic.IEnumerable<
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Collections.Generic.IEnumerable<TThird> third) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Func<TFirst, TSecond, TResult> resultSelector) { throw null; }
}
public partial interface IGrouping<out TKey, out TElement> : System.Collections.Generic.IEnumerable<TElement>, System.Collections.IEnumerable
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/ThrowHelper.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@ private static string GetArgumentString(ExceptionArgument argument)
case ExceptionArgument.second: return nameof(ExceptionArgument.second);
case ExceptionArgument.selector: return nameof(ExceptionArgument.selector);
case ExceptionArgument.source: return nameof(ExceptionArgument.source);
case ExceptionArgument.third: return nameof(ExceptionArgument.third);
default:
Debug.Fail("The ExceptionArgument value is not defined.");
return string.Empty;
Expand All@@ -76,5 +77,6 @@ internal enum ExceptionArgument
second,
selector,
source,
third,
}
}
43 changes: 43 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/Zip.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,36 @@ public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerabl
return ZipIterator(first, second);
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="first">The first sequence to merge.</param>
/// <param name="second">The second sequence to merge.</param>
/// <param name="third">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second, and third sequences, in that order.</returns>
public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}

if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}

if (third is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.third);
}

return ZipIterator(first, second, third);
}

private static IEnumerable<(TFirst First, TSecond Second)> ZipIterator<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
Expand All@@ -65,5 +95,18 @@ private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnume
}
}
}

private static IEnumerable<(TFirst First, TSecond Second, TThird Third)> ZipIterator<TFirst, TSecond, TThird>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
using (IEnumerator<TThird> e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
{
yield return (e1.Current, e2.Current, e3.Current);
}
}
}
}
}
126 changes: 122 additions & 4 deletions src/libraries/System.Linq/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -393,7 +393,7 @@ public void Zip2_ImplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip(second));
}
Expand All@@ -403,7 +403,7 @@ public void Zip2_ExplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip<int, int>(second));
}
Expand DownExpand Up@@ -431,7 +431,7 @@ public void Zip2_ExceptionThrownFromFirstsEnumerator()
{
ThrowsOnMatchEnumerable<int> first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> second = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };

Assert.Equal(expected, first.Zip(second));

Expand All@@ -447,7 +447,7 @@ public void Zip2_ExceptionThrownFromSecondsEnumerator()
{
ThrowsOnMatchEnumerable<int> second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> first = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };

Assert.Equal(expected, first.Zip(second));

Expand DownExpand Up@@ -601,5 +601,123 @@ public void Zip2_TupleNames()
Assert.Equal(t.Item1, t.First);
Assert.Equal(t.Item2, t.Second);
}

[Fact]
public void Zip3_FirstIsNull()
{
IEnumerable<int> first = null;
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new[] { 7, 8, 9 };

AssertExtensions.Throws<ArgumentNullException>("first", () => first.Zip(second, third));
}

[Fact]
public void Zip3_SecondIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = null;
IEnumerable<int> third = new[] { 4, 5, 6 };

AssertExtensions.Throws<ArgumentNullException>("second", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = null;

AssertExtensions.Throws<ArgumentNullException>("third", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdEmpty()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new int[] { };
IEnumerable<(int, int, int)> expected = new (int, int, int)[] { };

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

[Fact]
public void Zip3_ImplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ExplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.Zip<int, int, int>(second, third));
}

[Fact]
public void Zip3_ThirdOneMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdManyMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7, 8 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdOneLess()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_ThirdManyLess()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 3, 4, 5 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_RunOnce()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.RunOnce().Zip(second.RunOnce(), third.RunOnce()));
}
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 numberDiff line numberDiff line change
Expand Up@@ -162,6 +162,7 @@ public static partial class Queryable
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; }
public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Collections.Generic.IEnumerable<TThird> source3) { throw null; }
public static System.Linq.IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Linq.Expressions.Expression<System.Func<TFirst, TSecond, TResult>> resultSelector) { throw null; }
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -637,6 +637,12 @@
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TResult_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
Expand All@@ -662,4 +668,4 @@
<property name="Target">M:System.Linq.TypeHelper.GetStaticMethods(System.Type)</property>
</attribute>
</assembly>
</linker>
</linker>
Original file line numberDiff line numberDiff line change
Expand Up@@ -843,6 +843,13 @@ public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond,
(s_Zip_TFirst_TSecond_TResult_3 = new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object, object>>, IQueryable<object>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TResult);

private static MethodInfo? s_Zip_TFirst_TSecond_TThird_3;

public static MethodInfo Zip_TFirst_TSecond_TThird_3(Type TFirst, Type TSecond, Type TThird) =>
(s_Zip_TFirst_TSecond_TThird_3 ??
(s_Zip_TFirst_TSecond_TThird_3 = new Func<IQueryable<object>, IEnumerable<object>, IEnumerable<object>, IQueryable<(object, object, object)>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TThird);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to comment saying ??= could be used to simplify this, but I see you're just following the pattern used in a bunch of other cases above. We can clean it up later.



private static MethodInfo? s_SkipLast_TSource_2;

Expand Down
27 changes: 27 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -666,6 +666,33 @@ public static IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IQueryable<
));
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="source1">The first sequence to merge.</param>
/// <param name="source2">The second sequence to merge.</param>
/// <param name="source3">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second and third sequences, in that order.</returns>
[DynamicDependency("Zip`3", typeof(Enumerable))]
public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (source3 == null)
throw Error.ArgumentNull(nameof(source3));
return source1.Provider.CreateQuery<(TFirst, TSecond, TThird)>(
Expression.Call(
null,
CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(typeof(TFirst), typeof(TSecond), typeof(TThird)),
source1.Expression, GetSourceExpression(source2), GetSourceExpression(source3)
));
}

[DynamicDependency("Union`1", typeof(Enumerable))]
public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
Expand Down
50 changes: 50 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,5 +90,55 @@ public void TupleNames()
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
}

[Fact]
public void Zip3_CorrectResults()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
var expected = new (int, int, int)[] { (1, 2, 1), (3, 6, 7), (5, 8, 2) };
Assert.Equal(expected, first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()));
}


[Fact]
public void Zip3_FirstIsNull()
{
IQueryable<int> first = null;
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source1", () => first.Zip(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void Zip3_SecondIsNull()
{
int[] first = new int[] { 1, 3, 5 };
IQueryable<int> second = null;
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source2", () => first.AsQueryable().Zip(second, third.AsQueryable()));
}

[Fact]
public void Zip3_ThirdIsNull()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
IQueryable<int> third = null;
AssertExtensions.Throws<ArgumentNullException>("source3", () => first.AsQueryable().Zip(second.AsQueryable(), third));
}

[Fact]
public void Zip3_TupleNames()
{
int[] first = new int[] { 1 };
int[] second = new int[] { 2 };
int[] third = new int[] { 3 };
var tuple = first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()).First();
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
Assert.Equal(tuple.Item3, tuple.Third);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Linq/ref/System.Linq.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,7 @@ public static System.Collections.Generic.IEnumerable<
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Collections.Generic.IEnumerable<TThird> third) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Func<TFirst, TSecond, TResult> resultSelector) { throw null; }
}
public partial interface IGrouping<out TKey, out TElement> : System.Collections.Generic.IEnumerable<TElement>, System.Collections.IEnumerable
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/ThrowHelper.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@ private static string GetArgumentString(ExceptionArgument argument)
case ExceptionArgument.second: return nameof(ExceptionArgument.second);
case ExceptionArgument.selector: return nameof(ExceptionArgument.selector);
case ExceptionArgument.source: return nameof(ExceptionArgument.source);
case ExceptionArgument.third: return nameof(ExceptionArgument.third);
default:
Debug.Fail("The ExceptionArgument value is not defined.");
return string.Empty;
Expand All@@ -76,5 +77,6 @@ internal enum ExceptionArgument
second,
selector,
source,
third,
}
}
43 changes: 43 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/Zip.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,36 @@ public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerabl
return ZipIterator(first, second);
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="first">The first sequence to merge.</param>
/// <param name="second">The second sequence to merge.</param>
/// <param name="third">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second, and third sequences, in that order.</returns>
public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}

if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}

if (third is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.third);
}

return ZipIterator(first, second, third);
}

private static IEnumerable<(TFirst First, TSecond Second)> ZipIterator<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
Expand All@@ -65,5 +95,18 @@ private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnume
}
}
}

private static IEnumerable<(TFirst First, TSecond Second, TThird Third)> ZipIterator<TFirst, TSecond, TThird>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
using (IEnumerator<TThird> e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
{
yield return (e1.Current, e2.Current, e3.Current);
}
}
}
}
}
126 changes: 122 additions & 4 deletions src/libraries/System.Linq/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -393,7 +393,7 @@ public void Zip2_ImplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip(second));
}
Expand All@@ -403,7 +403,7 @@ public void Zip2_ExplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip<int, int>(second));
}
Expand DownExpand Up@@ -431,7 +431,7 @@ public void Zip2_ExceptionThrownFromFirstsEnumerator()
{
ThrowsOnMatchEnumerable<int> first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> second = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };

Assert.Equal(expected, first.Zip(second));

Expand All@@ -447,7 +447,7 @@ public void Zip2_ExceptionThrownFromSecondsEnumerator()
{
ThrowsOnMatchEnumerable<int> second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> first = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };

Assert.Equal(expected, first.Zip(second));

Expand DownExpand Up@@ -601,5 +601,123 @@ public void Zip2_TupleNames()
Assert.Equal(t.Item1, t.First);
Assert.Equal(t.Item2, t.Second);
}

[Fact]
public void Zip3_FirstIsNull()
{
IEnumerable<int> first = null;
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new[] { 7, 8, 9 };

AssertExtensions.Throws<ArgumentNullException>("first", () => first.Zip(second, third));
}

[Fact]
public void Zip3_SecondIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = null;
IEnumerable<int> third = new[] { 4, 5, 6 };

AssertExtensions.Throws<ArgumentNullException>("second", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = null;

AssertExtensions.Throws<ArgumentNullException>("third", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdEmpty()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new int[] { };
IEnumerable<(int, int, int)> expected = new (int, int, int)[] { };

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

[Fact]
public void Zip3_ImplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ExplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.Zip<int, int, int>(second, third));
}

[Fact]
public void Zip3_ThirdOneMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdManyMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7, 8 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdOneLess()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_ThirdManyLess()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 3, 4, 5 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_RunOnce()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.RunOnce().Zip(second.RunOnce(), third.RunOnce()));
}
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 numberDiff line numberDiff line change
Expand Up@@ -162,6 +162,7 @@ public static partial class Queryable
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; }
public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Collections.Generic.IEnumerable<TThird> source3) { throw null; }
public static System.Linq.IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Linq.Expressions.Expression<System.Func<TFirst, TSecond, TResult>> resultSelector) { throw null; }
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -637,6 +637,12 @@
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TResult_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
Expand All@@ -662,4 +668,4 @@
<property name="Target">M:System.Linq.TypeHelper.GetStaticMethods(System.Type)</property>
</attribute>
</assembly>
</linker>
</linker>
Original file line numberDiff line numberDiff line change
Expand Up@@ -843,6 +843,13 @@ public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond,
(s_Zip_TFirst_TSecond_TResult_3 = new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object, object>>, IQueryable<object>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TResult);

private static MethodInfo? s_Zip_TFirst_TSecond_TThird_3;

public static MethodInfo Zip_TFirst_TSecond_TThird_3(Type TFirst, Type TSecond, Type TThird) =>
(s_Zip_TFirst_TSecond_TThird_3 ??
(s_Zip_TFirst_TSecond_TThird_3 = new Func<IQueryable<object>, IEnumerable<object>, IEnumerable<object>, IQueryable<(object, object, object)>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TThird);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to comment saying ??= could be used to simplify this, but I see you're just following the pattern used in a bunch of other cases above. We can clean it up later.



private static MethodInfo? s_SkipLast_TSource_2;

Expand Down
27 changes: 27 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -666,6 +666,33 @@ public static IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IQueryable<
));
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="source1">The first sequence to merge.</param>
/// <param name="source2">The second sequence to merge.</param>
/// <param name="source3">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second and third sequences, in that order.</returns>
[DynamicDependency("Zip`3", typeof(Enumerable))]
public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (source3 == null)
throw Error.ArgumentNull(nameof(source3));
return source1.Provider.CreateQuery<(TFirst, TSecond, TThird)>(
Expression.Call(
null,
CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(typeof(TFirst), typeof(TSecond), typeof(TThird)),
source1.Expression, GetSourceExpression(source2), GetSourceExpression(source3)
));
}

[DynamicDependency("Union`1", typeof(Enumerable))]
public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
Expand Down
50 changes: 50 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,5 +90,55 @@ public void TupleNames()
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
}

[Fact]
public void Zip3_CorrectResults()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
var expected = new (int, int, int)[] { (1, 2, 1), (3, 6, 7), (5, 8, 2) };
Assert.Equal(expected, first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()));
}


[Fact]
public void Zip3_FirstIsNull()
{
IQueryable<int> first = null;
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source1", () => first.Zip(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void Zip3_SecondIsNull()
{
int[] first = new int[] { 1, 3, 5 };
IQueryable<int> second = null;
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source2", () => first.AsQueryable().Zip(second, third.AsQueryable()));
}

[Fact]
public void Zip3_ThirdIsNull()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
IQueryable<int> third = null;
AssertExtensions.Throws<ArgumentNullException>("source3", () => first.AsQueryable().Zip(second.AsQueryable(), third));
}

[Fact]
public void Zip3_TupleNames()
{
int[] first = new int[] { 1 };
int[] second = new int[] { 2 };
int[] third = new int[] { 3 };
var tuple = first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()).First();
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
Assert.Equal(tuple.Item3, tuple.Third);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Linq/ref/System.Linq.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,7 @@ public static System.Collections.Generic.IEnumerable<
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Collections.Generic.IEnumerable<TThird> third) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Func<TFirst, TSecond, TResult> resultSelector) { throw null; }
}
public partial interface IGrouping<out TKey, out TElement> : System.Collections.Generic.IEnumerable<TElement>, System.Collections.IEnumerable
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/ThrowHelper.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@ private static string GetArgumentString(ExceptionArgument argument)
case ExceptionArgument.second: return nameof(ExceptionArgument.second);
case ExceptionArgument.selector: return nameof(ExceptionArgument.selector);
case ExceptionArgument.source: return nameof(ExceptionArgument.source);
case ExceptionArgument.third: return nameof(ExceptionArgument.third);
default:
Debug.Fail("The ExceptionArgument value is not defined.");
return string.Empty;
Expand All@@ -76,5 +77,6 @@ internal enum ExceptionArgument
second,
selector,
source,
third,
}
}
43 changes: 43 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/Zip.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,36 @@ public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerabl
return ZipIterator(first, second);
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="first">The first sequence to merge.</param>
/// <param name="second">The second sequence to merge.</param>
/// <param name="third">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second, and third sequences, in that order.</returns>
public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}

if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}

if (third is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.third);
}

return ZipIterator(first, second, third);
}

private static IEnumerable<(TFirst First, TSecond Second)> ZipIterator<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
Expand All@@ -65,5 +95,18 @@ private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnume
}
}
}

private static IEnumerable<(TFirst First, TSecond Second, TThird Third)> ZipIterator<TFirst, TSecond, TThird>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
using (IEnumerator<TThird> e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
{
yield return (e1.Current, e2.Current, e3.Current);
}
}
}
}
}
126 changes: 122 additions & 4 deletions src/libraries/System.Linq/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -393,7 +393,7 @@ public void Zip2_ImplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip(second));
}
Expand All@@ -403,7 +403,7 @@ public void Zip2_ExplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip<int, int>(second));
}
Expand DownExpand Up@@ -431,7 +431,7 @@ public void Zip2_ExceptionThrownFromFirstsEnumerator()
{
ThrowsOnMatchEnumerable<int> first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> second = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };

Assert.Equal(expected, first.Zip(second));

Expand All@@ -447,7 +447,7 @@ public void Zip2_ExceptionThrownFromSecondsEnumerator()
{
ThrowsOnMatchEnumerable<int> second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> first = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };

Assert.Equal(expected, first.Zip(second));

Expand DownExpand Up@@ -601,5 +601,123 @@ public void Zip2_TupleNames()
Assert.Equal(t.Item1, t.First);
Assert.Equal(t.Item2, t.Second);
}

[Fact]
public void Zip3_FirstIsNull()
{
IEnumerable<int> first = null;
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new[] { 7, 8, 9 };

AssertExtensions.Throws<ArgumentNullException>("first", () => first.Zip(second, third));
}

[Fact]
public void Zip3_SecondIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = null;
IEnumerable<int> third = new[] { 4, 5, 6 };

AssertExtensions.Throws<ArgumentNullException>("second", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = null;

AssertExtensions.Throws<ArgumentNullException>("third", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdEmpty()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new int[] { };
IEnumerable<(int, int, int)> expected = new (int, int, int)[] { };

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

[Fact]
public void Zip3_ImplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ExplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.Zip<int, int, int>(second, third));
}

[Fact]
public void Zip3_ThirdOneMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdManyMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7, 8 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdOneLess()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_ThirdManyLess()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 3, 4, 5 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_RunOnce()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.RunOnce().Zip(second.RunOnce(), third.RunOnce()));
}
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 numberDiff line numberDiff line change
Expand Up@@ -162,6 +162,7 @@ public static partial class Queryable
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; }
public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Collections.Generic.IEnumerable<TThird> source3) { throw null; }
public static System.Linq.IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Linq.Expressions.Expression<System.Func<TFirst, TSecond, TResult>> resultSelector) { throw null; }
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -637,6 +637,12 @@
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TResult_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
Expand All@@ -662,4 +668,4 @@
<property name="Target">M:System.Linq.TypeHelper.GetStaticMethods(System.Type)</property>
</attribute>
</assembly>
</linker>
</linker>
Original file line numberDiff line numberDiff line change
Expand Up@@ -843,6 +843,13 @@ public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond,
(s_Zip_TFirst_TSecond_TResult_3 = new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object, object>>, IQueryable<object>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TResult);

private static MethodInfo? s_Zip_TFirst_TSecond_TThird_3;

public static MethodInfo Zip_TFirst_TSecond_TThird_3(Type TFirst, Type TSecond, Type TThird) =>
(s_Zip_TFirst_TSecond_TThird_3 ??
(s_Zip_TFirst_TSecond_TThird_3 = new Func<IQueryable<object>, IEnumerable<object>, IEnumerable<object>, IQueryable<(object, object, object)>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TThird);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to comment saying ??= could be used to simplify this, but I see you're just following the pattern used in a bunch of other cases above. We can clean it up later.



private static MethodInfo? s_SkipLast_TSource_2;

Expand Down
27 changes: 27 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -666,6 +666,33 @@ public static IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IQueryable<
));
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="source1">The first sequence to merge.</param>
/// <param name="source2">The second sequence to merge.</param>
/// <param name="source3">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second and third sequences, in that order.</returns>
[DynamicDependency("Zip`3", typeof(Enumerable))]
public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (source3 == null)
throw Error.ArgumentNull(nameof(source3));
return source1.Provider.CreateQuery<(TFirst, TSecond, TThird)>(
Expression.Call(
null,
CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(typeof(TFirst), typeof(TSecond), typeof(TThird)),
source1.Expression, GetSourceExpression(source2), GetSourceExpression(source3)
));
}

[DynamicDependency("Union`1", typeof(Enumerable))]
public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
Expand Down
50 changes: 50 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,5 +90,55 @@ public void TupleNames()
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
}

[Fact]
public void Zip3_CorrectResults()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
var expected = new (int, int, int)[] { (1, 2, 1), (3, 6, 7), (5, 8, 2) };
Assert.Equal(expected, first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()));
}


[Fact]
public void Zip3_FirstIsNull()
{
IQueryable<int> first = null;
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source1", () => first.Zip(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void Zip3_SecondIsNull()
{
int[] first = new int[] { 1, 3, 5 };
IQueryable<int> second = null;
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source2", () => first.AsQueryable().Zip(second, third.AsQueryable()));
}

[Fact]
public void Zip3_ThirdIsNull()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
IQueryable<int> third = null;
AssertExtensions.Throws<ArgumentNullException>("source3", () => first.AsQueryable().Zip(second.AsQueryable(), third));
}

[Fact]
public void Zip3_TupleNames()
{
int[] first = new int[] { 1 };
int[] second = new int[] { 2 };
int[] third = new int[] { 3 };
var tuple = first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()).First();
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
Assert.Equal(tuple.Item3, tuple.Third);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Linq/ref/System.Linq.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,7 @@ public static System.Collections.Generic.IEnumerable<
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Collections.Generic.IEnumerable<TThird> third) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Func<TFirst, TSecond, TResult> resultSelector) { throw null; }
}
public partial interface IGrouping<out TKey, out TElement> : System.Collections.Generic.IEnumerable<TElement>, System.Collections.IEnumerable
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/ThrowHelper.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@ private static string GetArgumentString(ExceptionArgument argument)
case ExceptionArgument.second: return nameof(ExceptionArgument.second);
case ExceptionArgument.selector: return nameof(ExceptionArgument.selector);
case ExceptionArgument.source: return nameof(ExceptionArgument.source);
case ExceptionArgument.third: return nameof(ExceptionArgument.third);
default:
Debug.Fail("The ExceptionArgument value is not defined.");
return string.Empty;
Expand All@@ -76,5 +77,6 @@ internal enum ExceptionArgument
second,
selector,
source,
third,
}
}
43 changes: 43 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/Zip.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,36 @@ public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerabl
return ZipIterator(first, second);
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="first">The first sequence to merge.</param>
/// <param name="second">The second sequence to merge.</param>
/// <param name="third">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second, and third sequences, in that order.</returns>
public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}

if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}

if (third is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.third);
}

return ZipIterator(first, second, third);
}

private static IEnumerable<(TFirst First, TSecond Second)> ZipIterator<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
Expand All@@ -65,5 +95,18 @@ private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnume
}
}
}

private static IEnumerable<(TFirst First, TSecond Second, TThird Third)> ZipIterator<TFirst, TSecond, TThird>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
using (IEnumerator<TThird> e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
{
yield return (e1.Current, e2.Current, e3.Current);
}
}
}
}
}
126 changes: 122 additions & 4 deletions src/libraries/System.Linq/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -393,7 +393,7 @@ public void Zip2_ImplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip(second));
}
Expand All@@ -403,7 +403,7 @@ public void Zip2_ExplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip<int, int>(second));
}
Expand DownExpand Up@@ -431,7 +431,7 @@ public void Zip2_ExceptionThrownFromFirstsEnumerator()
{
ThrowsOnMatchEnumerable<int> first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> second = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };

Assert.Equal(expected, first.Zip(second));

Expand All@@ -447,7 +447,7 @@ public void Zip2_ExceptionThrownFromSecondsEnumerator()
{
ThrowsOnMatchEnumerable<int> second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> first = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };

Assert.Equal(expected, first.Zip(second));

Expand DownExpand Up@@ -601,5 +601,123 @@ public void Zip2_TupleNames()
Assert.Equal(t.Item1, t.First);
Assert.Equal(t.Item2, t.Second);
}

[Fact]
public void Zip3_FirstIsNull()
{
IEnumerable<int> first = null;
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new[] { 7, 8, 9 };

AssertExtensions.Throws<ArgumentNullException>("first", () => first.Zip(second, third));
}

[Fact]
public void Zip3_SecondIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = null;
IEnumerable<int> third = new[] { 4, 5, 6 };

AssertExtensions.Throws<ArgumentNullException>("second", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = null;

AssertExtensions.Throws<ArgumentNullException>("third", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdEmpty()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new int[] { };
IEnumerable<(int, int, int)> expected = new (int, int, int)[] { };

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

[Fact]
public void Zip3_ImplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ExplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.Zip<int, int, int>(second, third));
}

[Fact]
public void Zip3_ThirdOneMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdManyMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7, 8 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdOneLess()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_ThirdManyLess()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 3, 4, 5 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_RunOnce()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.RunOnce().Zip(second.RunOnce(), third.RunOnce()));
}
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 numberDiff line numberDiff line change
Expand Up@@ -162,6 +162,7 @@ public static partial class Queryable
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; }
public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Collections.Generic.IEnumerable<TThird> source3) { throw null; }
public static System.Linq.IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Linq.Expressions.Expression<System.Func<TFirst, TSecond, TResult>> resultSelector) { throw null; }
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -637,6 +637,12 @@
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TResult_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
Expand All@@ -662,4 +668,4 @@
<property name="Target">M:System.Linq.TypeHelper.GetStaticMethods(System.Type)</property>
</attribute>
</assembly>
</linker>
</linker>
Original file line numberDiff line numberDiff line change
Expand Up@@ -843,6 +843,13 @@ public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond,
(s_Zip_TFirst_TSecond_TResult_3 = new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object, object>>, IQueryable<object>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TResult);

private static MethodInfo? s_Zip_TFirst_TSecond_TThird_3;

public static MethodInfo Zip_TFirst_TSecond_TThird_3(Type TFirst, Type TSecond, Type TThird) =>
(s_Zip_TFirst_TSecond_TThird_3 ??
(s_Zip_TFirst_TSecond_TThird_3 = new Func<IQueryable<object>, IEnumerable<object>, IEnumerable<object>, IQueryable<(object, object, object)>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TThird);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to comment saying ??= could be used to simplify this, but I see you're just following the pattern used in a bunch of other cases above. We can clean it up later.



private static MethodInfo? s_SkipLast_TSource_2;

Expand Down
27 changes: 27 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -666,6 +666,33 @@ public static IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IQueryable<
));
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="source1">The first sequence to merge.</param>
/// <param name="source2">The second sequence to merge.</param>
/// <param name="source3">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second and third sequences, in that order.</returns>
[DynamicDependency("Zip`3", typeof(Enumerable))]
public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (source3 == null)
throw Error.ArgumentNull(nameof(source3));
return source1.Provider.CreateQuery<(TFirst, TSecond, TThird)>(
Expression.Call(
null,
CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(typeof(TFirst), typeof(TSecond), typeof(TThird)),
source1.Expression, GetSourceExpression(source2), GetSourceExpression(source3)
));
}

[DynamicDependency("Union`1", typeof(Enumerable))]
public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
Expand Down
50 changes: 50 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,5 +90,55 @@ public void TupleNames()
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
}

[Fact]
public void Zip3_CorrectResults()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
var expected = new (int, int, int)[] { (1, 2, 1), (3, 6, 7), (5, 8, 2) };
Assert.Equal(expected, first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()));
}


[Fact]
public void Zip3_FirstIsNull()
{
IQueryable<int> first = null;
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source1", () => first.Zip(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void Zip3_SecondIsNull()
{
int[] first = new int[] { 1, 3, 5 };
IQueryable<int> second = null;
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source2", () => first.AsQueryable().Zip(second, third.AsQueryable()));
}

[Fact]
public void Zip3_ThirdIsNull()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
IQueryable<int> third = null;
AssertExtensions.Throws<ArgumentNullException>("source3", () => first.AsQueryable().Zip(second.AsQueryable(), third));
}

[Fact]
public void Zip3_TupleNames()
{
int[] first = new int[] { 1 };
int[] second = new int[] { 2 };
int[] third = new int[] { 3 };
var tuple = first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()).First();
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
Assert.Equal(tuple.Item3, tuple.Third);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Linq/ref/System.Linq.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,7 @@ public static System.Collections.Generic.IEnumerable<
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Collections.Generic.IEnumerable<TThird> third) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Func<TFirst, TSecond, TResult> resultSelector) { throw null; }
}
public partial interface IGrouping<out TKey, out TElement> : System.Collections.Generic.IEnumerable<TElement>, System.Collections.IEnumerable
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/ThrowHelper.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@ private static string GetArgumentString(ExceptionArgument argument)
case ExceptionArgument.second: return nameof(ExceptionArgument.second);
case ExceptionArgument.selector: return nameof(ExceptionArgument.selector);
case ExceptionArgument.source: return nameof(ExceptionArgument.source);
case ExceptionArgument.third: return nameof(ExceptionArgument.third);
default:
Debug.Fail("The ExceptionArgument value is not defined.");
return string.Empty;
Expand All@@ -76,5 +77,6 @@ internal enum ExceptionArgument
second,
selector,
source,
third,
}
}
43 changes: 43 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/Zip.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,36 @@ public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerabl
return ZipIterator(first, second);
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="first">The first sequence to merge.</param>
/// <param name="second">The second sequence to merge.</param>
/// <param name="third">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second, and third sequences, in that order.</returns>
public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}

if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}

if (third is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.third);
}

return ZipIterator(first, second, third);
}

private static IEnumerable<(TFirst First, TSecond Second)> ZipIterator<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
Expand All@@ -65,5 +95,18 @@ private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnume
}
}
}

private static IEnumerable<(TFirst First, TSecond Second, TThird Third)> ZipIterator<TFirst, TSecond, TThird>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
using (IEnumerator<TThird> e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
{
yield return (e1.Current, e2.Current, e3.Current);
}
}
}
}
}
126 changes: 122 additions & 4 deletions src/libraries/System.Linq/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -393,7 +393,7 @@ public void Zip2_ImplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip(second));
}
Expand All@@ -403,7 +403,7 @@ public void Zip2_ExplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip<int, int>(second));
}
Expand DownExpand Up@@ -431,7 +431,7 @@ public void Zip2_ExceptionThrownFromFirstsEnumerator()
{
ThrowsOnMatchEnumerable<int> first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> second = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };

Assert.Equal(expected, first.Zip(second));

Expand All@@ -447,7 +447,7 @@ public void Zip2_ExceptionThrownFromSecondsEnumerator()
{
ThrowsOnMatchEnumerable<int> second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> first = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };

Assert.Equal(expected, first.Zip(second));

Expand DownExpand Up@@ -601,5 +601,123 @@ public void Zip2_TupleNames()
Assert.Equal(t.Item1, t.First);
Assert.Equal(t.Item2, t.Second);
}

[Fact]
public void Zip3_FirstIsNull()
{
IEnumerable<int> first = null;
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new[] { 7, 8, 9 };

AssertExtensions.Throws<ArgumentNullException>("first", () => first.Zip(second, third));
}

[Fact]
public void Zip3_SecondIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = null;
IEnumerable<int> third = new[] { 4, 5, 6 };

AssertExtensions.Throws<ArgumentNullException>("second", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = null;

AssertExtensions.Throws<ArgumentNullException>("third", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdEmpty()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new int[] { };
IEnumerable<(int, int, int)> expected = new (int, int, int)[] { };

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

[Fact]
public void Zip3_ImplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ExplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.Zip<int, int, int>(second, third));
}

[Fact]
public void Zip3_ThirdOneMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdManyMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7, 8 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdOneLess()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_ThirdManyLess()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 3, 4, 5 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_RunOnce()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.RunOnce().Zip(second.RunOnce(), third.RunOnce()));
}
}
}
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 numberDiff line numberDiff line change
Expand Up@@ -162,6 +162,7 @@ public static partial class Queryable
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; }
public static System.Linq.IQueryable<TSource> Where<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2) { throw null; }
public static System.Linq.IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Collections.Generic.IEnumerable<TThird> source3) { throw null; }
public static System.Linq.IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this System.Linq.IQueryable<TFirst> source1, System.Collections.Generic.IEnumerable<TSecond> source2, System.Linq.Expressions.Expression<System.Func<TFirst, TSecond, TResult>> resultSelector) { throw null; }
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -637,6 +637,12 @@
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TResult_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Linq.CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(System.Type,System.Type,System.Type)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2060</argument>
Expand All@@ -662,4 +668,4 @@
<property name="Target">M:System.Linq.TypeHelper.GetStaticMethods(System.Type)</property>
</attribute>
</assembly>
</linker>
</linker>
Original file line numberDiff line numberDiff line change
Expand Up@@ -843,6 +843,13 @@ public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond,
(s_Zip_TFirst_TSecond_TResult_3 = new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object, object>>, IQueryable<object>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TResult);

private static MethodInfo? s_Zip_TFirst_TSecond_TThird_3;

public static MethodInfo Zip_TFirst_TSecond_TThird_3(Type TFirst, Type TSecond, Type TThird) =>
(s_Zip_TFirst_TSecond_TThird_3 ??
(s_Zip_TFirst_TSecond_TThird_3 = new Func<IQueryable<object>, IEnumerable<object>, IEnumerable<object>, IQueryable<(object, object, object)>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
.MakeGenericMethod(TFirst, TSecond, TThird);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to comment saying ??= could be used to simplify this, but I see you're just following the pattern used in a bunch of other cases above. We can clean it up later.



private static MethodInfo? s_SkipLast_TSource_2;

Expand Down
27 changes: 27 additions & 0 deletions src/libraries/System.Linq.Queryable/src/System/Linq/Queryable.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -666,6 +666,33 @@ public static IQueryable<TResult> Zip<TFirst, TSecond, TResult>(this IQueryable<
));
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="source1">The first sequence to merge.</param>
/// <param name="source2">The second sequence to merge.</param>
/// <param name="source3">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second and third sequences, in that order.</returns>
[DynamicDependency("Zip`3", typeof(Enumerable))]
public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3)
{
if (source1 == null)
throw Error.ArgumentNull(nameof(source1));
if (source2 == null)
throw Error.ArgumentNull(nameof(source2));
if (source3 == null)
throw Error.ArgumentNull(nameof(source3));
return source1.Provider.CreateQuery<(TFirst, TSecond, TThird)>(
Expression.Call(
null,
CachedReflectionInfo.Zip_TFirst_TSecond_TThird_3(typeof(TFirst), typeof(TSecond), typeof(TThird)),
source1.Expression, GetSourceExpression(source2), GetSourceExpression(source3)
));
}

[DynamicDependency("Union`1", typeof(Enumerable))]
public static IQueryable<TSource> Union<TSource>(this IQueryable<TSource> source1, IEnumerable<TSource> source2)
{
Expand Down
50 changes: 50 additions & 0 deletions src/libraries/System.Linq.Queryable/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -90,5 +90,55 @@ public void TupleNames()
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
}

[Fact]
public void Zip3_CorrectResults()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
var expected = new (int, int, int)[] { (1, 2, 1), (3, 6, 7), (5, 8, 2) };
Assert.Equal(expected, first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()));
}


[Fact]
public void Zip3_FirstIsNull()
{
IQueryable<int> first = null;
int[] second = new int[] { 2, 6, 8 };
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source1", () => first.Zip(second.AsQueryable(), third.AsQueryable()));
}

[Fact]
public void Zip3_SecondIsNull()
{
int[] first = new int[] { 1, 3, 5 };
IQueryable<int> second = null;
int[] third = new int[] { 1, 7, 2 };
AssertExtensions.Throws<ArgumentNullException>("source2", () => first.AsQueryable().Zip(second, third.AsQueryable()));
}

[Fact]
public void Zip3_ThirdIsNull()
{
int[] first = new int[] { 1, 3, 5 };
int[] second = new int[] { 2, 6, 8 };
IQueryable<int> third = null;
AssertExtensions.Throws<ArgumentNullException>("source3", () => first.AsQueryable().Zip(second.AsQueryable(), third));
}

[Fact]
public void Zip3_TupleNames()
{
int[] first = new int[] { 1 };
int[] second = new int[] { 2 };
int[] third = new int[] { 3 };
var tuple = first.AsQueryable().Zip(second.AsQueryable(), third.AsQueryable()).First();
Assert.Equal(tuple.Item1, tuple.First);
Assert.Equal(tuple.Item2, tuple.Second);
Assert.Equal(tuple.Item3, tuple.Third);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/System.Linq/ref/System.Linq.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -193,6 +193,7 @@ public static System.Collections.Generic.IEnumerable<
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<TSource> Where<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second) { throw null; }
public static System.Collections.Generic.IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Collections.Generic.IEnumerable<TThird> third) { throw null; }
public static System.Collections.Generic.IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this System.Collections.Generic.IEnumerable<TFirst> first, System.Collections.Generic.IEnumerable<TSecond> second, System.Func<TFirst, TSecond, TResult> resultSelector) { throw null; }
}
public partial interface IGrouping<out TKey, out TElement> : System.Collections.Generic.IEnumerable<TElement>, System.Collections.IEnumerable
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/ThrowHelper.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -50,6 +50,7 @@ private static string GetArgumentString(ExceptionArgument argument)
case ExceptionArgument.second: return nameof(ExceptionArgument.second);
case ExceptionArgument.selector: return nameof(ExceptionArgument.selector);
case ExceptionArgument.source: return nameof(ExceptionArgument.source);
case ExceptionArgument.third: return nameof(ExceptionArgument.third);
default:
Debug.Fail("The ExceptionArgument value is not defined.");
return string.Empty;
Expand All@@ -76,5 +77,6 @@ internal enum ExceptionArgument
second,
selector,
source,
third,
}
}
43 changes: 43 additions & 0 deletions src/libraries/System.Linq/src/System/Linq/Zip.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,6 +42,36 @@ public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerabl
return ZipIterator(first, second);
}

/// <summary>
/// Produces a sequence of tuples with elements from the three specified sequences.
/// </summary>
/// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
/// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
/// <typeparam name="TThird">The type of the elements of the third input sequence.</typeparam>
/// <param name="first">The first sequence to merge.</param>
/// <param name="second">The second sequence to merge.</param>
/// <param name="third">The third sequence to merge.</param>
/// <returns>A sequence of tuples with elements taken from the first, second, and third sequences, in that order.</returns>
public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
if (first is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.first);
}

if (second is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.second);
}

if (third is null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.third);
}

return ZipIterator(first, second, third);
}

private static IEnumerable<(TFirst First, TSecond Second)> ZipIterator<TFirst, TSecond>(IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
Expand All@@ -65,5 +95,18 @@ private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnume
}
}
}

private static IEnumerable<(TFirst First, TSecond Second, TThird Third)> ZipIterator<TFirst, TSecond, TThird>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
using (IEnumerator<TThird> e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
{
yield return (e1.Current, e2.Current, e3.Current);
}
}
}
}
}
126 changes: 122 additions & 4 deletions src/libraries/System.Linq/tests/ZipTests.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -393,7 +393,7 @@ public void Zip2_ImplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip(second));
}
Expand All@@ -403,7 +403,7 @@ public void Zip2_ExplicitTypeParameters()
{
IEnumerable<int> first = new int[] { 1, 2, 3 };
IEnumerable<int> second = new int[] { 2, 5, 9 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (2,5), (3,9) };

Assert.Equal(expected, first.Zip<int, int>(second));
}
Expand DownExpand Up@@ -431,7 +431,7 @@ public void Zip2_ExceptionThrownFromFirstsEnumerator()
{
ThrowsOnMatchEnumerable<int> first = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> second = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };
IEnumerable<(int, int)> expected = new (int,int)[] { (1,2), (3,4), (3,6) };

Assert.Equal(expected, first.Zip(second));

Expand All@@ -447,7 +447,7 @@ public void Zip2_ExceptionThrownFromSecondsEnumerator()
{
ThrowsOnMatchEnumerable<int> second = new ThrowsOnMatchEnumerable<int>(new int[] { 1, 3, 3 }, 2);
IEnumerable<int> first = new int[] { 2, 4, 6 };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };
IEnumerable<(int, int)> expected = new (int,int)[] { (2,1), (4,3), (6,3) };

Assert.Equal(expected, first.Zip(second));

Expand DownExpand Up@@ -601,5 +601,123 @@ public void Zip2_TupleNames()
Assert.Equal(t.Item1, t.First);
Assert.Equal(t.Item2, t.Second);
}

[Fact]
public void Zip3_FirstIsNull()
{
IEnumerable<int> first = null;
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new[] { 7, 8, 9 };

AssertExtensions.Throws<ArgumentNullException>("first", () => first.Zip(second, third));
}

[Fact]
public void Zip3_SecondIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = null;
IEnumerable<int> third = new[] { 4, 5, 6 };

AssertExtensions.Throws<ArgumentNullException>("second", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdIsNull()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = null;

AssertExtensions.Throws<ArgumentNullException>("third", () => first.Zip(second, third));
}

[Fact]
public void Zip3_ThirdEmpty()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 4, 5, 6 };
IEnumerable<int> third = new int[] { };
IEnumerable<(int, int, int)> expected = new (int, int, int)[] { };

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

[Fact]
public void Zip3_ImplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ExplicitTypeParameters()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.Zip<int, int, int>(second, third));
}

[Fact]
public void Zip3_ThirdOneMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdManyMore()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6, 7, 8 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

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

[Fact]
public void Zip3_ThirdOneLess()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_ThirdManyLess()
{
IEnumerable<int> first = new[] { 1, 2, 3 };
IEnumerable<int> second = new[] { 3, 4, 5 };
IEnumerable<int> third = new[] { 5 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5) };

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

[Fact]
public void Zip3_RunOnce()
{
IEnumerable<int> first = new[] { 1, 2 };
IEnumerable<int> second = new[] { 3, 4 };
IEnumerable<int> third = new[] { 5, 6 };
IEnumerable<(int, int, int)> expected = new[] { (1, 3, 5), (2, 4, 6) };

Assert.Equal(expected, first.RunOnce().Zip(second.RunOnce(), third.RunOnce()));
}
}
}