program story

Linq에서 Enumerable.Zip 확장 방법은 무엇입니까?

inputbox 2020. 7. 24. 20:52
반응형

Linq에서 Enumerable.Zip 확장 방법은 무엇입니까?


Enumerable.ZipLinq에서 확장 방법을 사용하는 것은 무엇입니까 ?


Zip 연산자는 지정된 선택기 함수를 사용하여 두 시퀀스의 해당 요소를 병합합니다.

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);

출력

A1
B2
C3

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);

하나의 시퀀스를 지퍼의 왼쪽으로, 다른 시퀀스를 지퍼의 오른쪽으로 생각하기 때문에 "zip"이라고하며, 지퍼 연산자는 양측을 함께 치아에서 떼어냅니다. 순서의 요소).


두 시퀀스를 반복하고 해당 요소를 하나씩 새 단일 시퀀스로 결합합니다. 따라서 시퀀스 A의 요소를 가져 와서 시퀀스 B의 해당 요소로 변환하면 결과가 시퀀스 C의 요소를 형성합니다.

생각하는 한 가지 방법 Select은 단일 컬렉션에서 항목을 변환하는 대신 한 번에 두 컬렉션에서 작동한다는 점을 제외하고는와 비슷하다는 것 입니다.

방법에 대한 MSDN 기사에서 :

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과 같이 클래스 로 조합 할 수 있습니다 .

IEnumerable<State> GetListOfStates(string[] stateNames, int[] statePopulations)
{
    return stateNames.Zip(statePopulations, 
                          (name, population) => new State()
                          {
                              Name = name,
                              Population = population
                          });
}

이름이 Zip당신을 버리지 못하게하십시오 . 파일이나 폴더를 압축하는 것과 같이 압축하는 것과는 아무런 관련이 없습니다 (압축). 그것은 실제로 옷의 지퍼가 작동하는 방식에서 이름을 얻습니다. 옷의 지퍼는 2면이 있고 각면에는 많은 이가 있습니다. 한 방향으로 갈 때 지퍼는 양쪽을 열거하고 (이동) 치아를 꽉 쥐어 지퍼를 닫습니다. 다른 방향으로 가면 이가 열립니다. 열려 있거나 닫힌 지퍼로 끝납니다.

Zip방법 과 동일한 아이디어입니다 . 두 개의 컬렉션이있는 예를 고려하십시오. 하나는 편지를, 다른 하나는 그 편지로 시작하는 음식 품목의 이름을 보유합니다. 명확성을 위해 내가 그들을 호출하고 leftSideOfZipperrightSideOfZipper. 코드는 다음과 같습니다.

var leftSideOfZipper = new List<string> { "A", "B", "C", "D", "E" };
var rightSideOfZipper = new List<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();

위에서 우리는 지퍼의 왼쪽과 지퍼의 오른쪽을 열거하고 (이동) 각 치아에서 작업을 수행합니다. 우리가 수행하는 작업은 왼쪽 치아 (식품 편지)와 :오른쪽 치아 (식품 이름)를 연결하는 것입니다. 우리는이 코드를 사용하여 그렇게합니다 :

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

최종 결과는 다음과 같습니다.

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

마지막 문자 E는 어떻게 되었습니까?

If you are enumerating (pulling) a real clothes zipper and one side, does not matter the left side or the right side, has less teeth than the other side, what will happen? Well the zipper will stop there. The Zip method will do exactly the same: It will stop once it has reached the last item on either side. In our case the right side has less teeth (food names) so it will stop at "Donut".


I don't have the rep points to post in the comments section, but to answer the related question :

What if I want zip to continue where one list run out of elements? In which case the shorter list element should take default value. Output in this case to be A1, B2, C3, D0, E0. – liang Nov 19 '15 at 3:29

What you would do is to use Array.Resize() to pad-out the shorter sequence with default values, and then Zip() them together.

Code example :

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);

Output:

A1
B2
C3
D0
E0

Please note that using Array.Resize() has a caveat : Redim Preserve in C#?

If it is unknown which sequence will be the shorter one, a function can be created that susses it:

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()); 
}

Output of plain .Zip() alongside ZipDefault() :

A1 A1
B2 B2
C3 C3
   D0
   E0

Going back to the main answer of the original question, another interesting thing that one might wish to do (when the lengths of the sequences to be "zipped" are different) is to join them in such a way so that the end of the list matches instead of the top. This can be accomplished by "skipping" the appropriate number of items using .Skip().

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

Output:

C1
D2
E3

As others have stated, Zip lets you combine two collections for use in further Linq statements or a foreach loop.

Operations that used to require a for loop and two arrays can now be done in a foreach loop using an anonymous object.

An example I just discovered, that is kind of silly, but could be useful if parallelization were beneficial would be a single line Queue traversal with side effects:

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 represents the current or dequeued items in a queue (the last element is truncated by Zip). timeSegments.Skip(1) represents the next or peek items in a queue. The Zip method combines these two into a single anonymous object with a Next and Current property. Then we filter with Where and make changes with AsParallel().ForAll. Of course the last bit could just be a regular foreach or another Select statement that returns the offending time segments.


A lot of the answers here demonstrate Zip, but without really explaining a real life use-case that would motivate the use of Zip.

One particularly common pattern that Zip is fantastic for iterating over successive pairs of things. This is done by iterating an enumerable X with itself, skipping 1 element: x.Zip(x.Skip(1). Visual Example:

 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)

These successive pairs are useful for finding the first differences between values. For example, successive pairs of IEnumable<MouseXPosition> can be used to produce IEnumerable<MouseXDelta>. Similarly, sampled bool values of a button can be interpretted into events like NotPressed/Clicked/Held/Released. Those events can then drive calls to delegate methods. Here's an example:

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) );
    }
}

Prints:

NotPressesd
NotPressesd
NotPressesd
Clicked
Held
Held
Released
Clicked
Released
NotPressesd
Clicked

The Zip method allows you to "merge" two unrelated sequences, using a merging function provider by you, the caller. The example on MSDN is actually pretty good at demonstrating what you can do with Zip. In this example, you take two arbitrary, unrelated sequences, and combine them using an arbitrary function (in this case, just concatenating items from both sequences into a single string).

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

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

참고URL : https://stackoverflow.com/questions/5122737/what-is-the-use-of-enumerable-zip-extension-method-in-linq

반응형