Quelle est l'utilité de Enumerable.Zip
méthode d'extension dans Linq ?
Réponses
Trop de publicités?L'opérateur Zip fusionne les éléments correspondants de deux séquences en utilisant une fonction de sélection spécifiée.
var letters= new string[] { "A", "B", "C", "D", "E" };
var numbers= new int[] { 1, 2, 3 };
var q = letters.Zip(numbers, (l, n) => l + n.ToString());
foreach (var s in q)
Console.WriteLine(s);
Ouput
A1
B2
C3
Zip
permet de combiner deux séquences en une seule. Par exemple, si vous avez les séquences
1, 2, 3
y
10, 20, 30
et vous voulez la séquence qui est le résultat de la multiplication des éléments dans la même position dans chaque séquence pour obtenir
10, 40, 90
vous pourriez dire
var left = new[] { 1, 2, 3 };
var right = new[] { 10, 20, 30 };
var products = left.Zip(right, (m, n) => m * n);
On l'appelle "zip" parce qu'il faut considérer une séquence comme le côté gauche d'une fermeture à glissière, et l'autre séquence comme le côté droit de la fermeture à glissière, et l'opérateur zip va tirer les deux côtés ensemble en appariant les dents (les éléments de la séquence) de manière appropriée.
Il itère à travers deux séquences et combine leurs éléments, un par un, en une seule nouvelle séquence. Ainsi, on prend un élément de la séquence A, on le transforme avec l'élément correspondant de la séquence B, et le résultat forme un élément de la séquence C.
Une façon d'y penser est que c'est similaire à Select
sauf qu'au lieu de transformer les éléments d'une seule collection, il travaille sur deux collections à la fois.
De la Article MSDN sur la méthode :
int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);
foreach (var item in numbersAndWords)
Console.WriteLine(item);
// This code produces the following output:
// 1 one
// 2 two
// 3 three
Si vous deviez faire cela en code impératif, vous feriez probablement quelque chose comme ceci :
for (int i = 0; i < numbers.Length && i < words.Length; i++)
{
numbersAndWords.Add(numbers[i] + " " + words[i]);
}
Ou si LINQ n'avait pas Zip
en elle, vous pourriez faire ça :
var numbersAndWords = numbers.Select(
(num, i) => num + " " + words[i]
);
C'est utile lorsque les données sont réparties dans des listes simples, semblables à des tableaux, ayant toutes la même longueur et le même ordre, et décrivant chacune une propriété différente du même ensemble d'objets. Zip
vous aide à rassembler ces éléments de données en une structure plus cohérente.
Ainsi, si vous disposez d'un tableau de noms d'États et d'un autre tableau de leurs abréviations, vous pouvez les regrouper dans un fichier State
comme ça :
IEnumerable<State> GetListOfStates(string[] stateNames, int[] statePopulations)
{
return stateNames.Zip(statePopulations,
(name, population) => new State()
{
Name = name,
Population = population
});
}
NE PAS laisser le nom Zip
vous déstabiliser. Cela n'a rien à voir avec le fait de zipper un fichier ou un dossier (compression). En fait, son nom provient du fonctionnement de la fermeture à glissière d'un vêtement : La fermeture à glissière des vêtements a deux côtés et chaque côté a un tas de dents. Lorsque vous allez dans une direction, la fermeture à glissière énumère (parcourt) les deux côtés et ferme la fermeture en serrant les dents. Lorsque vous allez dans l'autre sens, elle ouvre les dents. Vous vous retrouvez avec une fermeture à glissière ouverte ou fermée.
C'est la même idée avec le Zip
méthode. Prenons un exemple où nous avons deux collections. L'une contient des lettres et l'autre contient le nom d'un aliment qui commence par cette lettre. Pour des raisons de clarté, je les appelle leftSideOfZipper
y rightSideOfZipper
. Voici le code.
var leftSideOfZipper = new List<string> { "A", "B", "C", "D", "E" };
var rightSideOfZipper = new List<string> { "Apple", "Banana", "Coconut", "Donut" };
Notre tâche consiste à produire une collection dont la lettre du fruit est séparée par un signe :
et son nom. Comme ceci :
A : Apple
B : Banana
C : Coconut
D : Donut
Zip
à la rescousse. Pour rester dans la terminologie de notre fermeture éclair, nous appellerons ce résultat closedZipper
et les éléments de la fermeture éclair de gauche que nous appellerons leftTooth
et le côté droit que nous appellerons righTooth
pour des raisons évidentes :
var closedZipper = leftSideOfZipper
.Zip(rightSideOfZipper, (leftTooth, rightTooth) => leftTooth + " : " + rightTooth).ToList();
Dans l'exemple ci-dessus, nous énumérons (voyageons) le côté gauche de la fermeture à glissière et le côté droit de la fermeture à glissière et effectuons une opération sur chaque dent. L'opération que nous effectuons est la concaténation de la dent gauche (lettre alimentaire) avec une lettre :
et ensuite la dent de droite (nom de l'aliment). Nous faisons cela en utilisant ce code :
(leftTooth, rightTooth) => leftTooth + " : " + rightTooth)
Le résultat final est le suivant :
A : Apple
B : Banana
C : Coconut
D : Donut
Qu'est-il arrivé à la dernière lettre E ?
Si vous êtes en train d'énumérer (tirer) une vraie fermeture à glissière de vêtements et qu'un côté, peu importe le côté gauche ou le côté droit, a moins de dents que l'autre côté, que se passera-t-il ? Eh bien, la fermeture s'arrêtera là. Le site Zip
fera exactement la même chose : il s'arrêtera lorsqu'il aura atteint le dernier élément de chaque côté. Dans notre cas, le côté droit a moins de dents (noms des aliments), il s'arrêtera donc à "Donut".
Beaucoup de réponses ici démontrent Zip
mais sans vraiment expliquer un cas d'utilisation réel qui motiverait l'utilisation de l'outil. Zip
.
Un modèle particulièrement commun qui Zip
est fantastique pour itérer sur des paires successives de choses. Ceci est fait en itérant une énumérable X
avec lui-même, en sautant 1 élément : x.Zip(x.Skip(1)
. Exemple visuel :
x | x.Skip(1) | x.Zip(x.Skip(1), ...)
---+-----------+----------------------
| 1 |
1 | 2 | (1, 2)
2 | 3 | (2, 1)
3 | 4 | (3, 2)
4 | 5 | (4, 3)
Ces paires successives sont utiles pour trouver les premières différences entre les valeurs. Par exemple, les paires successives de IEnumable<MouseXPosition>
peut être utilisé pour produire IEnumerable<MouseXDelta>
. De même, les échantillons bool
valeurs de l'a button
peuvent être interprétés comme des événements tels que NotPressed
/ Clicked
/ Held
/ Released
. Ces événements peuvent alors conduire à des appels à des méthodes déléguées. Voici un exemple :
using System;
using System.Collections.Generic;
using System.Linq;
enum MouseEvent { NotPressed, Clicked, Held, Released }
public class Program {
public static void Main() {
// Example: Sampling the boolean state of a mouse button
List<bool> mouseStates = new List<bool> { false, false, false, false, true, true, true, false, true, false, false, true };
mouseStates.Zip(mouseStates.Skip(1), (oldMouseState, newMouseState) => {
if (oldMouseState) {
if (newMouseState) return MouseEvent.Held;
else return MouseEvent.Released;
} else {
if (newMouseState) return MouseEvent.Clicked;
else return MouseEvent.NotPressed;
}
})
.ToList()
.ForEach(mouseEvent => Console.WriteLine(mouseEvent) );
}
}
Imprimés :
NotPressesd
NotPressesd
NotPressesd
Clicked
Held
Held
Released
Clicked
Released
NotPressesd
Clicked
- Réponses précédentes
- Plus de réponses