Enumerable.Zip
Linq'te uzatma yönteminin kullanımı nedir ?
Enumerable.Zip
Linq'te uzatma yönteminin kullanımı nedir ?
Yanıtlar:
Zip operatörü, belirtilen bir seçici işlevini kullanarak iki dizinin karşılık gelen öğelerini birleştirir.
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);
Çıkışı
A1
B2
C3
Zip
alternatifinizi yazın . B) bir yöntem yazın yield return
daha kısa listenin her bir elemanına ve ardından devam yield return
ing default
süresiz bundan sonra. (B Seçeneği, hangi listenin daha kısa olduğunu önceden bilmenizi gerektirir.)
Zip
iki diziyi tek bir dizide birleştirmek içindir. Örneğin, dizileriniz varsa
1, 2, 3
ve
10, 20, 30
ve her dizide aynı konumdaki öğelerin çarpılmasının sonucu olan dizinin
10, 40, 90
söyleyebilirdin
var left = new[] { 1, 2, 3 };
var right = new[] { 10, 20, 30 };
var products = left.Zip(right, (m, n) => m * n);
Bir sırayı fermuarın sol tarafı olarak ve diğer sıralamayı fermuarın sağ tarafı olarak düşündüğünüz için "fermuar" olarak adlandırılır ve fermuar operatörü dişleri eşleştirerek iki tarafı birlikte çeker ( dizinin öğeleri) uygun şekilde.
İki diziyi yineler ve öğelerini tek tek tek bir yeni dizide birleştirir. Yani, A dizisindeki bir elemanı alıp, onu B dizisindeki karşılık gelen elemanla dönüştürürsünüz ve sonuç C dizisinin bir elemanını oluşturur.
Bunu düşünmenin bir yolu Select
, aynı koleksiyondaki öğeleri tek bir koleksiyondan dönüştürmek yerine, aynı anda iki koleksiyon üzerinde çalışması dışında benzer olmasıdır .
Gönderen yöntemine MSDN makalesinde :
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
Bunu emir kodu ile yapacak olsaydın, muhtemelen şöyle bir şey yapardın:
for (int i = 0; i < numbers.Length && i < words.Length; i++)
{
numbersAndWords.Add(numbers[i] + " " + words[i]);
}
Veya LINQ yoksa Zip
, bunu yapabilirdiniz:
var numbersAndWords = numbers.Select(
(num, i) => num + " " + words[i]
);
Bu, her biri aynı uzunluk ve sıraya sahip ve her biri aynı nesne kümesinin farklı bir özelliğini açıklayan basit, dizi benzeri listelere yayılmış verileriniz olduğunda kullanışlıdır. Zip
bu veri parçalarını daha tutarlı bir yapıya dönüştürmenize yardımcı olur.
Dolayısıyla, bir dizi durum adınız ve bunların başka bir kısaltmaları dizisine sahipseniz, bunları aşağıdaki State
gibi bir sınıfta harmanlayabilirsiniz :
IEnumerable<State> GetListOfStates(string[] stateNames, int[] statePopulations)
{
return stateNames.Zip(statePopulations,
(name, population) => new State()
{
Name = name,
Population = population
});
}
Select
İsmin Zip
sizi atmasına izin VERMEYİN . Bir dosyayı veya klasörü sıkıştırırken (sıkıştırma) olduğu gibi sıkıştırmayla ilgisi yoktur. Aslında ismini giysilerin üzerindeki bir fermuarın çalışma şeklinden alıyor: Giysilerin üzerindeki fermuarın 2 tarafı ve her iki tarafında da bir demet diş var. Tek yöne gittiğinizde fermuar her iki tarafı numaralandırır (hareket eder) ve dişleri sıkarak fermuarı kapatır. Diğer yöne gittiğinizde dişleri açar. Ya açık ya da kapalı bir fermuarla bitirirsiniz.
Zip
Yöntemle aynı fikirdir . İki koleksiyonumuzun olduğu bir örnek düşünün. Biri harfleri, diğeri de o harfle başlayan bir gıda maddesinin adını tutar. Açıklık amacıyla onları arıyorum leftSideOfZipper
ve rightSideOfZipper
. İşte kod.
var leftSideOfZipper = new List<string> { "A", "B", "C", "D", "E" };
var rightSideOfZipper = new List<string> { "Apple", "Banana", "Coconut", "Donut" };
Görevimiz, meyvenin harfini a :
ve adı ile ayrılmış bir koleksiyon oluşturmaktır . Bunun gibi:
A : Apple
B : Banana
C : Coconut
D : Donut
Zip
kurtarmak için. Fermuar terminolojimize ayak uydurmak için bu sonuca closedZipper
ve arayacağımız sol fermuarın eşyalarına leftTooth
, sağ tarafa da righTooth
bariz nedenlerle arayacağız :
var closedZipper = leftSideOfZipper
.Zip(rightSideOfZipper, (leftTooth, rightTooth) => leftTooth + " : " + rightTooth).ToList();
Yukarıda fermuarın sol tarafını ve fermuarın sağ tarafını sıralıyoruz (seyahat ediyor) ve her dişe bir işlem yapıyoruz. Yaptığımız operasyon sol dişi (yemek harfi) önce a :
ve ardından sağ diş (yemek adı) ile birleştirmektir. Bunu şu kodu kullanarak yapıyoruz:
(leftTooth, rightTooth) => leftTooth + " : " + rightTooth)
Sonuç şudur:
A : Apple
B : Banana
C : Coconut
D : Donut
Son E harfine ne oldu?
Gerçek bir elbise fermuarını sayıyorsanız (çekiyorsanız) ve bir tarafı, sol tarafı veya sağ tarafı önemli değil, diğer tarafa göre daha az dişe sahipse ne olur? Peki fermuar orada duracak. Zip
Yöntem tamamen aynı olacaktır: Duracak o iki tarafında son öğeyi ulaştığında. Bizim durumumuzda, sağ tarafta daha az diş (yiyecek isimleri) vardır, bu yüzden "Donut" da duracaktır.
Yorumlar bölümünde yayınlayacak temsilci puanlarım yok, ancak ilgili soruyu cevaplamak için:
Bir listenin öğeleri kalmadığında zip'in devam etmesini istersem ne olur? Bu durumda, daha kısa liste öğesi varsayılan değeri almalıdır. Bu durumda çıktı A1, B2, C3, D0, E0 olacaktır. - liang 19 Kasım 2015, 3:29
Yapacağınız şey, daha kısa diziyi varsayılan değerlerle doldurmak için Array.Resize () kullanmak ve ardından bunları birlikte Zip () kullanmaktır.
Kod örneği:
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);
Çıktı:
A1
B2
C3
D0
E0
Array.Resize () kullanmanın bir uyarı olduğunu lütfen unutmayın : C # içinde Redim Preserve?
Hangi dizinin daha kısa olacağı bilinmiyorsa, onu destekleyen bir işlev oluşturulabilir:
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 () ile birlikte düz .Zip () çıktısı:
A1 A1
B2 B2
C3 C3
D0
E0
Orijinal sorunun ana cevabına geri dönecek olursak, yapmak isteyebileceğiniz bir başka ilginç şey ("sıkıştırılacak" dizilerin uzunlukları farklı olduğunda), listenin sonuna gelecek şekilde onları birleştirmektir. üst yerine eşleşir. Bu, .Skip () kullanılarak uygun sayıda öğe "atlanarak" gerçekleştirilebilir.
foreach (var s in letters.Skip(letters.Length - numbers.Length).Zip(numbers, (l, n) => l + n.ToString()).ToArray())
Console.WriteLine(s);
Çıktı:
C1
D2
E3
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; } }
Buradaki cevapların çoğu gösteriyor Zip
, ancak kullanımını motive edecek gerçek bir kullanım durumunu gerçekten açıklamadan Zip
.
Zip
Birbirini izleyen şey çiftlerini yinelemek için harika olan özellikle yaygın bir model . Bu bir enumerator yineleme yapılır X
1 elemanının atlayarak, kendisi ile: x.Zip(x.Skip(1)
. Görsel Örnek:
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)
Bu ardışık çiftler, değerler arasındaki ilk farkları bulmak için kullanışlıdır. Örneğin, ardışık çiftler IEnumable<MouseXPosition>
üretmek için kullanılabilir IEnumerable<MouseXDelta>
. Benzer şekilde, a'nın örneklenmiş bool
değerleri / / / button
gibi olaylara yorumlanabilir . Bu olaylar, daha sonra yöntemlerin temsilciliğine yönelik çağrıları yönlendirebilir. İşte bir örnek: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) );
}
}
Baskılar:
NotPressesd
NotPressesd
NotPressesd
Clicked
Held
Held
Released
Clicked
Released
NotPressesd
Clicked
Diğerlerinin de belirttiği gibi Zip, iki koleksiyonu birleştirip daha fazla Linq ifadesinde veya bir foreach döngüsünde kullanmanıza izin verir.
Bir for döngüsü ve iki dizi gerektiren işlemler artık anonim bir nesne kullanılarak bir foreach döngüsünde yapılabilir.
Az önce keşfettiğim bir örnek, bu biraz saçma, ancak paralelleştirme yararlı olsaydı yararlı olabilirdi, yan etkileri olan tek bir kuyruk geçişi olurdu:
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, bir kuyruktaki geçerli veya kuyruğu kaldırılmış öğeleri temsil eder (son öğe Zip tarafından kesilir). timeSegments.Skip (1), bir kuyruktaki sonraki veya gözetleme öğelerini temsil eder. Zip yöntemi, bu ikisini Next ve Current özelliğine sahip tek bir anonim nesnede birleştirir. Sonra Where ile filtreliyoruz ve AsParallel (). ForAll ile değişiklik yapıyoruz. Elbette son bit, normal bir foreach veya rahatsız edici zaman dilimlerini döndüren başka bir Select ifadesi olabilir.
Zip yöntemi, sizin tarafınızdan, yani arayan tarafından bir birleştirme işlevi sağlayıcısı kullanarak iki ilgisiz diziyi "birleştirmenize" olanak tanır. MSDN'deki örnek, Zip ile neler yapabileceğinizi göstermede oldukça iyidir. Bu örnekte, iki rastgele, ilgisiz dizi alır ve bunları rastgele bir işlev kullanarak birleştirirsiniz (bu durumda, her iki dizideki öğeleri tek bir dizede birleştirerek).
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