1. Intro
Imagine deux piles de lettres : la première — avec des enveloppes où il y a des noms, la seconde — avec des cartes postales pour ces destinataires. Ton taf — associer chaque enveloppe avec la carte postale à la même position dans la deuxième pile. La première enveloppe — avec la première carte, la deuxième — avec la deuxième, etc. Tu avances dans les deux piles, tu prends un élément de chaque et tu les "zip" ensemble.
En prog, cette opération s’appelle zip — comme la fermeture éclair qui "colle" deux côtés ensemble, pile par position.
En LINQ, c’est un outil super pratique et puissant pour fusionner des données de deux (parfois plus) séquences, quand l’ordre compte : premier avec premier, deuxième avec deuxième, etc.
À retenir : Zip marche seulement tant qu’il y a des éléments dans les deux collections. Si l’une est plus courte — le résultat sera coupé à la plus courte.
Syntaxe et principe
Zip prend deux (ou plus) listes et les fusionne en une nouvelle : les éléments sont associés par index, donc 0 avec 0, 1 avec 1, etc. Si une des listes se termine avant, les nouvelles paires ne seront plus "zippées" — le résultat aura la taille de la liste la plus courte.
Signature (de base) :
IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TSecond, TResult> resultSelector
)
Fais gaffe : Zip ne renvoie pas juste des paires, mais ce que tu mets dans resultSelector. Ça peut être un tuple, une string, un objet, ce que tu veux — tout dépend de ta logique.
- first et second — les collections à fusionner.
- resultSelector — une fonction qui prend les éléments courants des deux collections et renvoie le résultat que tu veux.
En mode simple : on avance dans les deux collections en même temps, à chaque étape on fait ce qu’on veut avec la paire.
2. Exemples d’utilisation de Zip — du simple au plus costaud
Exemple le plus simple : additionner deux tableaux de nombres
Imaginons qu’on a deux listes :
- Notes de maths
- Notes de littérature
// Deux tableaux avec les notes des étudiants
int[] mathScores = { 5, 4, 3, 5, 2 };
int[] literatureScores = { 4, 5, 3, 4, 3 };
// On zip par position et on additionne
var totalScores = mathScores.Zip(literatureScores, (math, literature) => math + literature);
foreach (var score in totalScores)
Console.WriteLine($"Note totale de l’étudiant : {score}");
Note totale de l’étudiant : 9
Note totale de l’étudiant : 9
Note totale de l’étudiant : 6
Note totale de l’étudiant : 9
Note totale de l’étudiant : 5
Tu vois, c’est logique : le premier étudiant a la somme des premières notes, le deuxième — des deuxièmes, etc.
Combiner des strings et des nombres : afficher les produits avec les prix
Disons que t’as une liste de produits et une liste de prix. Tu veux afficher : "NomDuProduit — Prix".
string[] productNames = { "Pomme", "Poire", "Banane" };
decimal[] productPrices = { 50.5m, 60.0m, 35.2m };
var info = productNames.Zip(productPrices, (name, price) => $"{name} — {price} euro");
foreach (var s in info)
Console.WriteLine(s);
Pomme — 50,5 euro
Poire — 60,0 euro
Banane — 35,2 euro
Utiliser avec des collections d’objets
Imaginons qu’on avait une classe Student et une collection d’étudiants. Et maintenant — un tableau séparé de notes, aligné par ordre :
public class Student
{
public string Name { get; set; }
}
List<Student> students = new List<Student>
{
new Student { Name = "Vasya" },
new Student { Name = "Petya" },
new Student { Name = "Masha" }
};
int[] ratings = { 7, 9, 8 };
var studentsWithRatings = students.Zip(ratings, (student, rating) =>
$"{student.Name} : note {rating}");
foreach (var s in studentsWithRatings)
Console.WriteLine(s);
Vasya : note 7
Petya : note 9
Masha : note 8
Fusionner des objets plus complexes
Dans la vraie vie, tu dois souvent fusionner, par exemple, deux listes de commandes de systèmes différents : à chaque commande, associer le résultat du traitement ou l’état.
string[] orders = { "A-1", "B-2", "C-3" };
string[] statuses = { "Terminé", "En cours", "Refusé" };
var orderStatusList = orders.Zip(statuses, (order, status) => new { Order = order, Status = status });
foreach (var os in orderStatusList)
Console.WriteLine($"Commande {os.Order} : {os.Status}");
Commande A-1 : Terminé
Commande B-2 : En cours
Commande C-3 : Refusé
4. Particularités importantes et erreurs classiques
Quand tu utilises Zip, retiens : la collection finale aura la même taille que la plus courte des entrantes.
Par exemple, si t’as 10 étudiants mais seulement 7 notes, le résultat aura seulement 7 éléments. C’est une cause fréquente de "perte" de données inattendue.
Erreur n°1 : tailles ou ordre des collections pas synchro.
Si tu inverses les listes ou tu oublies de les trier pour que les éléments correspondent, la paire finale sera fausse.
Imagine : la liste des étudiants n’est pas dans le même ordre que celle des notes — Vasya peut choper la note de Petya. C’est comme mettre deux chaussettes différentes : elles sont là, mais ça fait bizarre.
Erreur n°2 : une des collections est vide.
Si au moins une liste est vide, le résultat sera vide.
Zip marche seulement tant qu’il y a des éléments dans les deux séquences. Si une source n’a rien, tu obtiens "rien".
5. Zip avec plusieurs collections
À la base, LINQ ne permettait de fusionner que deux collections. Mais depuis .NET 6, il y a une surcharge qui permet de zipper TROIS (ouais !) collections d’un coup.
Exemple avec trois collections :
string[] names = { "Rey", "Luke", "Leia" };
string[] planets = { "Tatooine", "Tatooine", "Alderaan" };
int[] ages = { 19, 23, 19 };
// En .NET 6+ il y a la surcharge :
var characters = names.Zip(planets, ages, (name, planet, age) =>
$"{name} ({planet}), {age} ans");
foreach (var c in characters)
Console.WriteLine(c);
Rey (Tatooine), 19 ans
Luke (Tatooine), 23 ans
Leia (Alderaan), 19 ans
Le nombre d’éléments du résultat — c’est la taille de la plus courte ! Si ages est plus court, le dernier Luke part "sans âge".
6. Zip et LINQ Query Syntax : ça existe ?
T’as peut-être remarqué qu’on utilise toujours la "method syntax". En LINQ Query Syntax, il n’y a pas de mot-clé pour Zip, et tu peux pas le faire avec from ... in .... La raison est simple : le concept de "zip" — c’est une fusion par position, alors que les requêtes LINQ classiques bossent avec join, select, etc., en général pour "lier" par clé, pas par position.
En résumé : Pour Zip on utilise que la syntaxe pointée (enfin, la method syntax quoi) :
var zipped = collection1.Zip(collection2, (a, b) => ...);
Si tu veux absolument utiliser la query syntax — franchement, évite. Mais si t’es vraiment motivé, tu peux te coder ta propre méthode d’extension. 🙂
7. Utiliser Zip dans des applis réelles
Exemple de ta pratique : comparer anciens et nouveaux prix
Imaginons, dans notre appli d’apprentissage, t’as une liste de produits et de temps en temps tu reçois de nouveaux prix. Tu veux afficher comment le prix de chaque produit a changé :
string[] productNames = { "Pomme", "Banane", "Ananas" };
decimal[] oldPrices = { 80.0m, 30.0m, 110.0m };
decimal[] newPrices = { 75.0m, 33.0m, 120.0m };
var priceChanges = productNames
.Zip(oldPrices, newPrices, (name, oldPrice, newPrice) =>
$"{name} : avant {oldPrice}, maintenant {newPrice}, changement : {newPrice - oldPrice:+#;-#;0}");
foreach (var s in priceChanges)
Console.WriteLine(s);
Pomme : avant 80, maintenant 75, changement : -5
Banane : avant 30, maintenant 33, changement : +3
Ananas : avant 110, maintenant 120, changement : +10
Pratique : fusionner des résultats de différentes sources
Dans les projets "en prod", Zip sert souvent à fusionner des résultats de différents API ou tables, où l’ordre des données est garanti (genre, des tableaux de prévisions météo et de mesures réelles).
8. Zip dans les chaînes LINQ : combiner avec d’autres opérateurs
Souvent, Zip est utilisé dans des chaînes LINQ plus longues. Par exemple, tu peux d’abord filtrer les listes, puis les zipper, puis calculer des stats.
var passedMath = mathScores.Where(x => x >= 3);
var passedLit = literatureScores.Where(x => x >= 3);
var passedPairs = passedMath.Zip(passedLit, (m, l) => m + l);
var avgScore = passedPairs.Average();
Console.WriteLine($"Moyenne totale parmi ceux qui ont réussi : {avgScore}");
Astuce : si après Where une des listes est plus courte, Zip coupe le résultat à la plus courte.
Comparer Zip et d’autres méthodes de fusion
| Méthode | Essence | Fusionne par… | Résultat : taille… |
|---|---|---|---|
|
Fusionne les éléments par index | Index (position) | Collection la plus courte |
|
Fusionne par clé | Clé commune | Toutes les paires qui matchent |
|
Pour chaque clé, une collection | Clé | Par la première collection |
|
Colle les collections à la suite | Juste à la fin | Somme des tailles |
Zip — c’est pour l’alignement positionnel. Join — c’est pour l’alignement par une valeur commune (genre code ou id).
9. Conseils pratiques et erreurs typiques
Quand tu utilises Zip dans ton appli, le plus important — assure-toi que :
- L’ordre des éléments dans les deux collections est synchro : sinon tu "colles" Vasya avec la note de Petya.
- Les collections sont bien triées ou préparées pour un parcours synchrone.
- Le résultat aura toujours la taille de la collection la plus courte.
- N’utilise pas Zip si tu dois associer les données par une clé ! Pour ça, il y a Join.
- Autre erreur fréquente — t’as oublié qu’il y a des doublons dans une collection et pas dans l’autre. Ou t’as changé l’ordre pendant un filtrage et perdu la correspondance.
GO TO FULL VERSION