O operador Zip mescla os elementos correspondentes de duas seqüências usando uma função seletora 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);
I como esta resposta, porque mostra o que acontece quando o número de elementos não corresponde, similar à documentação da MSDN
DLeh
2
e se eu quiser que o zip continue onde uma lista fica sem elementos? nesse caso, o elemento da lista mais curto deve assumir o valor padrão. A saída neste caso deve ser A1, B2, C3, D0, E0.
Liang
2
@liang Duas opções: A) Escreva sua própria Zipalternativa. B) Escrever um método para yield returncada elemento da lista mais curta, e, em seguida, continuar yield returning defaultpor tempo indeterminado. (Opção B requer que você sabe de antemão que a lista é mais curto.)
jpaugh
105
Zipé para combinar duas seqüências em uma. Por exemplo, se você tiver as sequências
1,2,3
e
10,20,30
e você deseja que a sequência resultante da multiplicação de elementos na mesma posição em cada sequência obtenha
10,40,90
você poderia dizer
var left =new[]{1,2,3};var right =new[]{10,20,30};var products = left.Zip(right,(m, n)=> m * n);
É chamado de "zip" porque você pensa em uma sequência como o lado esquerdo de um zíper, e a outra sequência como o lado direito do zíper, e o operador de zip puxará os dois lados juntos emparelhando os dentes (o elementos da sequência) adequadamente.
Adorei o exemplo de zíper. Foi tão natural. Minha impressão inicial foi se ele tem algo a ver com velocidade ou algo parecido como se você passasse por uma rua em seu carro.
RBT
23
Ele itera através de duas seqüências e combina seus elementos, um por um, em uma única nova seqüência. Então você pega um elemento da sequência A, transforma-o com o elemento correspondente da sequência B e o resultado forma um elemento da sequência C.
Uma maneira de pensar é que é semelhante a Select, exceto que, em vez de transformar itens de uma única coleção, funciona em duas coleções ao mesmo tempo.
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
Se você fizesse isso em código imperativo, provavelmente faria algo assim:
for(int i =0; i < numbers.Length&& i < words.Length; i++){
numbersAndWords.Add(numbers[i]+" "+ words[i]);}
Ou se o LINQ não continha Zip, você poderia fazer o seguinte:
var numbersAndWords = numbers.Select((num, i)=> num +" "+ words[i]);
Isso é útil quando você tem dados espalhados em listas simples, semelhantes a matrizes, cada uma com o mesmo comprimento e ordem, e cada uma descrevendo uma propriedade diferente do mesmo conjunto de objetos. Zipajuda a reunir esses dados em uma estrutura mais coerente.
Portanto, se você tiver uma matriz de nomes de estados e outra matriz de abreviações, poderá agrupá-las em uma Stateclasse como esta:
IEnumerable<State>GetListOfStates(string[] stateNames,int[] statePopulations){return stateNames.Zip(statePopulations,(name, population)=>newState(){Name= name,Population= population
});}
Eu gostei desta resposta também, porque menciona a semelhança comSelect
iliketocode
17
NÃO deixe o nome Zip jogá-lo fora. Não tem nada a ver com compactar, como compactar um arquivo ou uma pasta (compactar). Na verdade, ele recebe o nome de como funciona o zíper nas roupas: o zíper nas roupas tem dois lados e cada lado tem um monte de dentes. Quando você segue uma direção, o zíper enumera (viaja) dos dois lados e fecha o zíper apertando os dentes. Quando você vai na outra direção, ele abre os dentes. Você termina com um zíper aberto ou fechado.
É a mesma idéia com o Zipmétodo. Considere um exemplo em que temos duas coleções. Um contém letras e o outro contém o nome de um item alimentar que começa com essa letra. Para fins de clareza, eu os estou chamando leftSideOfZippere rightSideOfZipper. Aqui está o código.
var leftSideOfZipper =newList<string>{"A","B","C","D","E"};var rightSideOfZipper =newList<string>{"Apple","Banana","Coconut","Donut"};
Nossa tarefa é produzir uma coleção que tenha a letra da fruta separada por a :e seu nome. Como isso:
A :Apple
B :Banana
C :Coconut
D :Donut
Zippara o resgate. Para acompanhar a terminologia do zíper, chamaremos esse resultado closedZippere os itens do zíper esquerdo, leftTootho lado direito, e o lado direito, righToothpor razões óbvias:
No exposto acima, estamos enumerando (deslocando) o lado esquerdo do zíper e o lado direito do zíper e realizando uma operação em cada dente. A operação que estamos realizando está concatenando o dente esquerdo (letra do alimento) com um :e depois o dente direito (nome do alimento). Fazemos isso usando este código:
Se você está enumerando (puxando) um zíper de roupa real e um lado, não importa o lado esquerdo ou o lado direito, tem menos dentes do que o outro lado, o que acontecerá? Bem, o zíper vai parar por aí. O Zipmétodo fará exatamente o mesmo: será interrompido quando atingir o último item de cada lado. No nosso caso, o lado direito tem menos dentes (nomes de alimentos) e, portanto, para em "Donut".
+1. Sim, o nome "Zip" pode ser confuso no início. Talvez "intercalar" ou "tecer" tivesse sido nomes mais descritivos para o método.
BACON
1
@ Bacon sim, mas então eu não teria sido capaz de usar o meu exemplo de zíper;) Eu acho que depois que você descobrir que é como um zíper, é bastante direto depois.
CodingYoshi
Embora eu soubesse exatamente o que o método de extensão Zip faz, sempre fiquei curioso para saber por que foi chamado assim. No jargão geral do software, zip sempre significou outra coisa. Ótima analogia :-) Você deve ter lido a mente do criador.
Raghu Reddy Muttana
7
Não tenho os pontos de representante para postar na seção de comentários, mas para responder à pergunta relacionada:
E se eu quiser que o zip continue onde uma lista fica sem elementos? Nesse caso, o elemento da lista mais curto deve assumir o valor padrão. A saída neste caso deve ser A1, B2, C3, D0, E0. # Liang Nov 19 '15 às 3:29
O que você faria é usar Array.Resize () para preencher a sequência mais curta com os valores padrão e depois Zip () juntos.
Exemplo 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);
Se não se sabe qual sequência será a mais curta, uma função pode ser criada que a exclua:
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());}
Saída do .Zip () comum ao lado de ZipDefault ():
A1 A1
B2 B2
C3 C3
D0
E0
Voltando à resposta principal da pergunta original , outra coisa interessante que alguém pode querer fazer (quando os comprimentos das seqüências a serem "compactadas" são diferentes) é juntá-las de tal maneira que o final da lista corresponde ao invés do topo. Isso pode ser feito "pulando" o número apropriado de itens usando .Skip ().
foreach(var s in letters.Skip(letters.Length- numbers.Length).Zip(numbers,(l, n)=> l + n.ToString()).ToArray())Console.WriteLine(s);
O redimensionamento é um desperdício, especialmente se uma das coleções for grande. O que você realmente deseja fazer é ter uma enumeração que continue após o final da coleção, preenchendo-a com valores vazios sob demanda (sem uma coleção de backup). Você pode fazer isso com: 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 5/11/19
Parece que não tenho certeza de como formatar com êxito o código em um comentário ...
Pagefault
7
Muitas das respostas aqui demonstram Zip, mas sem realmente explicar um caso de uso da vida real que motivaria o uso Zip.
Um padrão particularmente comum que Zipé fantástico para iterar sobre pares sucessivos de coisas. Isso é feito por iteração um enumeráveis Xcom ele mesmo, pulando 1 elemento: x.Zip(x.Skip(1). Exemplo 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)
Esses pares sucessivos são úteis para encontrar as primeiras diferenças entre os valores. Por exemplo, pares sucessivos de IEnumable<MouseXPosition>podem ser usados para produzir IEnumerable<MouseXDelta>. Da mesma forma, os boolvalores amostrados de a buttonpodem ser interpretados em eventos como NotPressed/ Clicked/ Held/ Released. Esses eventos podem direcionar chamadas para delegar métodos. Aqui está um exemplo:
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 já foi dito, o Zip permite combinar duas coleções para uso em outras instruções do Linq ou em um loop foreach.
As operações que costumavam exigir um loop for e duas matrizes agora podem ser feitas em um loop foreach usando um objeto anônimo.
Um exemplo que acabei de descobrir, que é meio bobo, mas poderia ser útil se a paralelização fosse benéfica, seria uma travessia de fila única com efeitos colaterais:
timeSegments representa os itens atuais ou desenfileirados em uma fila (o último elemento é truncado por Zip). timeSegments.Skip (1) representa os itens seguintes ou espiar em uma fila. O método Zip combina esses dois em um único objeto anônimo com a propriedade Next e Current. Em seguida, filtramos com Where e fazemos alterações com AsParallel (). ForAll. É claro que o último bit poderia ser apenas uma foreach regular ou outra instrução Select que retorne os segmentos de tempo ofensivos.
O método Zip permite "mesclar" duas seqüências não relacionadas, usando um provedor de função de mesclagem por você, o chamador. O exemplo no MSDN é realmente muito bom em demonstrar o que você pode fazer com o Zip. Neste exemplo, você pega duas seqüências arbitrárias não relacionadas e as combina usando uma função arbitrária (nesse caso, apenas concatenando itens de ambas as seqüências em uma única sequência).
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
Respostas:
O operador Zip mescla os elementos correspondentes de duas seqüências usando uma função seletora especificada.
Ouput
fonte
Zip
alternativa. B) Escrever um método parayield return
cada elemento da lista mais curta, e, em seguida, continuaryield return
ingdefault
por tempo indeterminado. (Opção B requer que você sabe de antemão que a lista é mais curto.)Zip
é para combinar duas seqüências em uma. Por exemplo, se você tiver as sequênciase
e você deseja que a sequência resultante da multiplicação de elementos na mesma posição em cada sequência obtenha
você poderia dizer
É chamado de "zip" porque você pensa em uma sequência como o lado esquerdo de um zíper, e a outra sequência como o lado direito do zíper, e o operador de zip puxará os dois lados juntos emparelhando os dentes (o elementos da sequência) adequadamente.
fonte
Ele itera através de duas seqüências e combina seus elementos, um por um, em uma única nova seqüência. Então você pega um elemento da sequência A, transforma-o com o elemento correspondente da sequência B e o resultado forma um elemento da sequência C.
Uma maneira de pensar é que é semelhante a
Select
, exceto que, em vez de transformar itens de uma única coleção, funciona em duas coleções ao mesmo tempo.No artigo do MSDN sobre o método :
Se você fizesse isso em código imperativo, provavelmente faria algo assim:
Ou se o LINQ não continha
Zip
, você poderia fazer o seguinte:Isso é útil quando você tem dados espalhados em listas simples, semelhantes a matrizes, cada uma com o mesmo comprimento e ordem, e cada uma descrevendo uma propriedade diferente do mesmo conjunto de objetos.
Zip
ajuda a reunir esses dados em uma estrutura mais coerente.Portanto, se você tiver uma matriz de nomes de estados e outra matriz de abreviações, poderá agrupá-las em uma
State
classe como esta:fonte
Select
NÃO deixe o nome
Zip
jogá-lo fora. Não tem nada a ver com compactar, como compactar um arquivo ou uma pasta (compactar). Na verdade, ele recebe o nome de como funciona o zíper nas roupas: o zíper nas roupas tem dois lados e cada lado tem um monte de dentes. Quando você segue uma direção, o zíper enumera (viaja) dos dois lados e fecha o zíper apertando os dentes. Quando você vai na outra direção, ele abre os dentes. Você termina com um zíper aberto ou fechado.É a mesma idéia com o
Zip
método. Considere um exemplo em que temos duas coleções. Um contém letras e o outro contém o nome de um item alimentar que começa com essa letra. Para fins de clareza, eu os estou chamandoleftSideOfZipper
erightSideOfZipper
. Aqui está o código.Nossa tarefa é produzir uma coleção que tenha a letra da fruta separada por a
:
e seu nome. Como isso:Zip
para o resgate. Para acompanhar a terminologia do zíper, chamaremos esse resultadoclosedZipper
e os itens do zíper esquerdo,leftTooth
o lado direito, e o lado direito,righTooth
por razões óbvias:No exposto acima, estamos enumerando (deslocando) o lado esquerdo do zíper e o lado direito do zíper e realizando uma operação em cada dente. A operação que estamos realizando está concatenando o dente esquerdo (letra do alimento) com um
:
e depois o dente direito (nome do alimento). Fazemos isso usando este código:O resultado final é este:
O que aconteceu com a última letra E?
Se você está enumerando (puxando) um zíper de roupa real e um lado, não importa o lado esquerdo ou o lado direito, tem menos dentes do que o outro lado, o que acontecerá? Bem, o zíper vai parar por aí. O
Zip
método fará exatamente o mesmo: será interrompido quando atingir o último item de cada lado. No nosso caso, o lado direito tem menos dentes (nomes de alimentos) e, portanto, para em "Donut".fonte
Não tenho os pontos de representante para postar na seção de comentários, mas para responder à pergunta relacionada:
O que você faria é usar Array.Resize () para preencher a sequência mais curta com os valores padrão e depois Zip () juntos.
Exemplo de código:
Resultado:
Observe que o uso de Array.Resize () tem uma ressalva : Redim Preserve in C #?
Se não se sabe qual sequência será a mais curta, uma função pode ser criada que a exclua:
Saída do .Zip () comum ao lado de ZipDefault ():
Voltando à resposta principal da pergunta original , outra coisa interessante que alguém pode querer fazer (quando os comprimentos das seqüências a serem "compactadas" são diferentes) é juntá-las de tal maneira que o final da lista corresponde ao invés do topo. Isso pode ser feito "pulando" o número apropriado de itens usando .Skip ().
Resultado:
fonte
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; } }
Muitas das respostas aqui demonstram
Zip
, mas sem realmente explicar um caso de uso da vida real que motivaria o usoZip
.Um padrão particularmente comum que
Zip
é fantástico para iterar sobre pares sucessivos de coisas. Isso é feito por iteração um enumeráveisX
com ele mesmo, pulando 1 elemento:x.Zip(x.Skip(1)
. Exemplo visual:Esses pares sucessivos são úteis para encontrar as primeiras diferenças entre os valores. Por exemplo, pares sucessivos de
IEnumable<MouseXPosition>
podem ser usados para produzirIEnumerable<MouseXDelta>
. Da mesma forma, osbool
valores amostrados de abutton
podem ser interpretados em eventos comoNotPressed
/Clicked
/Held
/Released
. Esses eventos podem direcionar chamadas para delegar métodos. Aqui está um exemplo:Impressões:
fonte
Como já foi dito, o Zip permite combinar duas coleções para uso em outras instruções do Linq ou em um loop foreach.
As operações que costumavam exigir um loop for e duas matrizes agora podem ser feitas em um loop foreach usando um objeto anônimo.
Um exemplo que acabei de descobrir, que é meio bobo, mas poderia ser útil se a paralelização fosse benéfica, seria uma travessia de fila única com efeitos colaterais:
timeSegments representa os itens atuais ou desenfileirados em uma fila (o último elemento é truncado por Zip). timeSegments.Skip (1) representa os itens seguintes ou espiar em uma fila. O método Zip combina esses dois em um único objeto anônimo com a propriedade Next e Current. Em seguida, filtramos com Where e fazemos alterações com AsParallel (). ForAll. É claro que o último bit poderia ser apenas uma foreach regular ou outra instrução Select que retorne os segmentos de tempo ofensivos.
fonte
O método Zip permite "mesclar" duas seqüências não relacionadas, usando um provedor de função de mesclagem por você, o chamador. O exemplo no MSDN é realmente muito bom em demonstrar o que você pode fazer com o Zip. Neste exemplo, você pega duas seqüências arbitrárias não relacionadas e as combina usando uma função arbitrária (nesse caso, apenas concatenando itens de ambas as seqüências em uma única sequência).
fonte
fonte