Enumerable.Join() / Zip()
| Since: | C# 3.0(2007) |
|---|
These are LINQ extension methods for joining two sequences. This page covers Join(), which joins by a common key, and Zip(), which pairs elements by position.
Syntax
using System.Linq;
// Performs an inner join on two sequences using a common key.
IEnumerable<TResult> joined = outer.Join(
inner,
outerKey => outer key,
innerKey => inner key,
(outer, inner) => result selector
);
// Pairs elements from two sequences by position.
IEnumerable<TResult> zipped = first.Zip(second, (a, b) => result selector);
// .NET 6 and later: Zip that returns tuple pairs without a selector
IEnumerable<(T1, T2)> zipped2 = first.Zip(second);
Method List
| Method | Description |
|---|---|
| Join(inner, outerKey, innerKey, result) | Returns the transformed results of an inner join (INNER JOIN) on elements with matching keys. |
| GroupJoin(inner, outerKey, innerKey, result) | Performs a group join equivalent to a left outer join (LEFT OUTER JOIN). |
| Zip(second, resultSelector) | Pairs elements from two sequences by position and returns the transformed results. |
| Zip(second) | .NET 6 and later. Returns a sequence of tuples (T1, T2). |
Sample Code
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
// Join a member list with an organization list.
var members = new List<(int Id, string Name, int OrgId)>
{
(1, "Son Goku", 10),
(2, "Vegeta", 10),
(3, "Krillin", 20),
};
var orgs = new List<(int Id, string OrgName)>
{
(10, "Capsule Corp"),
(20, "Turtle School"),
};
// Join: combine rows where the organization ID matches.
var joined = members.Join(
orgs,
m => m.OrgId,
o => o.Id,
(m, o) => $"{m.Name} ({o.OrgName})"
);
foreach (var s in joined) Console.WriteLine(s);
// Zip: pair two lists by position.
List<string> names = new List<string> { "Son Goku", "Vegeta", "Krillin" };
List<int> scores = new List<int> { 80, 95, 70 };
var zipped = names.Zip(scores, (name, score) => $"{name}: {score} pts");
foreach (var s in zipped) Console.WriteLine(s);
// .NET 6 and later: Zip returning tuples.
var pairs = names.Zip(scores);
foreach (var (name, score) in pairs)
Console.WriteLine($"{name} = {score}");
// Pair characters with their songs.
List<string> songs = new List<string> { "CHA-LA HEAD-CHA-LA", "DAN DAN Kokoro Hikarete ku", "Maka Fushigi Adventure!" };
var charSongs = names.Zip(songs, (name, song) => $"{name}: {song}");
foreach (var s in charSongs) Console.WriteLine(s);
Run the following command:
dotnet run Son Goku (Capsule Corp) Vegeta (Capsule Corp) Krillin (Turtle School) Son Goku: 80 pts Vegeta: 95 pts Krillin: 70 pts Son Goku = 80 Vegeta = 95 Krillin = 70 Son Goku: CHA-LA HEAD-CHA-LA Vegeta: DAN DAN Kokoro Hikarete ku Krillin: Maka Fushigi Adventure!
Common Mistakes
Common Mistake: Zip() Silently Drops Elements When Sequences Have Different Lengths
Zip() pairs elements by position, but it stops as soon as the shorter sequence runs out. No warning or error is raised when the sequences differ in length, so elements can be silently lost without any indication.
using System;
using System.Collections.Generic;
using System.Linq;
List<string> names = new List<string> { "Son Goku", "Vegeta", "Krillin", "Piccolo" };
List<int> scores = new List<int> { 100, 95, 80 };
// NG: scores has only 3 elements, so "Piccolo" is silently dropped
var zipped = names.Zip(scores, (n, s) => $"{n}: {s}");
foreach (var s in zipped) Console.WriteLine(s);
Console.WriteLine($"Count: {zipped.Count()}"); // 3 (expected 4)
Run the following command:
dotnet run Son Goku: 100 Vegeta: 95 Krillin: 80 Count: 3
Notes
Join() is equivalent to SQL's INNER JOIN. An element is included in the result only when a matching key exists in both the outer and inner sequences. Because the inner sequence is pre-loaded into a hash table, the join is efficient even with large datasets.
Zip() stops when the shorter sequence is exhausted. No warning is issued when the sequences have different lengths, so elements may be silently dropped. This is fine when the length difference is intentional, but if you expect both sequences to be the same length, verify their lengths beforehand.
For a left outer join (LEFT JOIN equivalent), combine GroupJoin() with SelectMany(). See Enumerable.Select() / SelectMany() for details. If you need a grouped join, also refer to Enumerable.GroupBy().
If you find any errors or copyright issues, please contact us.