Linq में Enumerable.Zip विस्तार विधि का उपयोग क्या है?


130

Enumerable.ZipLinq में विस्तार विधि का उपयोग क्या है ?


2
क्या आप इसका उल्लेख कर रहे हैं: msdn.microsoft.com/en-us/library/dd267698.aspx ? - आप क्या खत्म करने की कोशिश कर रहे हैं?

13
यह एक ज़िप के दो किनारों की तरह है जो एक साथ आते हैं।
केविन पेंको

जवाबों:


191

ज़िप ऑपरेटर एक निर्दिष्ट चयनकर्ता फ़ंक्शन का उपयोग करके दो अनुक्रमों के संबंधित तत्वों को मिलाता है।

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

ouput

A1
B2
C3

41
मुझे यह उत्तर पसंद है क्योंकि यह दिखाता है कि क्या होता है जब तत्वों की संख्या मेल नहीं खाती है, msdn प्रलेखन के
DLeh

2
क्या होगा अगर मैं ज़िप जारी रखना चाहता हूं जहां एक सूची तत्वों से बाहर चलती है? जिस स्थिति में छोटी सूची तत्व को डिफ़ॉल्ट मान लेना चाहिए। इस मामले में आउटपुट A1, B2, C3, D0, E0 होना चाहिए।
लियांग

2
@ विकल्प दो विकल्प: ए) अपना खुद का Zipविकल्प लिखें । बी) के लिए एक विधि लिखें yield returnछोटी सूची के प्रत्येक तत्व, और उसके बाद जारी रखने के yield returning defaultअनिश्चित काल के लिए उसके बाद। (विकल्प बी के लिए आपको पहले से जानना आवश्यक है कि कौन सी सूची छोटी है।)
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);

इसे "ज़िप" कहा जाता है क्योंकि आप एक अनुक्रम को एक ज़िप के बाईं ओर के रूप में सोचते हैं, और दूसरा क्रम ज़िप के दाईं ओर के रूप में है, और ज़िप ऑपरेटर दोनों पक्षों को एक साथ खींचकर दांत निकाल देगा ( अनुक्रम के तत्व) उचित रूप से।


8
निश्चित रूप से यहाँ सबसे अच्छा स्पष्टीकरण।
मैक्सिम गेर्शकोविच

2
जिपर का उदाहरण पसंद आया। यह बहुत स्वाभाविक था। मेरी शुरुआती धारणा यह थी कि अगर इसका गति के साथ कुछ करना है या ऐसा कुछ है जैसे कि आप अपनी कार पर एक सड़क से गुजरते हैं।
RBT

23

यह दो अनुक्रमों के माध्यम से पुनरावृत्त करता है और उनके तत्वों को एक-एक करके, एक नए अनुक्रम में जोड़ता है। तो आप अनुक्रम ए का एक तत्व लेते हैं, इसे अनुक्रम बी से संबंधित तत्व के साथ बदलते हैं, और परिणाम अनुक्रम सी का एक तत्व बनाता है।

इसके बारे में सोचने का एक तरीका यह है कि यह 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
                          });
}

मुझे यह उत्तर भी पसंद आया, क्योंकि इसमें समानता का उल्लेख हैSelect
iliketocode

17

नाम Zipको आपको फेंकने न दें । यह एक फ़ाइल या एक फ़ोल्डर ज़िप करने के रूप में zipping के साथ कुछ नहीं करना है (संपीड़ित)। यह वास्तव में इसका नाम मिलता है कि कपड़े पर जिपर कैसे काम करता है: कपड़े पर जिपर के 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विधि बिल्कुल वैसा ही करना होगा: यह बंद हो जाएगा एक बार यह दोनों तरफ पिछले आइटम तक पहुँच गया है। हमारे मामले में दाईं ओर के दाँत (भोजन के नाम) कम हैं, इसलिए यह "डोनट" पर रुक जाएगा।


1
+1। हां, "ज़िप" नाम पहली बार में भ्रमित करने वाला हो सकता है। शायद "इंटरलीव" या "वीव" विधि के लिए अधिक वर्णनात्मक नाम होते।
BACON

1
@bacon हाँ, लेकिन फिर मैं अपने ज़िप उदाहरण का उपयोग करने में सक्षम नहीं होगा;) मुझे लगता है कि एक बार जब आप इसे ज़िपर की तरह समझ लेते हैं, तो यह बाद में बहुत सीधा होता है।
कोडिंगयॉशी

हालाँकि मुझे पता था कि ज़िप विस्तार विधि क्या करती है, मैं हमेशा से उत्सुक रहा हूँ कि इसका नाम ऐसा क्यों रखा गया। सॉफ्टवेयर ज़िप के सामान्य शब्दजाल में हमेशा कुछ और होता है। महान सादृश्य :-) आपने रचनाकार का मन पढ़ा होगा।
रघु रेड्डी मुत्ताना

7

मेरे पास टिप्पणी अनुभाग में पोस्ट करने के लिए प्रतिनिधि अंक नहीं हैं, लेकिन संबंधित प्रश्न का उत्तर देने के लिए:

क्या होगा अगर मैं जिप जारी रखना चाहता हूं जहां एक सूची तत्वों से बाहर चलती है? जिस स्थिति में छोटी सूची तत्व को डिफ़ॉल्ट मान लेना चाहिए। इस मामले में आउटपुट A1, B2, C3, D0, E0 होना चाहिए। - लियांग 19 '15 को 3:29 बजे

आप क्या करेंगे Array.Resize का उपयोग करें (डिफ़ॉल्ट मान के साथ छोटे अनुक्रम को पैड-आउट करने के लिए, और फिर उन्हें एक साथ ज़िप करें)।

कोड उदाहरण:

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 () का उपयोग करके एक चेतावनी है : C # में Redim संरक्षित करें?

यदि यह अज्ञात है कि कौन सा अनुक्रम छोटा होगा, तो एक फ़ंक्शन बनाया जा सकता है जो इसे बनाता है:

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

मूल प्रश्न के मुख्य उत्तर पर जा रहे हैं , एक और दिलचस्प बात जो कोई भी करना चाहता है (जब अनुक्रम की लंबाई "ज़िपित" अलग हो) उन्हें इस तरह से शामिल करना है ताकि सूची का अंत हो शीर्ष के बजाय मैच। यह। स्किप () का उपयोग करके उचित संख्या में "लंघन" द्वारा पूरा किया जा सकता है।

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

आउटपुट:

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; } }
पेजफॉल्ट

लगता है मुझे यकीन नहीं है कि एक टिप्पणी में कोड को सफलतापूर्वक कैसे प्रारूपित किया जाए ...
पेजफॉल्ट

7

यहां बहुत सारे उत्तर प्रदर्शित होते हैं Zip, लेकिन वास्तव में एक वास्तविक जीवन उपयोग-मामले की व्याख्या किए बिना जो उपयोग के लिए प्रेरित करेगा Zip

एक विशेष रूप से सामान्य पैटर्न जो Zipचीजों के क्रमिक जोड़े पर चलने के लिए शानदार है। यह X1 तत्व को छोड़ते हुए, अपने साथ एक एन्यूमरेट को पुनरावृत्त करके किया जाता है 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वैल्यूज़ को / / / buttonजैसी घटनाओं में व्याख्यायित किया जा सकता है । उन घटनाओं के बाद प्रतिनिधि विधियों को कॉल चला सकते हैं। यहाँ एक उदाहरण है:NotPressedClickedHeldReleased

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

6

जैसा कि दूसरों ने कहा है, जिप आपको आगे के लिन्क स्टेटमेंट्स या एक फॉरेक्स लूप में उपयोग के लिए दो संग्रह को संयोजित करने देता है।

लूप और दो सरणियों के लिए जिन ऑपरेशनों की आवश्यकता होती है, वे अब एक अनाम ऑब्जेक्ट का उपयोग करके फ़ॉरच लूप में किए जा सकते हैं।

एक उदाहरण जो मैंने अभी खोजा है, वह मूर्खतापूर्ण है, लेकिन उपयोगी हो सकता है अगर समानांतर लाभकारी होते हैं तो साइड इफेक्ट्स के साथ एक एकल पंक्ति क्यू ट्रैवर्सल होगा:

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 एक कतार में वर्तमान या हटाए गए आइटम का प्रतिनिधित्व करता है (अंतिम तत्व ज़िप द्वारा काट दिया जाता है)। timeSegments.Skip (1) एक कतार में अगले या तिरछी वस्तुओं का प्रतिनिधित्व करता है। ज़िप विधि इन दोनों को एक अगली और वर्तमान संपत्ति के साथ एक एकल अनाम वस्तु में जोड़ती है। फिर हम कहां से फ़िल्टर करते हैं और AsParallel () के साथ परिवर्तन करते हैं। बेशक अंतिम बिट नियमित रूप से फोरचेक हो सकता है या एक और चुनिंदा बयान हो सकता है जो आपत्तिजनक समय खंडों को लौटाता है।


3

ज़िप विधि आपको दो असंबंधित अनुक्रमों को "मर्ज" करने की अनुमति देता है, आपके द्वारा एक विलय फ़ंक्शन प्रदाता का उपयोग करके, कॉलर। 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

0
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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.