Enumerable.Zip
Linq에서 확장 방법을 사용하는 것은 무엇입니까 ?
답변
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
은 단일 컬렉션에서 항목을 변환하는 대신 한 번에 두 컬렉션에서 작동한다는 점을 제외하고 와 비슷하다는 것 입니다.
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
방법 과 동일한 아이디어입니다 . 두 개의 컬렉션이있는 예를 고려하십시오. 하나는 편지를, 다른 하나는 그 편지로 시작하는 음식 품목의 이름을 보유합니다. 명확성을 위해 내가 그들을 호출하고 leftSideOfZipper
와 rightSideOfZipper
. 코드는 다음과 같습니다.
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는 어떻게 되었습니까?
실제 옷 지퍼와 한쪽을 열거 (풀링)하는 경우 왼쪽이나 오른쪽이 중요하지 않고 다른 쪽보다 치아가 적 으면 어떻게됩니까? 지퍼는 거기서 멈출 것이다. 이 Zip
방법은 정확히 동일하게 수행됩니다. 한 쪽의 마지막 항목에 도달하면 중지됩니다. 우리의 경우 오른쪽에는 이빨 (식품 이름)이 적으므로 “도넛”에서 멈 춥니 다.
답변
의견 섹션에 게시 할 담당자가 없지만 관련 질문에 답변해야합니다.
하나의 목록에서 요소가 부족한 곳에서 zip을 계속 사용하려면 어떻게해야합니까? 이 경우 더 짧은 목록 요소가 기본값을 가져야합니다. 이 경우 출력은 A1, B2, C3, D0, E0이됩니다. – liang 11 월 19 일 15시 3:29
할 일은 Array.Resize ()를 사용하여 더 짧은 시퀀스를 기본값으로 채운 다음 Zip ()을 함께 사용하는 것입니다.
코드 예 :
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);
산출:
A1
B2
C3
D0
E0
Array.Resize () 를 사용하면주의 사항이 있습니다 : Redim Preserve in C #?
어떤 시퀀스가 더 짧은 시퀀스인지 알 수없는 경우이를 일시 중단하는 함수를 만들 수 있습니다.
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());
}
ZipDefault ()와 함께 일반 .Zip () 출력 :
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);
산출:
C1
D2
E3
답변
여기에 많은 답변이 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>
. 마찬가지로, a의 샘플링 된 bool
값은 / / / button
와 같은 이벤트로 해석 될 수 있습니다 . 그런 다음 해당 이벤트는 메소드를 위임하기위한 호출을 유발할 수 있습니다. 예를 들면 다음과 같습니다.NotPressed
Clicked
Held
Released
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) );
}
}
인쇄물:
NotPressesd
NotPressesd
NotPressesd
Clicked
Held
Held
Released
Clicked
Released
NotPressesd
Clicked
답변
다른 사람들이 언급했듯이 Zip을 사용하면 추가 Linq 문 또는 foreach 루프에 사용하기 위해 두 컬렉션을 결합 할 수 있습니다.
for 루프와 두 개의 배열이 필요한 작업은 이제 익명 객체를 사용하여 foreach 루프에서 수행 할 수 있습니다.
방금 발견 한 예는 어리석은 일이지만 병렬 처리가 유익한 경우 부작용이있는 단일 행 큐 순회가 유용 할 수 있습니다.
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는 대기열에서 현재 또는 대기열에서 제외 된 항목을 나타냅니다 (마지막 요소는 Zip으로 잘립니다). timeSegments.Skip (1)은 큐에서 다음 또는 엿보기 항목을 나타냅니다. Zip 메서드는이 두 속성을 Next 및 Current 속성을 사용하여 단일 익명 개체로 결합합니다. 그런 다음 Where로 필터링하고 AsParallel (). ForAll을 사용하여 변경합니다. 물론 마지막 비트는 규칙적인 foreach 또는 문제가되는 시간 세그먼트를 반환하는 다른 Select 문일 수 있습니다.