El operador Zip combina los elementos correspondientes de dos secuencias utilizando una función de selector especificada.
var letters=newstring[]{"A","B","C","D","E"};var numbers=newint[]{1,2,3};var q = letters.Zip(numbers,(l, n)=> l + n.ToString());foreach(var s in q)Console.WriteLine(s);
Me gusta esta respuesta porque muestra lo que sucede cuando el número de elementos no coincide, similar a la documentación de
msdn
2
¿Qué pasa si quiero que zip continúe donde una lista se queda sin elementos? en cuyo caso el elemento de la lista más corta debe tomar el valor predeterminado. La salida en este caso será A1, B2, C3, D0, E0.
liang
2
@liang Dos opciones: A) Escribe tu propia Zipalternativa. B) Escribir un método para yield returncada elemento de la lista más corta, y luego continuar yield returning defaultindefinidamente a partir de entonces. (La opción B requiere que sepa de antemano qué lista es más corta.)
jpaugh
105
Zipes para combinar dos secuencias en una. Por ejemplo, si tienes las secuencias
1,2,3
y
10,20,30
y desea obtener la secuencia que es el resultado de multiplicar elementos en la misma posición en cada secuencia para obtener
10,40,90
tu puedes 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 piensas 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 la cremallera unirá los dos lados para emparejar los dientes (el elementos de la secuencia) adecuadamente.
Me encantó el ejemplo de la cremallera. Fue muy natural. Mi impresión inicial fue si tiene algo que ver con la velocidad o algo así, como si cruzaras una calle en tu auto.
RBT
23
Se itera a través de dos secuencias y combina sus elementos, uno por uno, en una sola secuencia nueva. Entonces toma un elemento de secuencia A, lo transforma con el elemento correspondiente de la secuencia B, y el resultado forma un elemento de 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.
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 un 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 lo tuviera Zip, podría hacer esto:
var numbersAndWords = numbers.Select((num, i)=> num +" "+ words[i]);
Esto es útil cuando tiene datos distribuidos en listas simples, en forma de matriz, cada una con la misma longitud y orden, y cada una describe una propiedad diferente del mismo conjunto de objetos. Ziple ayuda a unir esos datos en una estructura más coherente.
Entonces, si tiene una matriz de nombres de estado y otra matriz de sus abreviaturas, podría clasificarlas en una Stateclase como esta:
IEnumerable<State>GetListOfStates(string[] stateNames,int[] statePopulations){return stateNames.Zip(statePopulations,(name, population)=>newState(){Name= name,Population= population
});}
También me gustó esta respuesta, porque menciona la similitud conSelect
iliketocode
17
NO dejes que el nombre Zipte desanime. No tiene nada que ver con comprimir, como 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 va en una dirección, la cremallera enumera (recorre) ambos lados y cierra la cremallera apretando los dientes. Cuando vas en la otra dirección, se abren los dientes. Usted termina con una cremallera abierta o cerrada.
Es la misma idea con el Zipmétodo. Considere un ejemplo donde tenemos dos colecciones. Uno tiene letras y el otro tiene el nombre de un alimento que comienza con esa letra. Por razones de claridad, los estoy llamando leftSideOfZippery rightSideOfZipper. Aquí está el código.
var leftSideOfZipper =newList<string>{"A","B","C","D","E"};var rightSideOfZipper =newList<string>{"Apple","Banana","Coconut","Donut"};
Nuestra tarea es producir una colección que tenga la letra de la fruta separada por ay :su nombre. Me gusta esto:
A :Apple
B :Banana
C :Coconut
D :Donut
Zipal rescate. Para mantenernos al día con nuestra terminología de cremallera, llamaremos a este resultado closedZippery a los elementos de la cremallera izquierda que llamaremos leftToothy al lado derecho que llamaremos righToothpor razones obvias:
En lo anterior enumeramos (viajando) el lado izquierdo de la cremallera y el lado derecho de la cremallera y realizamos una operación en cada diente. La operación que estamos realizando es concatenar el diente izquierdo (letra del alimento) con ay :luego el diente derecho (nombre del alimento). Hacemos eso usando este código:
Si está enumerando (tirando) una cremallera de ropa real y un lado, no importa el lado izquierdo o el derecho, tiene menos dientes que el otro lado, ¿qué sucederá? Bueno, la cremallera se detendrá allí. El Zipmétodo hará exactamente lo mismo: se detendrá una vez que haya alcanzado el último elemento a cada lado. En nuestro caso, el lado derecho tiene menos dientes (nombres de alimentos), por lo que se detendrá en "Donut".
+1. Sí, el nombre "Zip" puede ser confuso al principio. Quizás "Intercalar" o "Tejer" habrían sido nombres más descriptivos para el método.
BACON
1
@bacon sí, pero entonces no habría podido usar mi ejemplo de cremallera;) Creo que una vez que descubras que es como una cremallera, es bastante sencillo después.
CodificaciónYoshi
Aunque sabía exactamente qué hace el método de extensión Zip, siempre he tenido curiosidad de por qué se llama así. En la jerga general del software, zip siempre ha significado algo más. Gran analogía :-) Debes haber leído la mente del creador.
Raghu Reddy Muttana
7
No tengo los puntos de representante para publicar en la sección de comentarios, pero para responder la pregunta relacionada:
¿Qué sucede si deseo que zip continúe donde una lista se quede sin elementos? En cuyo caso, el elemento de lista más corto debería tomar el valor predeterminado. La salida en este caso será A1, B2, C3, D0, E0. - liang 19 de noviembre de 2015 a las 3:29
Lo que haría es usar Array.Resize () para rellenar la secuencia más corta con los valores predeterminados, y luego comprimirlos () juntos.
Ejemplo de código:
var letters =newstring[]{"A","B","C","D","E"};var numbers =newint[]{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 Array.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 lo detecte:
staticvoidMain(string[] args){var letters =newstring[]{"A","B","C","D","E"};var numbers =newint[]{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-sideforeach(var s in q.Zip(qDef,(a, b)=>string.Format("{0, 2} {1, 2}", a, b)))Console.WriteLine(s);}staticIEnumerable<string>ZipDefault(string[] letters,int[] numbers){switch(letters.Length.CompareTo(numbers.Length)){case-1:Array.Resize(ref letters, numbers.Length);break;case0:gotodefault;case1:Array.Resize(ref numbers, letters.Length);break;default:break;}return letters.Zip(numbers,(l, n)=> l + n.ToString());}
Salida de .Zip () junto a 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 las secuencias que se van a "comprimir" son diferentes) es unirlas de tal manera que el final de la lista coincide en lugar de la parte superior. Esto se puede lograr "omitiendo" el número apropiado de elementos usando .Skip ().
foreach(var s in letters.Skip(letters.Length- numbers.Length).Zip(numbers,(l, n)=> l + n.ToString()).ToArray())Console.WriteLine(s);
Cambiar el tamaño es un desperdicio, especialmente si alguna de las colecciones es grande. Lo que realmente quiere hacer es tener una enumeración que continúe después del final de la colección, llenándola con valores vacíos a pedido (sin una colección de respaldo). Puede hacerlo con: public static IEnumerable<T> Pad<T>(this IEnumerable<T> input, long minLength, T value = default(T)) { long numYielded = 0; foreach (T element in input) { yield return element; ++numYielded; } while (numYielded < minLength) { yield return value; ++numYielded; } }
Pagefault
Parece que no estoy seguro de cómo formatear con éxito el código en un comentario ...
Pagefault
7
Muchas de las respuestas aquí demuestran Zip, pero sin explicar realmente un caso de uso de la vida real que motivaría el uso de Zip.
Un patrón particularmente común que Zipes fantástico para iterar sobre pares sucesivos de cosas. Esto se hace mediante la iteración de un enumerable Xconsigo mismo, omitiendo 1 elemento: x.Zip(x.Skip(1). Ejemplo visual:
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)
Estos pares sucesivos son útiles para encontrar las primeras diferencias entre valores. Por ejemplo, IEnumable<MouseXPosition>se pueden usar sucesivos pares de para producir IEnumerable<MouseXDelta>. Del mismo modo, los boolvalores de muestra de a buttonse pueden interpretar en eventos como NotPressed/ Clicked/ Held/ Released. Esos eventos pueden conducir llamadas a delegar métodos. Aquí hay un ejemplo:
using System;
using System.Collections.Generic;
using System.Linq;enumMouseEvent{NotPressed,Clicked,Held,Released}publicclassProgram{publicstaticvoidMain(){// Example: Sampling the boolean state of a mouse buttonList<bool> mouseStates =newList<bool>{false,false,false,false,true,true,true,false,true,false,false,true};
mouseStates.Zip(mouseStates.Skip(1),(oldMouseState, newMouseState)=>{if(oldMouseState){if(newMouseState)returnMouseEvent.Held;elsereturnMouseEvent.Released;}else{if(newMouseState)returnMouseEvent.Clicked;elsereturnMouseEvent.NotPressed;}}).ToList().ForEach(mouseEvent =>Console.WriteLine(mouseEvent));}}
Como han dicho otros, Zip le permite combinar dos colecciones para usar en otras declaraciones de 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 usando 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 un recorrido de cola de una sola línea con efectos secundarios:
timeSegments representa los elementos actuales o en cola en una cola (el último elemento se trunca por Zip). timeSegments.Skip (1) representa los elementos siguientes o de vistazo en una cola. El método Zip combina estos dos en un solo 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 devuelva los segmentos de tiempo ofensivos.
El método Zip le permite "fusionar" dos secuencias no relacionadas, utilizando un proveedor de funciones de fusión por usted, la persona que llama. El ejemplo en MSDN es bastante bueno para demostrar lo que puede hacer con Zip. En este ejemplo, toma dos secuencias arbitrarias no relacionadas y las combina usando una función arbitraria (en este caso, concatenando 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
Respuestas:
El operador Zip combina los elementos correspondientes de dos secuencias utilizando una función de selector especificada.
Ouput
fuente
Zip
alternativa. B) Escribir un método parayield return
cada elemento de la lista más corta, y luego continuaryield return
ingdefault
indefinidamente a partir de entonces. (La opción B requiere que sepa de antemano qué lista es más corta.)Zip
es para combinar dos secuencias en una. Por ejemplo, si tienes las secuenciasy
y desea obtener la secuencia que es el resultado de multiplicar elementos en la misma posición en cada secuencia para obtener
tu puedes decir
Se llama "zip" porque piensas 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 la cremallera unirá los dos lados para emparejar los dientes (el elementos de la secuencia) adecuadamente.
fuente
Se itera a través de dos secuencias y combina sus elementos, uno por uno, en una sola secuencia nueva. Entonces toma un elemento de secuencia A, lo transforma con el elemento correspondiente de la secuencia B, y el resultado forma un elemento de 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 :
Si hicieras esto en un código imperativo, probablemente harías algo como esto:
O si LINQ no lo tuviera
Zip
, podría hacer esto:Esto es útil cuando tiene datos distribuidos en listas simples, en forma de matriz, cada una con la misma longitud y orden, y cada una describe una propiedad diferente del mismo conjunto de objetos.
Zip
le ayuda a unir esos datos en una estructura más coherente.Entonces, si tiene una matriz de nombres de estado y otra matriz de sus abreviaturas, podría clasificarlas en una
State
clase como esta:fuente
Select
NO dejes que el nombre
Zip
te desanime. No tiene nada que ver con comprimir, como 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 va en una dirección, la cremallera enumera (recorre) ambos lados y cierra la cremallera apretando los dientes. Cuando vas en la otra dirección, se abren los dientes. Usted termina con una cremallera abierta o cerrada.Es la misma idea con el
Zip
método. Considere un ejemplo donde tenemos dos colecciones. Uno tiene letras y el otro tiene el nombre de un alimento que comienza con esa letra. Por razones de claridad, los estoy llamandoleftSideOfZipper
yrightSideOfZipper
. Aquí está el código.Nuestra tarea es producir una colección que tenga la letra de la fruta separada por ay
:
su nombre. Me gusta esto:Zip
al rescate. Para mantenernos al día con nuestra terminología de cremallera, llamaremos a este resultadoclosedZipper
y a los elementos de la cremallera izquierda que llamaremosleftTooth
y al lado derecho que llamaremosrighTooth
por razones obvias:En lo anterior enumeramos (viajando) el lado izquierdo de la cremallera y el lado derecho de la cremallera y realizamos una operación en cada diente. La operación que estamos realizando es concatenar el diente izquierdo (letra del alimento) con ay
:
luego el diente derecho (nombre del alimento). Hacemos eso usando este código:El resultado final es este:
¿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 derecho, tiene menos dientes que el otro lado, ¿qué sucederá? Bueno, la cremallera se detendrá allí. El
Zip
método hará exactamente lo mismo: se detendrá una vez que haya alcanzado el último elemento a cada lado. En nuestro caso, el lado derecho tiene menos dientes (nombres de alimentos), por lo que se detendrá en "Donut".fuente
No tengo los puntos de representante para publicar en la sección de comentarios, pero para responder la pregunta relacionada:
Lo que haría es usar Array.Resize () para rellenar la secuencia más corta con los valores predeterminados, y luego comprimirlos () juntos.
Ejemplo de código:
Salida:
Tenga en cuenta que el uso de Array.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 lo detecte:
Salida de .Zip () junto a ZipDefault ():
Volviendo a la respuesta principal de la pregunta original , otra cosa interesante que uno podría desear hacer (cuando las longitudes de las secuencias que se van a "comprimir" son diferentes) es unirlas de tal manera que el final de la lista coincide en lugar de la parte superior. Esto se puede lograr "omitiendo" el número apropiado de elementos usando .Skip ().
Salida:
fuente
public static IEnumerable<T> Pad<T>(this IEnumerable<T> input, long minLength, T value = default(T)) { long numYielded = 0; foreach (T element in input) { yield return element; ++numYielded; } while (numYielded < minLength) { yield return value; ++numYielded; } }
Muchas de las respuestas aquí demuestran
Zip
, pero sin explicar realmente un caso de uso de la vida real que motivaría el uso deZip
.Un patrón particularmente común que
Zip
es fantástico para iterar sobre pares sucesivos de cosas. Esto se hace mediante la iteración de un enumerableX
consigo mismo, omitiendo 1 elemento:x.Zip(x.Skip(1)
. Ejemplo visual:Estos pares sucesivos son útiles para encontrar las primeras diferencias entre valores. Por ejemplo,
IEnumable<MouseXPosition>
se pueden usar sucesivos pares de para producirIEnumerable<MouseXDelta>
. Del mismo modo, losbool
valores de muestra de abutton
se pueden interpretar en eventos comoNotPressed
/Clicked
/Held
/Released
. Esos eventos pueden conducir llamadas a delegar métodos. Aquí hay un ejemplo:Huellas dactilares:
fuente
Como han dicho otros, Zip le permite combinar dos colecciones para usar en otras declaraciones de 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 usando 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 un recorrido de cola de una sola línea con efectos secundarios:
timeSegments representa los elementos actuales o en cola en una cola (el último elemento se trunca por Zip). timeSegments.Skip (1) representa los elementos siguientes o de vistazo en una cola. El método Zip combina estos dos en un solo 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 devuelva los segmentos de tiempo ofensivos.
fuente
El método Zip le permite "fusionar" dos secuencias no relacionadas, utilizando un proveedor de funciones de fusión por usted, la persona que llama. El ejemplo en MSDN es bastante bueno para demostrar lo que puede hacer con Zip. En este ejemplo, toma dos secuencias arbitrarias no relacionadas y las combina usando una función arbitraria (en este caso, concatenando elementos de ambas secuencias en una sola cadena).
fuente
fuente