¿Cuál es el uso de Enumerable.Método de extensión Zip en Linq?


¿Cuál es el uso de Enumerable.Zip método de extensión en Linq?

 96
Author: kdbanman, 2011-02-26

8 answers

El operador Zip fusiona los elementos correspondientes de dos secuencias usando una función selectora especificada.

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
 136
Author: santosh singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-16 08:26:36

Zip es para combinar dos secuencias en una. Por ejemplo, si tiene las secuencias

1, 2, 3

Y

10, 20, 30

Y desea que la secuencia que es el resultado de multiplicar elementos en la misma posición en cada secuencia para obtener

10, 40, 90

Se podría decir

var left = new[] { 1, 2, 3 };
var right = new[] { 10, 20, 30 };
var products = left.Zip(right, (m, n) => m * n);

Se llama " zip " porque se piensa en una secuencia como el lado izquierdo de una cremallera, y la otra secuencia como el lado derecho de la cremallera, y el operador de cremallera tirará de los dos lados juntos emparejar los dientes (los elementos de la secuencia) apropiadamente.

 92
Author: jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-25 21:20:03

Itera a través de dos secuencias y combina sus elementos, uno por uno, en una sola nueva secuencia. Así que toma un elemento de la secuencia A, transformarlo con el elemento correspondiente de la secuencia B, y el resultado forma un elemento de la secuencia C.

Una forma de pensarlo es que es similar a Select, excepto que en lugar de transformar elementos de una sola colección, funciona en dos colecciones a la vez.

Del artículo de MSDN sobre el método:

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 hicieras esto en código imperativo, probablemente harías algo como esto:

for (int i = 0; i < numbers.Length && i < words.Length; i++)
{
    numbersAndWords.Add(numbers[i] + " " + words[i]);
}

O si LINQ no tenía Zip en él, podrías hacer esto:

var numbersAndWords = numbers.Select(
                          (num, i) => num + " " + words[i]
                      );

Esto es útil cuando tiene datos distribuidos en listas simples, similares a matrices, cada una con la misma longitud y orden, y cada una describe una propiedad diferente del mismo conjunto de objetos. Zip te ayuda a unir esos datos en una estructura más coherente.

Así que si usted tiene un array of state names and another array of their abbreviations, you could collate them into a State class like so:

IEnumerable<State> GetListOfStates(string[] stateNames, int[] statePopulations)
{
    return stateNames.Zip(statePopulations, 
                          (name, population) => new State()
                          {
                              Name = name,
                              Population = population
                          });
}
 20
Author: Justin Morgan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-29 16:31:34

NO dejes que el nombre Zip te desanime. No tiene nada que ver con comprimir como en comprimir un archivo o una carpeta (comprimir). En realidad, recibe su nombre de cómo funciona una cremallera en la ropa: La cremallera en la ropa tiene 2 lados y cada lado tiene un montón de dientes. Cuando vas en una dirección, la cremallera enumera (viaja) ambos lados y cierra la cremallera apretando los dientes. Cuando vas en la otra dirección se abre los dientes. Puedes terminar con una cremallera abierta o cerrada.

Es la misma idea con el método Zip. Consideremos un ejemplo donde tenemos dos colecciones. Uno sostiene letras y el otro sostiene el nombre de un alimento que comienza con esa letra. Por razones de claridad los llamo leftSideOfZipper y rightSideOfZipper. Aquí está el código.

var leftSideOfZipper = new List<string> { "A", "B", "C", "D", "E" };
var rightSideOfZipper = new List<string> { "Apple", "Banana", "Coconut", "Donut" };

Nuestra tarea es producir una colección que tenga la letra del fruto separada por un : y su nombre. Así:

A : Apple
B : Banana
C : Coconut
D : Donut

Zip al rescate. Para mantenerse al día con nuestra terminología de cremallera, llamaremos a este resultado closedZipper y a los elementos de la cremallera izquierda llamaremos leftTooth y al lado derecho llamaremos righTooth por razones obvias:

var closedZipper = leftSideOfZipper
   .Zip(rightSideOfZipper, (leftTooth, rightTooth) => leftTooth + " : " + rightTooth).ToList();

En lo anterior estamos enumerando (viajando) el lado izquierdo de la cremallera y el lado derecho de la cremallera y realizando una operación en cada diente. La operación que estamos realizando es concatenar el diente izquierdo (letra del alimento) con un : y luego el diente derecho (nombre del alimento). Lo hacemos usando este código:

(leftTooth, rightTooth) => leftTooth + " : " + rightTooth)

El fin el resultado es el siguiente:

A : Apple
B : Banana
C : Coconut
D : Donut

¿Qué pasó con la última letra E?

Si está enumerando (tirando) una cremallera de ropa real y un lado, no importa el lado izquierdo o el lado derecho, tiene menos dientes que el otro lado, ¿qué sucederá? Bueno, la cremallera se detendrá allí. El método Zip hará exactamente lo mismo: Se detendrá una vez que haya alcanzado el último elemento en cada lado. En nuestro caso el lado derecho tiene menos dientes (nombres de alimentos) por lo que se detendrá en "Donut".

 7
Author: CodingYoshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-12 16:21:41

Como otros han dicho, Zip le permite combinar dos colecciones para su uso en otras sentencias Linq o un bucle foreach.

Las operaciones que solían requerir un bucle for y dos matrices ahora se pueden realizar en un bucle foreach utilizando un objeto anónimo.

Un ejemplo que acabo de descubrir, que es un poco tonto, pero podría ser útil si la paralelización fuera beneficiosa sería una sola cola de línea transversal con efectos secundarios:

timeSegments
    .Zip(timeSegments.Skip(1), (Current, Next) => new {Current, Next})
    .Where(zip => zip.Current.EndTime > zip.Next.StartTime)
    .AsParallel()
    .ForAll(zip => zip.Current.EndTime = zip.Next.StartTime);

TimeSegments representa el actual o descargados elementos en una cola (el último elemento es truncado por Zip). Segmentos de tiempo.Skip(1) representa los siguientes elementos de una cola. El método Zip combina estos dos en un único objeto anónimo con una propiedad Siguiente y Actual. Luego filtramos con Where y hacemos cambios con AsParallel ().ForAll. Por supuesto, el último bit podría ser un foreach regular u otra instrucción Select que devuelve los segmentos de tiempo ofensivos.

 5
Author: Novaterata,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-23 14:12:19

No tengo los puntos de rep para publicar en la sección de comentarios, pero para responder a la pregunta relacionada:

¿Qué pasa si quiero que zip continúe donde una lista se queda sin elementos? En en cuyo caso el elemento list más corto debe tomar el valor predeterminado. Salida en este caso ser A1, B2, C3, D0, E0. - liang Nov 19 ' 15 at 3: 29

Lo que haría es usar Array.Redimensionar () para rellenar la secuencia más corta con los valores predeterminados, y luego comprimir () juntos.

Código ejemplo :

var letters = new string[] { "A", "B", "C", "D", "E" };
var numbers = new int[] { 1, 2, 3 };
if (numbers.Length < letters.Length)
    Array.Resize(ref numbers, letters.Length);
var q = letters.Zip(numbers, (l, n) => l + n.ToString());
foreach (var s in q)
    Console.WriteLine(s);

Salida:

A1
B2
C3
D0
E0

Tenga en cuenta que el uso de matriz.Resize() tiene una advertencia : Redim Preserve en C#?

Si se desconoce qué secuencia será la más corta, se puede crear una función que la perciba:

static void Main(string[] args)
{
    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()).ToArray();
    var qDef = ZipDefault(letters, numbers);
    Array.Resize(ref q, qDef.Count());
    // Note: using a second .Zip() to show the results side-by-side
    foreach (var s in q.Zip(qDef, (a, b) => string.Format("{0, 2} {1, 2}", a, b)))
        Console.WriteLine(s);
}

static IEnumerable<string> ZipDefault(string[] letters, int[] numbers)
{
    switch (letters.Length.CompareTo(numbers.Length))
    {
        case -1: Array.Resize(ref letters, numbers.Length); break;
        case 0: goto default;
        case 1: Array.Resize(ref numbers, letters.Length); break;
        default: break;
    }
    return letters.Zip(numbers, (l, n) => l + n.ToString()); 
}

Salida de plain .Zip () junto con ZipDefault ():

A1 A1
B2 B2
C3 C3
   D0
   E0

Volviendo a la respuesta principal de la pregunta original, otra cosa interesante que uno podría desear hacer (cuando las longitudes de la las secuencias a ser "comprimidas" son diferentes) es unirlas de tal manera que el final de la lista coincida en lugar de la parte superior. Esto se puede lograr "omitiendo" el número apropiado de elementos utilizando .Saltar().

foreach (var s in letters.Skip(letters.Length - numbers.Length).Zip(numbers, (l, n) => l + n.ToString()).ToArray())
Console.WriteLine(s);

Salida:

C1
D2
E3
 5
Author: an odder guest,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:28

El método Zip le permite "fusionar" dos secuencias no relacionadas, utilizando un proveedor de función de fusión por usted, el llamante. El ejemplo de MSDN es bastante bueno para demostrar lo que puedes hacer con Zip. En este ejemplo, toma dos secuencias arbitrarias no relacionadas y las combina usando una función arbitraria (en este caso, solo concatena elementos de ambas secuencias en una sola cadena).

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
 2
Author: Andy White,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-02-25 21:19:20
string[] fname = { "mark", "john", "joseph" };
string[] lname = { "castro", "cruz", "lopez" };

var fullName = fname.Zip(lname, (f, l) => f + " " + l);

foreach (var item in fullName)
{
    Console.WriteLine(item);
}
// The output are

//mark castro..etc
 0
Author: CodeSlayer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-05 08:43:55