Оператор Zip объединяет соответствующие элементы двух последовательностей, используя указанную функцию-селектор.
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);
Мне нравится этот ответ, потому что он показывает, что происходит, когда количество элементов не совпадает, аналогично документации
msdn
2
что, если я хочу, чтобы zip продолжался там, где в одном списке заканчиваются элементы? в этом случае более короткий элемент списка должен принимать значение по умолчанию. В этом случае выведите A1, B2, C3, D0, E0.
лян
2
@liang Два варианта: A) Напишите свой собственный Zipвариант. Б) Написать метод yield returnкаждому элементу короткий список, а затем продолжить yield returnИНГ на defaultнеопределенный срок после этого. (Вариант B требует, чтобы вы знали заранее, какой список короче.)
jpaugh
105
Zipпредназначен для объединения двух последовательностей в одну. Например, если у вас есть последовательности
1,2,3
и
10,20,30
и вы хотите, чтобы последовательность, являющаяся результатом умножения элементов в одной позиции в каждой последовательности, получила
10,40,90
ты мог бы сказать
var left =new[]{1,2,3};var right =new[]{10,20,30};var products = left.Zip(right,(m, n)=> m * n);
Это называется «застежка-молния», потому что вы думаете об одной последовательности как о левой стороне застежки-молнии, а о другой последовательности как о правой стороне застежки-молнии, и оператор застежки-молнии стягивает две стороны вместе, создавая пары зубцов ( элементы последовательности) соответственно.
Очень понравился пример молнии. Это было так естественно. Мое первое впечатление было, связано ли это со скоростью или чем-то в этом роде, как будто вы мчитесь по улице на своей машине.
RBT
23
Он выполняет итерацию по двум последовательностям и объединяет их элементы один за другим в одну новую последовательность. Итак, вы берете элемент последовательности A, преобразуете его соответствующим элементом из последовательности B, и результат формирует элемент последовательности C.
Один из способов думать об этом - это то, что он похож на Select, за исключением того, что вместо преобразования элементов из одной коллекции он работает с двумя коллекциями одновременно.
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
Если бы вы сделали это в императивном коде, вы, вероятно, сделали бы что-то вроде этого:
for(int i =0; i < numbers.Length&& i < words.Length; i++){
numbersAndWords.Add(numbers[i]+" "+ words[i]);}
Или, если LINQ не был Zipв нем, вы могли бы сделать это:
var numbersAndWords = numbers.Select((num, i)=> num +" "+ words[i]);
Это полезно, когда данные разбиты на простые списки, похожие на массивы, каждый с одинаковой длиной и порядком, и каждый описывает разные свойства одного и того же набора объектов. Zipпомогает объединить эти фрагменты данных в более согласованную структуру.
Итак, если у вас есть массив имен состояний и другой массив их сокращений, вы можете сопоставить их в Stateкласс следующим образом:
Мне тоже понравился этот ответ, потому что в нем упоминается сходство сSelect
iliketocode
17
НЕ позволяйте имени Zipсбивать вас с толку. Это не имеет ничего общего с архивированием, как с архивированием файла или папки (сжатием). На самом деле он получил свое название от того, как работает застежка-молния на одежде: молния на одежде имеет 2 стороны, и каждая сторона имеет несколько зубцов. Когда вы идете в одном направлении, молния пронумеровывает (перемещается) с обеих сторон и закрывает молнию, стиснув зубы. Когда вы идете в другую сторону, зубы открываются. У вас либо открытая, либо закрытая молния.
То же самое и с Zipметодом. Рассмотрим пример, когда у нас есть две коллекции. Один содержит буквы, а другой - название продукта, которое начинается с этой буквы. Для наглядности я их называю leftSideOfZipperи rightSideOfZipper. Вот код.
var leftSideOfZipper =newList<string>{"A","B","C","D","E"};var rightSideOfZipper =newList<string>{"Apple","Banana","Coconut","Donut"};
Наша задача - создать одну коллекцию, в которой буква фруктов разделена буквой «А» :и ее название. Как это:
A :Apple
B :Banana
C :Coconut
D :Donut
Zipдля спасения. Чтобы не отставать от нашей терминологии застежки-молнии, мы будем называть этот результат closedZipperи элементы левой молнии, которую мы будем называть, leftToothи правой стороны, которые мы будем называть righToothпо очевидным причинам:
var closedZipper = leftSideOfZipper.Zip(rightSideOfZipper,(leftTooth, rightTooth)=> leftTooth +" : "+ rightTooth).ToList();
Выше мы перечисляем (перемещаемся) левую сторону молнии и правую сторону молнии и выполняем операцию с каждым зубом. Операция, которую мы выполняем, соединяет левый зуб (буква еды) с буквой a, :а затем правый зуб (название продукта). Мы делаем это с помощью этого кода:
Что произойдет, если вы перечисляете (натягиваете) настоящую молнию на одежде, и у одной стороны, неважно с левой или с правой стороны, меньше зубцов, чем с другой стороны? На этом молния остановится. ZipМетод будет делать точно так же: Он остановится , как только он достиг последнего пункта с обеих сторон. В нашем случае на правой стороне меньше зубов (названий продуктов), поэтому она остановится на «Пончике».
+1. Да, название «Zip» поначалу может сбивать с толку. Возможно, «Interleave» или «Weave» были бы более описательными названиями для метода.
BACON
1
@bacon, да, но тогда я бы не смог использовать свой пример с застежкой-молнией;) Думаю, как только вы поймете, что это как застежка-молния, потом все будет довольно просто.
CodingYoshi
Хотя я точно знал, что делает метод расширения Zip, мне всегда было любопытно, почему он так назван. На общем программном жаргоне zip всегда означал что-то другое. Отличная аналогия :-) Вы, должно быть, прочитали мысли создателя.
Рагху Редди Муттана
7
У меня нет репутации для публикации в разделе комментариев, но я хочу ответить на связанный вопрос:
Что, если я хочу, чтобы zip продолжался там, где в одном списке заканчиваются элементы? В этом случае более короткий элемент списка должен принимать значение по умолчанию. В этом случае выведите A1, B2, C3, D0, E0. - liang 19 нояб.
Что бы вы сделали, так это использовать Array.Resize (), чтобы дополнить более короткую последовательность значениями по умолчанию, а затем Zip () их вместе.
Пример кода:
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);
Вывод:
A1
B2
C3
D0
E0
Обратите внимание, что при использовании Array.Resize () есть предостережение : Redim Preserve в C #?
Если неизвестно, какая последовательность будет короче, можно создать функцию, которая ее обрабатывает:
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());}
Вывод простого .Zip () вместе с ZipDefault ():
A1 A1
B2 B2
C3 C3
D0
E0
Возвращаясь к основному ответу на исходный вопрос , еще одна интересная вещь, которую можно было бы сделать (когда длины «заархивируемых» последовательностей различаются), - это соединить их таким образом, чтобы конец списка совпадения вместо вершины. Этого можно добиться, «пропустив» соответствующее количество элементов с помощью .Skip ().
foreach(var s in letters.Skip(letters.Length- numbers.Length).Zip(numbers,(l, n)=> l + n.ToString()).ToArray())Console.WriteLine(s);
Изменение размера расточительно, особенно если какая-либо из коллекций велика. Что вам действительно нужно, так это иметь перечисление, которое продолжается после конца коллекции, заполняя его пустыми значениями по запросу (без вспомогательной коллекции). Вы можете сделать это с помощью: 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
Кажется, я не уверен, как правильно форматировать код в комментарии ...
Pagefault
7
Многие ответы здесь демонстрируют Zip, но без реального объяснения реального варианта использования, который мотивировал бы использование Zip.
Один из наиболее распространенных паттернов, который Zipотлично подходит для перебора последовательных пар вещей. Это делается итерируя Перечислимый Xс собой, пропустив 1 элемент: x.Zip(x.Skip(1). Визуальный пример:
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)
Эти последовательные пары полезны для поиска первых различий между значениями. Например, IEnumable<MouseXPosition>для производства могут использоваться следующие друг за другом пары IEnumerable<MouseXDelta>. Точно так же выборочные boolзначения a buttonможно интерпретировать в такие события, как NotPressed/ Clicked/ Held/ Released. Затем эти события могут вызывать вызовы методов делегирования. Вот пример:
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));}}
Как утверждали другие, Zip позволяет объединить две коллекции для использования в дальнейших операторах Linq или в цикле foreach.
Операции, которые раньше требовали цикла for и двух массивов, теперь могут выполняться в цикле foreach с использованием анонимного объекта.
Пример, который я только что обнаружил, выглядит глупо, но мог бы быть полезным, если бы распараллеливание было полезным, было бы обход очереди одной строки с побочными эффектами:
timeSegments представляет текущие или исключенные из очереди элементы в очереди (последний элемент усекается Zip). timeSegments.Skip (1) представляет следующие или просматриваемые элементы в очереди. Метод Zip объединяет эти два объекта в один анонимный объект со свойствами Next и Current. Затем мы фильтруем с помощью Where и вносим изменения с помощью AsParallel (). ForAll. Конечно, последний бит может быть просто обычным foreach или другим оператором Select, который возвращает неправильные временные сегменты.
Метод Zip позволяет вам «объединить» две несвязанные последовательности, используя провайдер функции слияния, сделанный вами, вызывающей стороной. Пример на MSDN на самом деле довольно хорошо демонстрирует, что вы можете делать с Zip. В этом примере вы берете две произвольные, несвязанные последовательности и объединяете их с помощью произвольной функции (в данном случае просто объединяете элементы из обеих последовательностей в одну строку).
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
Ответы:
Оператор Zip объединяет соответствующие элементы двух последовательностей, используя указанную функцию-селектор.
Ouput
источник
Zip
вариант. Б) Написать методyield return
каждому элементу короткий список, а затем продолжитьyield return
ИНГ наdefault
неопределенный срок после этого. (Вариант B требует, чтобы вы знали заранее, какой список короче.)Zip
предназначен для объединения двух последовательностей в одну. Например, если у вас есть последовательностии
и вы хотите, чтобы последовательность, являющаяся результатом умножения элементов в одной позиции в каждой последовательности, получила
ты мог бы сказать
Это называется «застежка-молния», потому что вы думаете об одной последовательности как о левой стороне застежки-молнии, а о другой последовательности как о правой стороне застежки-молнии, и оператор застежки-молнии стягивает две стороны вместе, создавая пары зубцов ( элементы последовательности) соответственно.
источник
Он выполняет итерацию по двум последовательностям и объединяет их элементы один за другим в одну новую последовательность. Итак, вы берете элемент последовательности A, преобразуете его соответствующим элементом из последовательности B, и результат формирует элемент последовательности C.
Один из способов думать об этом - это то, что он похож на
Select
, за исключением того, что вместо преобразования элементов из одной коллекции он работает с двумя коллекциями одновременно.Из статьи MSDN о методе :
Если бы вы сделали это в императивном коде, вы, вероятно, сделали бы что-то вроде этого:
Или, если LINQ не был
Zip
в нем, вы могли бы сделать это:Это полезно, когда данные разбиты на простые списки, похожие на массивы, каждый с одинаковой длиной и порядком, и каждый описывает разные свойства одного и того же набора объектов.
Zip
помогает объединить эти фрагменты данных в более согласованную структуру.Итак, если у вас есть массив имен состояний и другой массив их сокращений, вы можете сопоставить их в
State
класс следующим образом:источник
Select
НЕ позволяйте имени
Zip
сбивать вас с толку. Это не имеет ничего общего с архивированием, как с архивированием файла или папки (сжатием). На самом деле он получил свое название от того, как работает застежка-молния на одежде: молния на одежде имеет 2 стороны, и каждая сторона имеет несколько зубцов. Когда вы идете в одном направлении, молния пронумеровывает (перемещается) с обеих сторон и закрывает молнию, стиснув зубы. Когда вы идете в другую сторону, зубы открываются. У вас либо открытая, либо закрытая молния.То же самое и с
Zip
методом. Рассмотрим пример, когда у нас есть две коллекции. Один содержит буквы, а другой - название продукта, которое начинается с этой буквы. Для наглядности я их называюleftSideOfZipper
иrightSideOfZipper
. Вот код.Наша задача - создать одну коллекцию, в которой буква фруктов разделена буквой «А»
:
и ее название. Как это:Zip
для спасения. Чтобы не отставать от нашей терминологии застежки-молнии, мы будем называть этот результатclosedZipper
и элементы левой молнии, которую мы будем называть,leftTooth
и правой стороны, которые мы будем называтьrighTooth
по очевидным причинам:Выше мы перечисляем (перемещаемся) левую сторону молнии и правую сторону молнии и выполняем операцию с каждым зубом. Операция, которую мы выполняем, соединяет левый зуб (буква еды) с буквой a,
:
а затем правый зуб (название продукта). Мы делаем это с помощью этого кода:Конечный результат таков:
Что случилось с последней буквой Е?
Что произойдет, если вы перечисляете (натягиваете) настоящую молнию на одежде, и у одной стороны, неважно с левой или с правой стороны, меньше зубцов, чем с другой стороны? На этом молния остановится.
Zip
Метод будет делать точно так же: Он остановится , как только он достиг последнего пункта с обеих сторон. В нашем случае на правой стороне меньше зубов (названий продуктов), поэтому она остановится на «Пончике».источник
У меня нет репутации для публикации в разделе комментариев, но я хочу ответить на связанный вопрос:
Что бы вы сделали, так это использовать Array.Resize (), чтобы дополнить более короткую последовательность значениями по умолчанию, а затем Zip () их вместе.
Пример кода:
Вывод:
Обратите внимание, что при использовании Array.Resize () есть предостережение : Redim Preserve в C #?
Если неизвестно, какая последовательность будет короче, можно создать функцию, которая ее обрабатывает:
Вывод простого .Zip () вместе с ZipDefault ():
Возвращаясь к основному ответу на исходный вопрос , еще одна интересная вещь, которую можно было бы сделать (когда длины «заархивируемых» последовательностей различаются), - это соединить их таким образом, чтобы конец списка совпадения вместо вершины. Этого можно добиться, «пропустив» соответствующее количество элементов с помощью .Skip ().
Вывод:
источник
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; } }
Многие ответы здесь демонстрируют
Zip
, но без реального объяснения реального варианта использования, который мотивировал бы использованиеZip
.Один из наиболее распространенных паттернов, который
Zip
отлично подходит для перебора последовательных пар вещей. Это делается итерируя ПеречислимыйX
с собой, пропустив 1 элемент:x.Zip(x.Skip(1)
. Визуальный пример:Эти последовательные пары полезны для поиска первых различий между значениями. Например,
IEnumable<MouseXPosition>
для производства могут использоваться следующие друг за другом парыIEnumerable<MouseXDelta>
. Точно так же выборочныеbool
значения abutton
можно интерпретировать в такие события, какNotPressed
/Clicked
/Held
/Released
. Затем эти события могут вызывать вызовы методов делегирования. Вот пример:Печать:
источник
Как утверждали другие, Zip позволяет объединить две коллекции для использования в дальнейших операторах Linq или в цикле foreach.
Операции, которые раньше требовали цикла for и двух массивов, теперь могут выполняться в цикле foreach с использованием анонимного объекта.
Пример, который я только что обнаружил, выглядит глупо, но мог бы быть полезным, если бы распараллеливание было полезным, было бы обход очереди одной строки с побочными эффектами:
timeSegments представляет текущие или исключенные из очереди элементы в очереди (последний элемент усекается Zip). timeSegments.Skip (1) представляет следующие или просматриваемые элементы в очереди. Метод Zip объединяет эти два объекта в один анонимный объект со свойствами Next и Current. Затем мы фильтруем с помощью Where и вносим изменения с помощью AsParallel (). ForAll. Конечно, последний бит может быть просто обычным foreach или другим оператором Select, который возвращает неправильные временные сегменты.
источник
Метод Zip позволяет вам «объединить» две несвязанные последовательности, используя провайдер функции слияния, сделанный вами, вызывающей стороной. Пример на MSDN на самом деле довольно хорошо демонстрирует, что вы можете делать с Zip. В этом примере вы берете две произвольные, несвязанные последовательности и объединяете их с помощью произвольной функции (в данном случае просто объединяете элементы из обеих последовательностей в одну строку).
источник
источник