फ़ॉरच लूप के वर्तमान पुनरावृत्ति का सूचकांक आपको कैसे मिलता है?


938

क्या कुछ दुर्लभ भाषा का निर्माण है जिसका मैंने सामना नहीं किया है (जैसे कि मैंने हाल ही में कुछ सीखा है, कुछ स्टैक ओवरफ्लो पर) C # में एक फॉरेस्ट लूप की वर्तमान पुनरावृत्ति का प्रतिनिधित्व करने वाला मान प्राप्त करने के लिए?

उदाहरण के लिए, मैं वर्तमान में परिस्थितियों के आधार पर ऐसा कुछ करता हूं:

int i = 0;
foreach (Object o in collection)
{
    // ...
    i++;
}

1
फोरचेक कास्टिंग पुनर्प्राप्ति आम तौर पर मेरे लिए एक संग्रह पर सूचकांक-आधारित पहुंच का उपयोग करने की तुलना में अधिक अनुकूलित नहीं है, हालांकि कई मामलों में यह बराबर होगा। फॉर्च्यूनर का उद्देश्य आपके कोड को पठनीय बनाना है, लेकिन यह (आमतौर पर) अप्रत्यक्ष की एक परत जोड़ता है, जो मुफ़्त नहीं है।
ब्रायन

8
मैं कहूंगा कि इसका मुख्य उद्देश्य foreachसभी संग्रहों के लिए एक सामान्य पुनरावृत्ति तंत्र प्रदान करना है, भले ही वे इंडेक्सेबल हों ( Listया नहीं Dictionary)।
ब्रायन गिदोन

2
हाय ब्रायन गिदोन - निश्चित रूप से सहमत हैं (यह कुछ साल पहले था और मैं उस समय बहुत कम अनुभवी था)। हालाँकि, जब Dictionaryयह अनुक्रमणीय नहीं होता है , तो इसका एक पुनरावृत्ति Dictionaryएक विशेष क्रम में इसे पार करता है (अर्थात एक एन्यूमर इस तथ्य से अनुक्रमित होता है कि यह तत्वों को क्रमिक रूप से प्राप्त करता है)। इस अर्थ में, हम कह सकते हैं कि हम संग्रह के भीतर सूचकांक की तलाश नहीं कर रहे हैं, बल्कि गणना के भीतर वर्तमान गणना तत्व के सूचकांक (यानी कि क्या हम पहले या पांचवें या अंतिम गणना किए गए तत्व पर हैं)।
मैट मिशेल

4
foreach भी संकलक को संकलित कोड में प्रत्येक ऐरे एक्सेस की जाँच करने की सीमा को छोड़ देता है। एक इंडेक्स के साथ उपयोग करने से रनटाइम चेक हो जाएगा कि आपका इंडेक्स एक्सेस सुरक्षित है या नहीं।
IvoTops

1
लेकिन यह झूठा है। यदि आप लूप के भीतर लूप के लिए पुनरावृत्ति चर को नहीं बदलते हैं, तो कंपाइलर जानता है कि उसकी सीमाएं क्या हैं और उन्हें फिर से जांचने की आवश्यकता नहीं है। यह एक ऐसा सामान्य मामला है कि कोई भी सभ्य संकलक इसे लागू करेगा।
जिम बेल्टर

जवाबों:


550

foreachसंग्रह है कि लागू अधिक पुनरावृत्ति के लिए है IEnumerable। यह GetEnumeratorसंग्रह पर कॉल करके ऐसा करता है , जो वापस आ जाएगा Enumerator

इस एन्यूमरेटर की एक विधि और एक संपत्ति है:

  • MoveNext ()
  • वर्तमान

Currentवर्तमान में Enumerator जिस ऑब्जेक्ट पर लौटा है, वह अगली ऑब्जेक्ट पर MoveNextअपडेट Currentहोता है।

सूचकांक की अवधारणा अभिज्ञान की अवधारणा के लिए विदेशी है, और ऐसा नहीं किया जा सकता है।

उसके कारण, अधिकांश संग्रह अनुक्रमणिका और लूप निर्माण के लिए उपयोग किए जा सकते हैं।

मैं स्थानीय चर के साथ सूचकांक को ट्रैक करने की तुलना में इस स्थिति में लूप के लिए उपयोग करना पसंद करता हूं।


165
"जाहिर है, एक सूचकांक की अवधारणा अभिज्ञान की अवधारणा के लिए विदेशी है, और नहीं किया जा सकता है।" - यह बकवास है, क्योंकि डेविड बी और bcahill के जवाब स्पष्ट करते हैं। एक सूचकांक एक सीमा पर एक गणना है, और कोई कारण नहीं है कि कोई दो चीजों को समानांतर में गणना नहीं कर सकता है ... यह वास्तव में Enumerable.Select का अनुक्रमण रूप है।
जिम बाल्टर

11
मूल कोड उदाहरण:for(var i = 0; i < myList.Count; i ++){System.Diagnostics.Debug.WriteLine(i);}
चाड हेडगॉक

22
@JimBalter: यह पहला लिंक है जो मैंने पढ़ा है। मैं यह नहीं देखता कि यह आपकी स्थिति का समर्थन कैसे करता है। जवाब देते समय मैं लोगों पर विज्ञापन-हम्स और विभाजनकारी शब्दों को नहीं फेंकता। मैं एक उदाहरण के रूप में "बकवास", "असाधारण भ्रम", "पूरी तरह से गलत और भ्रमित", "मुझे 37 अपवोट्स", आदि का उपयोग करने का हवाला देता है (मुझे लगता है कि आपका अहंकार यहां लाइन पर थोड़ा सा है।) इसके बजाय, मैं। आप विनम्रता से पूछना चाहते हैं कि आप अपने 'डिबगम ऐड वर्चुंडियम' के साथ दूसरों को धोखा नहीं देते हैं और मैं आपको स्टैकऑवरफ्लो पर यहां लोगों का समर्थन करने के तरीकों के बारे में सोचने के लिए प्रोत्साहित करना चाहता हूं। मैं कहता हूँ कि जॉन स्कीट उस संबंध में एक मॉडल नागरिक है।
प्रेट्ज़ेल

11
प्रेट्ज़ेल: एक मॉडल नागरिक के रूप में जॉन स्कीट का आपका उपयोग गलत है, क्योंकि वह किसी ऐसे व्यक्ति को भूलेगा (और उधेड़ देगा) जो उससे सहमत नहीं है, जैसा कि मैंने अनुभव किया है। जिम बेल्टर: आपकी कुछ टिप्पणियां अत्यधिक आक्रामक थीं और आप कम गुस्से और अधिक शिक्षा के साथ अपनी बातों को प्रस्तुत कर सकते थे।
सनकैट २२

7
@Pretzel जिम का कहना है कि जब तक आप तत्वों को पूर्णांकों के अनुक्रम में मैप कर सकते हैं, तब तक आप इसे अनुक्रमित कर सकते हैं। यह कि वर्ग स्वयं एक इंडेक्स को स्टोर नहीं करता है वह बिंदु के बगल में है। इसके अतिरिक्त, लिंक की गई सूची में एक आदेश है जो केवल जिम की स्थिति को मजबूत करता है। आपको केवल प्रत्येक तत्व को क्रम में करना है। विशेष रूप से, आप इसे पुनरावृत्ति करते हुए एक संख्या बढ़ाकर प्राप्त कर सकते हैं , या आप एक ही लंबाई के साथ पूर्णांकों की सूची तैयार कर सकते हैं और फिर उन्हें ज़िप कर सकते हैं (जैसा कि पायथन के zipकार्य में है)।
jpmc26

666

इयान मर्सर ने फिल हैक के ब्लॉग पर इसका एक समान समाधान पोस्ट किया :

foreach (var item in Model.Select((value, i) => new { i, value }))
{
    var value = item.value;
    var index = item.i;
}

यह आपको मद (हो जाता है item.value) और उसके सूचकांक ( item.i) का उपयोग करके LINQ के इस अधिभारSelect :

फ़ंक्शन का दूसरा पैरामीटर [इनसाइड सेलेक्ट] सोर्स एलिमेंट के इंडेक्स को दर्शाता है।

new { i, value }एक नया पैदा कर रही है गुमनाम वस्तु

ValueTupleयदि आप C # 7.0 या उसके बाद का उपयोग कर रहे हैं, तो ढेर आवंटन से बचा जा सकता है :

foreach (var item in Model.Select((value, i) => ( value, i )))
{
    var value = item.value;
    var index = item.i;
}

आप item.स्वचालित विनाशकारी का उपयोग करके भी समाप्त कर सकते हैं :

<ol>
foreach ((MyType value, Int32 i) in Model.Select((value, i) => ( value, i )))
{
    <li id="item_@i">@value</li>
}
</ol>

9
रेजर टेम्पलेट के मामले में यह समाधान अच्छा है, जहां टेम्पलेट की नीरसता एक गैर-तुच्छ डिजाइन चिंता है और आप प्रत्येक आइटम के सूचकांक का उपयोग करना चाहते हैं जो कि एनुमरेटेड है। हालाँकि, इस बात को ध्यान में रखें कि 'रैपिंग' से ऑब्जेक्ट एलोकेशन किसी पूर्णांक के (अपरिहार्य) इंक्रीमेंट के ऊपर व्यय (अंतरिक्ष और समय में) जोड़ता है।
डेविड बुलक


19
क्षमा करें - यह चतुर है, लेकिन क्या यह वास्तव में फॉर्च्यूनर के बाहर एक सूचकांक बनाने और प्रत्येक लूप को बढ़ाने से अधिक पठनीय है?
jbyrd

13
बाद के C # संस्करणों के साथ इसलिए आप ट्यूपल्स का भी उपयोग करते हैं, इसलिए आपके पास कुछ इस तरह का होगा: मॉडल में चयन करें। foreach (var (item, i) टुपल डिकंस्ट्रक्शन के साथ फॉर-लूप के अंदर सीधे आइटम और इंडेक्स (i) को एक्सेस करें।
हकमन Ha ’

5
क्या कोई मुझे समझा सकता है कि यह एक अच्छा जवाब क्यों है (लेखन के समय 450 से अधिक upvotes)? जहां तक ​​मैं देख सकता हूं कि एक काउंटर को बढ़ाने के बजाय इसे समझना अधिक कठिन है, इसलिए कम रखरखाव योग्य है, यह अधिक मेमोरी का उपयोग करता है, और यह शायद धीमा है। क्या मैं कुछ भूल रहा हूँ?
रिच एन

182

अंत में C # 7 में एक foreachलूप के अंदर एक इंडेक्स प्राप्त करने के लिए एक सभ्य वाक्यविन्यास है (यानी टुपल्स):

foreach (var (item, index) in collection.WithIndex())
{
    Debug.WriteLine($"{index}: {item}");
}

थोड़ा विस्तार विधि की आवश्यकता होगी:

public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self)       
   => self.Select((item, index) => (item, index)); 

8
इस उत्तर को रेखांकित किया गया है, टुपल्स ज्यादा साफ होने पर
टॉड

7
अशक्त संग्रह को संभालने के लिए संशोधित:public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> self) => self?.Select((item, index) => (item, index)) ?? new List<(T, int)>();
2Toad

अच्छा है। मैं वास्तव में इस समाधान को सबसे अधिक पसंद करता हूं।
फ्रांजहुबर

यह सबसे अच्छा जवाब है
w0ns88

2
अन्य भाषाओं मेंEnumerated उपयोग किए जाने वाले लोगों के लिए अधिक पहचानने योग्य होने के लिए कॉल करने के लिए उपयोगी हो सकता है (और शायद टपल मापदंडों के क्रम को स्वैप भी)। ऐसा नहीं है कि स्पष्ट वैसे भी नहीं है। WithIndex
फर्नांड्र

114

कुछ इस तरह कर सकता है:

public static class ForEachExtensions
{
    public static void ForEachWithIndex<T>(this IEnumerable<T> enumerable, Action<T, int> handler)
    {
        int idx = 0;
        foreach (T item in enumerable)
            handler(item, idx++);
    }
}

public class Example
{
    public static void Main()
    {
        string[] values = new[] { "foo", "bar", "baz" };

        values.ForEachWithIndex((item, idx) => Console.WriteLine("{0}: {1}", idx, item));
    }
}

12
यह "वास्तव में" समस्या को हल नहीं करता है। यह विचार अच्छा है, लेकिन यह अतिरिक्त गणना चर से नहीं बचता है
Atmocreations

अगर हमारे पास लूप के लिए रिटर्न स्टेटमेंट है तो यह काम नहीं करता है, यदि आप इसके लिए "ForEachWithIndex" बदलते हैं, तो यह सामान्य नहीं है, लूप के लिए नियमित रूप से लिखना बेहतर है
शंकर राजू

आपका ForEachWithIndex कॉल लाइनक सिलेक्ट का उपयोग करके इसके बराबर है जो एक स्ट्रिंग और एक इंडेक्स लेता है:values.Select((item, idx) => { Console.WriteLine("{0}: {1}", idx, item); return item; }).ToList();
user2023861

95

मैं टिप्पणियों से असहमत हूं कि forज्यादातर मामलों में एक बेहतर विकल्प है।

foreachएक उपयोगी निर्माण है, और forसभी परिस्थितियों में एक लूप द्वारा प्रतिस्थापित करने योग्य नहीं है।

उदाहरण के लिए, यदि आपके पास DataReader और लूप है , तो सभी रिकॉर्ड के माध्यम से foreachयह स्वचालित रूप से डिस्पोज़ विधि को कॉल करता है और रीडर को बंद कर देता है (जो तब कनेक्शन को स्वचालित रूप से बंद कर सकता है)। यह इसलिए सुरक्षित है क्योंकि यह पाठक को बंद करने के लिए भूल जाने पर भी कनेक्शन लीक को रोकता है।

(निश्चित रूप से यह हमेशा पाठकों को बंद करने के लिए अच्छा अभ्यास है, लेकिन संकलक इसे पकड़ने नहीं जा रहा है यदि आप नहीं करते हैं - आप गारंटी नहीं दे सकते कि आपने सभी पाठकों को बंद कर दिया है, लेकिन आप इसे अधिक संभावना बना सकते हैं कि आप कनेक्शन को लीक नहीं करेंगे फोर्क का उपयोग करने की आदत में।)

Disposeउपयोगी होने की विधि के निहित कॉल के अन्य उदाहरण हो सकते हैं ।


2
इस पर ध्यान दिलाने के लिए धन्यवाद। बल्कि सूक्ष्म है। आप अधिक जानकारी pvle.be/2010/05/foreach-statement-calls-dispose-on-ienumerator और msdn.microsoft.com/en-us/library/aa664754(VS51).aspx पर प्राप्त कर सकते हैं ।
मार्क मेउर

+1। मैं और अधिक विस्तार से लिख रहा था कि कैसे प्रोग्रामर्स पर foreachअलग for(और करीब से while) है ।
आर्सेनी मौरज़ेंको

64

शाब्दिक उत्तर - चेतावनी, प्रदर्शन केवल intसूचकांक को ट्रैक करने के लिए उपयोग करने के रूप में अच्छा नहीं हो सकता है । कम से कम यह उपयोग करने से बेहतर है IndexOf

आपको बस संग्रह में प्रत्येक आइटम को अनुक्रमणिका को जानने वाली अनाम वस्तु के साथ लपेटने के लिए अनुक्रमणिका अधिभार का उपयोग करने की आवश्यकता है। यह कुछ भी है कि IEnumerable लागू करता है के खिलाफ किया जा सकता है।

System.Collections.IEnumerable collection = Enumerable.Range(100, 10);

foreach (var o in collection.OfType<object>().Select((x, i) => new {x, i}))
{
    Console.WriteLine("{0} {1}", o.i, o.x);
}

3
कास्ट <टी> () के बजाय टाइप्ट <टी> () का उपयोग करने का एकमात्र कारण यह है कि गणना में कुछ आइटम एक स्पष्ट डाली विफल हो सकते हैं। वस्तु के लिए, यह मामला कभी नहीं होगा।
डेहल्बेक

13
निश्चित रूप से, कास्ट के बजाय टाइप का उपयोग करने के अन्य कारण को छोड़कर - जो कि मैं कास्ट का उपयोग कभी नहीं करता।
एमी बी

36

LINQ, C # 7, और System.ValueTupleNuGet पैकेज का उपयोग करके , आप यह कर सकते हैं:

foreach (var (value, index) in collection.Select((v, i)=>(v, i))) {
    Console.WriteLine(value + " is at index " + index);
}

आप नियमित foreachनिर्माण का उपयोग कर सकते हैं और किसी वस्तु के सदस्य के रूप में मूल्य और सूचकांक को सीधे एक्सेस करने में सक्षम हो सकते हैं, और दोनों क्षेत्रों को केवल लूप के दायरे में रखते हैं। इन कारणों से, मेरा मानना ​​है कि यह सबसे अच्छा उपाय है यदि आप C # 7 और का उपयोग करने में सक्षम हैं System.ValueTuple



@EdwardBrey मुझे लगता है कि यह नहीं है। इस सवाल के बहुत सारे उत्तर हैं, मैं शायद इसे याद कर रहा हूं या ध्यान नहीं देता कि यह क्या कर रहा था क्योंकि उसने कुछ तर्क को एक विस्तार विधि में अलग कर दिया था।
पावेल

1
यह अलग है क्योंकि। चयन LINQ से अंतर्निहित है। आपको अपना कार्य लिखना नहीं आता है? हालांकि आपको वीएस "सिस्टम.वेल्यू टपल" स्थापित करने की आवश्यकता होगी।
एंटोन

33

@ फ्लाईस्वाट के उत्तर का उपयोग करते हुए, मैं इस समाधान के साथ आया:

//var list = new List<int> { 1, 2, 3, 4, 5, 6 }; // Your sample collection

var listEnumerator = list.GetEnumerator(); // Get enumerator

for (var i = 0; listEnumerator.MoveNext() == true; i++)
{
  int currentItem = listEnumerator.Current; // Get current item.
  //Console.WriteLine("At index {0}, item is {1}", i, currentItem); // Do as you wish with i and  currentItem
}

आपको एन्यूमरेटर का उपयोग करके मिलता है GetEnumeratorऔर फिर आप लूप का उपयोग करके लूप करते हैं for। हालांकि, चाल पाश की स्थिति बनाने के लिए है listEnumerator.MoveNext() == true

चूंकि MoveNextएक एन्यूमरेटर की विधि सही है अगर कोई अगला तत्व है और इसे एक्सेस किया जा सकता है, जिससे यह बनता है कि लूप की स्थिति लूप बंद कर देती है जब हम तत्वों को खत्म करने के लिए भागते हैं।


10
सूची की तुलना करने की कोई आवश्यकता नहीं है। यह कंप्यूटर से पूछने जैसा है अगर सच == सच है? :) बस कहते हैं अगर listEnumerator.MoveNext () {}
कृपया यहाँ क्लिक करें

9
@ प्रसन्नता, आप बिल्कुल सही हैं। मैंने महसूस किया कि इस मामले में इसे जोड़ना अधिक पठनीय है, खासकर उन लोगों के लिए, जिन्हें एक शर्त के रूप में i <blahSize के अलावा और कुछ भी दर्ज करने की आदत नहीं है।
Gezim

@ गीज़िम मुझे यहां कोई बुरा नहीं लगता, लेकिन मुझे आपकी बात समझ में आती है, लेकिन यह एक विधेय जैसा नहीं लगता।
जॉन ड्वोरक

1
आपको एन्यूमरेटर को निपटाना चाहिए।
एंटोनिन लेजसेक

@ AntonínLejsek एन्यूमरेटर लागू नहीं होता है IDisposable
एडवर्ड ब्रे

27

काउंटर चर का उपयोग करने में कुछ भी गलत नहीं है। वास्तव में, चाहे आप उपयोग करते हों for, foreach whileया do, एक काउंटर चर को कहीं न कहीं घोषित और संवर्धित किया जाना चाहिए।

यदि आप निश्चित रूप से अनुक्रमित संग्रह में हैं, तो इस मुहावरे का उपयोग करें:

var i = 0;
foreach (var e in collection) {
   // Do stuff with 'e' and 'i'
   i++;
}

यदि आप जानते हैं कि आपके अनुक्रमणिका संग्रह के लिए O (1) है (जो कि इसके लिए होगा Arrayऔर संभवतः के लिए List<T>(प्रलेखन नहीं कहता है), लेकिन जरूरी नहीं कि अन्य प्रकारों (जैसे LinkedList) के लिए भी इसका उपयोग करें।

// Hope the JIT compiler optimises read of the 'Count' property!
for (var i = 0; i < collection.Count; i++) {
   var e = collection[i];
   // Do stuff with 'e' and 'i'
}

यह कभी भी 'मैन्युअल रूप से' जरूरी नहीं होना चाहिए IEnumeratorकि आह्वान करके MoveNext()और पूछताछ करके Current- foreachआपको उस विशेष परेशान को बचा रहा है ... यदि आपको वस्तुओं को छोड़ने की आवश्यकता है, तो बस continueलूप के शरीर में उपयोग करें ।

और बस पूर्णता के लिए, आप क्या कर रहे थे के आधार पर कर रही है अपने सूचकांक के साथ (ऊपर निर्माणों लचीलापन के बहुत सारे प्रदान करते हैं), तो आपको समानांतर LINQ का उपयोग हो सकता है:

// First, filter 'e' based on 'i',
// then apply an action to remaining 'e'
collection
    .AsParallel()
    .Where((e,i) => /* filter with e,i */)
    .ForAll(e => { /* use e, but don't modify it */ });

// Using 'e' and 'i', produce a new collection,
// where each element incorporates 'i'
collection
    .AsParallel()
    .Select((e, i) => new MyWrapper(e, i));

हम AsParallel()ऊपर का उपयोग करते हैं, क्योंकि यह पहले से ही 2014 है, और हम चीजों को गति देने के लिए उन एकाधिक कोर का अच्छा उपयोग करना चाहते हैं। इसके अलावा, 'अनुक्रमिक' LINQ के लिए, आपको केवल एक ForEach()विस्तार विधि मिलती है List<T>औरArray ... और यह स्पष्ट नहीं है कि इसका उपयोग करना सरल करने से बेहतर है foreach, क्योंकि आप अभी भी बदसूरत सिंटैक्स के लिए एकल-थ्रेडेड चल रहे हैं।


26

आप मूल एन्यूमरेटर को दूसरे के साथ लपेट सकते हैं जिसमें सूचकांक जानकारी शामिल है।

foreach (var item in ForEachHelper.WithIndex(collection))
{
    Console.Write("Index=" + item.Index);
    Console.Write(";Value= " + item.Value);
    Console.Write(";IsLast=" + item.IsLast);
    Console.WriteLine();
}

यहाँ ForEachHelperकक्षा के लिए कोड है ।

public static class ForEachHelper
{
    public sealed class Item<T>
    {
        public int Index { get; set; }
        public T Value { get; set; }
        public bool IsLast { get; set; }
    }

    public static IEnumerable<Item<T>> WithIndex<T>(IEnumerable<T> enumerable)
    {
        Item<T> item = null;
        foreach (T value in enumerable)
        {
            Item<T> next = new Item<T>();
            next.Index = 0;
            next.Value = value;
            next.IsLast = false;
            if (item != null)
            {
                next.Index = item.Index + 1;
                yield return item;
            }
            item = next;
        }
        if (item != null)
        {
            item.IsLast = true;
            yield return item;
        }            
    }
}

यह वास्तव में आइटम के सूचकांक को वापस नहीं करेगा। इसके बजाय, यह प्रगणित सूची के अंदर सूचकांक लौटाएगा, जो केवल सूची का एक सबलिस्ट हो सकता है, जिससे आपको सटीक डेटा केवल तभी मिलेगा जब सबलिस्ट और सूची समान आकार के हों। मूल रूप से, किसी भी समय संग्रह में ऐसी वस्तुएं हैं जो अनुरोधित प्रकार में नहीं हैं, तो आपका सूचकांक गलत होगा।
लुकास बी

7
@ लुकास: नहीं, लेकिन यह वर्तमान फॉर्च्यूनर पुनरावृत्ति के सूचकांक को लौटा देगा। यही सवाल था।
ब्रायन गिदोन

20

यहाँ एक समाधान मैं सिर्फ इस समस्या के साथ आया हूँ

मूल कोड:

int index=0;
foreach (var item in enumerable)
{
    blah(item, index); // some code that depends on the index
    index++;
}

अद्यतन कोड

enumerable.ForEach((item, index) => blah(item, index));

विस्तार विधि:

    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T, int> action)
    {
        var unit = new Unit(); // unit is a new type from the reactive framework (http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx) to represent a void, since in C# you can't return a void
        enumerable.Select((item, i) => 
            {
                action(item, i);
                return unit;
            }).ToList();

        return pSource;
    }

16

बस अपना खुद का सूचकांक जोड़ें। इसे सरल रखें।

int i = 0;
foreach (var item in Collection)
{
    item.index = i;
    ++i;
}

15

यह केवल एक सूची और किसी भी IEnumerable के लिए काम करने जा रहा है, लेकिन LINQ में यह है:

IList<Object> collection = new List<Object> { 
    new Object(), 
    new Object(), 
    new Object(), 
    };

foreach (Object o in collection)
{
    Console.WriteLine(collection.IndexOf(o));
}

Console.ReadLine();

@ जोनाथन मैंने यह नहीं कहा कि यह एक महान जवाब था, मैंने सिर्फ इतना कहा कि यह दिखा रहा था कि उसने जो पूछा वह करना संभव था :)

@Graphain मुझे उम्मीद है कि यह तेज़ नहीं होगा - मुझे पूरी तरह से यकीन नहीं है कि यह कैसे काम करता है, यह पूरी सूची के माध्यम से हर बार एक मिलान वस्तु खोजने के लिए दोहरा सकता है, जो तुलनाओं का एक नरकंकाल होगा।

उस ने कहा, सूची गिनती के साथ प्रत्येक वस्तु का एक सूचकांक रख सकती है।

लगता है कि जोनाथन एक बेहतर विचार है, अगर वह विस्तृत होता?

बेहतर होगा कि आप इस बात का ध्यान रखें कि जहाँ आप फ़ॉर्च्यूनर में हैं, वहाँ सरल और अधिक अनुकूलनीय है।


5
भारी डाउनवोटिंग पर यकीन नहीं है। निश्चित प्रदर्शन इसे निषेधात्मक बनाता है लेकिन आपने प्रश्न का उत्तर दिया है!
मैट मिशेल

4
इसके साथ एक और मुद्दा यह है कि यह केवल तभी काम करता है जब सूची में आइटम अद्वितीय हों।
कोडइन्चोस

14

C # 7 आखिरकार हमें ऐसा करने का एक सुंदर तरीका देता है:

static class Extensions
{
    public static IEnumerable<(int, T)> Enumerate<T>(
        this IEnumerable<T> input,
        int start = 0
    )
    {
        int i = start;
        foreach (var t in input)
        {
            yield return (i++, t);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var s = new string[]
        {
            "Alpha",
            "Bravo",
            "Charlie",
            "Delta"
        };

        foreach (var (i, t) in s.Enumerate())
        {
            Console.WriteLine($"{i}: {t}");
        }
    }
}

हां, और MS को इस तरह की चीज़ को मूल बनाने के लिए CLR / BCL का विस्तार करना चाहिए।
टॉड

14

क्यों फ़र्क़ पड़ता है?!

सबसे आसान तरीका उपयोग कर रहा है के लिए foreach के बजाय यदि आप सूची का उपयोग कर रहे :

for (int i = 0 ; i < myList.Count ; i++)
{
    // Do something...
}

या यदि आप उपयोग करना चाहते हैं foreach:

foreach (string m in myList)
{
     // Do something...
}

आप प्रत्येक लूप के सूचकांक को जानने के लिए इसका उपयोग कर सकते हैं:

myList.indexOf(m)

8
indexOf समाधान डुप्लिकेट के साथ सूची के लिए अमान्य है और बहुत धीमा भी है।
tymtam

2
समस्या से बचने के लिए जहाँ आप IEnumerable को कई बार पार करते हैं, उदाहरण के लिए वस्तुओं की गिनती और फिर प्रत्येक आइटम। यह निहितार्थ है जब IEnumerable उदाहरण के लिए एक डेटाबेस क्वेरी का परिणाम है।
डेविड क्लार्क

2
myList.IndexOf () O (n) है, इसलिए आपका लूप O (n ^ 2) होगा।
पैट्रिक दाढ़ी

9
int index;
foreach (Object o in collection)
{
    index = collection.indexOf(o);
}

यह संग्रह समर्थन के लिए काम करेगा IList


68
दो समस्याएं: 1) यह O(n^2)सबसे अधिक कार्यान्वयन में IndexOfहै O(n)। 2) यह विफल रहता है अगर सूची में डुप्लिकेट आइटम हैं।
कोडइन्चोस 19

15
नोट: O (n ^ 2) का अर्थ है कि यह बड़े संग्रह के लिए विनाशकारी रूप से धीमा हो सकता है।
ओ'रोनी

IndexOf विधि का उपयोग करने के लिए महान आविष्कार! यह वह है जो मैं फ़ॉरच लूप में सूचकांक (संख्या) प्राप्त करने के लिए देख रहा था! बिग Thx
मिता बोन्का

19
भगवान, मुझे आशा है कि आपने इसका उपयोग नहीं किया था! :( यह उस चर का उपयोग करता है जिसे आप बनाना नहीं चाहते थे - वास्तव में, यह n + 1 किलों का निर्माण करेगा क्योंकि उस फ़ंक्शन को वापस लौटने के लिए भी एक बनाना होगा, - और यह कि इंडेक्सऑफ़ की खोज की तुलना में बहुत धीमी है हर चरण में एक पूर्ण वेतन वृद्धि ऑपरेशन। लोग इस जवाब को वोट क्यों नहीं देंगे?
कनहरी

13
इस उत्तर का उपयोग न करें, मुझे एक टिप्पणी में उल्लिखित कठिन सत्य मिला। "यह विफल रहता है अगर सूची में डुप्लिकेट आइटम हैं।" !!!
ब्रूस

9

यह है कि मैं इसे कैसे करता हूं, जो इसकी सादगी / संक्षिप्तता के लिए अच्छा है, लेकिन यदि आप लूप बॉडी में बहुत कुछ कर रहे हैं obj.Value, तो यह बहुत पुराना है।

foreach(var obj in collection.Select((item, index) => new { Index = index, Value = item }) {
    string foo = string.Format("Something[{0}] = {1}", obj.Index, obj.Value);
    ...
}

8

यह उत्तर: प्रत्यक्ष भाषा समर्थन के लिए C # भाषा टीम की पैरवी करता है।

प्रमुख उत्तर बताता है:

जाहिर है, एक सूचकांक की अवधारणा अभिज्ञान की अवधारणा के लिए विदेशी है, और ऐसा नहीं किया जा सकता है।

हालांकि यह वर्तमान सी # भाषा संस्करण (2020) का सच है, यह एक वैचारिक सीएलआर / भाषा सीमा नहीं है, यह किया जा सकता है।

Microsoft C # भाषा विकास टीम एक नई इंटरफ़ेस #ndexedEnumerable के लिए समर्थन जोड़कर एक नई C # भाषा सुविधा बना सकती है

foreach (var item in collection with var index)
{
    Console.WriteLine("Iteration {0} has value {1}", index, item);
}

//or, building on @user1414213562's answer
foreach (var (item, index) in collection)
{
    Console.WriteLine("Iteration {0} has value {1}", index, item);
}

यदि foreach ()उपयोग किया जाता है और with var indexमौजूद है, तो कंपाइलर आइटम संग्रह को IIndexedEnumerableइंटरफ़ेस घोषित करने की उम्मीद करता है। यदि इंटरफ़ेस अनुपस्थित है, तो कंपाइलर स्रोत को IndexedEnumerable ऑब्जेक्ट के साथ लपेट सकता है, जो इंडेक्स को ट्रैक करने के लिए कोड में जोड़ता है।

interface IIndexedEnumerable<T> : IEnumerable<T>
{
    //Not index, because sometimes source IEnumerables are transient
    public long IterationNumber { get; }
}

बाद में, CLR को आंतरिक इंडेक्स ट्रैकिंग के लिए अपडेट किया जा सकता है, इसका उपयोग केवल तभी किया जाता है जब withकीवर्ड निर्दिष्ट किया जाता है और स्रोत सीधे लागू नहीं होता हैIIndexedEnumerable

क्यों:

  • फॉर्च्यूसर अच्छा दिखता है, और व्यावसायिक अनुप्रयोगों में, फ़ॉरचेक लूप शायद ही कभी एक प्रदर्शन अड़चन है
  • स्मृति पर फ़ार्च अधिक कुशल हो सकता है। प्रत्येक चरण में नए संग्रह में बदलने के बजाय कार्यों की एक पाइपलाइन होना। कम सीपीयू कैश फाल्ट और कम कचरा संग्रह होने पर कुछ और सीपीयू साइकिल का उपयोग करने पर कौन परवाह करता है?
  • इंडेक्स-ट्रैकिंग कोड को जोड़ने के लिए कोडर की आवश्यकता, सुंदरता को खराब करती है
  • इसे लागू करना काफी आसान है (कृपया Microsoft) और पिछड़े संगत है

जबकि यहां ज्यादातर लोग Microsoft कर्मचारी नहीं हैं, यह एक सही उत्तर है, आप इस तरह की सुविधा को जोड़ने के लिए Microsoft की पैरवी कर सकते हैं। आप पहले से ही एक्सटेंशन फ़ंक्शन के साथ अपना इटैटर बना सकते हैं और ट्यूपल्स का उपयोग कर सकते हैं, लेकिन Microsoft एक्सटेंशन फ़ंक्शन से बचने के लिए सिंटैक्टिक शुगर छिड़क सकता है


रुको, तो क्या यह भाषा सुविधा पहले से मौजूद है, या क्या यह भविष्य के लिए प्रस्तावित है?
पावेल १

1
@Pavel मैंने उत्तर स्पष्ट होने के लिए अद्यतन किया। यह उत्तर अग्रणी उत्तर का मुकाबला करने के लिए प्रदान किया गया था जो बताता है कि "स्पष्ट रूप से, एक सूचकांक की अवधारणा अभिज्ञान की अवधारणा के लिए विदेशी है, और ऐसा नहीं किया जा सकता है।"
टॉड

6

यदि संग्रह एक सूची है, तो आप List.IndexOf का उपयोग कर सकते हैं, जैसे कि:

foreach (Object o in collection)
{
    // ...
    @collection.IndexOf(o)
}

13
और अब एल्गोरिथ्म हे (n ^ 2) (यदि बुरा नहीं है)। मैं इसे इस्तेमाल करने से पहले बहुत सावधानी से सोचूंगा। इसके
क्रूसिबल के

1
@BradleyDotNET बिल्कुल सही है, इस संस्करण का उपयोग न करें।
Oskar

1
इससे सावधान रहें! यदि आपको अपनी सूची में एक डुप्लिकेट आइटम मिला है, तो उसे पहले वाले का स्थान मिलेगा!
सोनहजा

5

continueइस तरह से कीवर्ड सुरक्षित निर्माण का उपयोग करने के लिए बेहतर है

int i=-1;
foreach (Object o in collection)
{
    ++i;
    //...
    continue; //<--- safe to call, index will be increased
    //...
}

5

आप अपना लूप इस तरह लिख सकते हैं:

var s = "ABCDEFG";
foreach (var item in s.GetEnumeratorWithIndex())
{
    System.Console.WriteLine("Character: {0}, Position: {1}", item.Value, item.Index);
}

निम्नलिखित संरचना और विस्तार विधि जोड़ने के बाद।

संरचना और विस्तार विधि Enumerable.Select कार्यक्षमता को एन्क्रिप्ट करती है।

public struct ValueWithIndex<T>
{
    public readonly T Value;
    public readonly int Index;

    public ValueWithIndex(T value, int index)
    {
        this.Value = value;
        this.Index = index;
    }

    public static ValueWithIndex<T> Create(T value, int index)
    {
        return new ValueWithIndex<T>(value, index);
    }
}

public static class ExtensionMethods
{
    public static IEnumerable<ValueWithIndex<T>> GetEnumeratorWithIndex<T>(this IEnumerable<T> enumerable)
    {
        return enumerable.Select(ValueWithIndex<T>.Create);
    }
}

3

इस समस्या के लिए मेरा समाधान एक विस्तार विधि है WithIndex(),

http://code.google.com/p/ub-dotnet-utilities/source/browse/trunk/Src/Utilities/Extensions/EnumerableExtensions.cs

इसका उपयोग करें

var list = new List<int> { 1, 2, 3, 4, 5, 6 };    

var odd = list.WithIndex().Where(i => (i.Item & 1) == 1);
CollectionAssert.AreEqual(new[] { 0, 2, 4 }, odd.Select(i => i.Index));
CollectionAssert.AreEqual(new[] { 1, 3, 5 }, odd.Select(i => i.Item));

मैं एक struct(इंडेक्स, आइटम) जोड़ी के लिए उपयोग करूंगा ।
कोडइन्चोस

3

रुचि के लिए, फिल हैक ने केवल एक रेजर टेम्पलेटेड डेलिगेट ( http://haacked.com/archive/2011/04/14/a-better-razor-foreach-loop.aspx ) के संदर्भ में इसका एक उदाहरण लिखा था।

प्रभावी रूप से वह एक विस्तार विधि लिखते हैं जो "IteratedItem" वर्ग (नीचे देखें) में पुनरावृत्ति को लपेटता है, जिससे सूचकांक के साथ-साथ पुनरावृत्ति के दौरान तत्व तक पहुंच की अनुमति मिलती है।

public class IndexedItem<TModel> {
  public IndexedItem(int index, TModel item) {
    Index = index;
    Item = item;
  }

  public int Index { get; private set; }
  public TModel Item { get; private set; }
}

हालाँकि, गैर-रेज़र वातावरण में यह ठीक रहेगा यदि आप एक ही ऑपरेशन कर रहे हैं (यानी एक जिसे लैम्ब्डा के रूप में प्रदान किया जा सकता है) यह नॉन-रेज़र कॉन्टेक्ट्स में फॉर / सिंटैक्स सिंटैक्स का ठोस प्रतिस्थापन नहीं होगा। ।


3

मुझे नहीं लगता कि यह काफी कुशल होना चाहिए, लेकिन यह काम करता है:

@foreach (var banner in Model.MainBanners) {
    @Model.MainBanners.IndexOf(banner)
}

3

मैंने इसे LINQPad में बनाया है :

var listOfNames = new List<string>(){"John","Steve","Anna","Chris"};

var listCount = listOfNames.Count;

var NamesWithCommas = string.Empty;

foreach (var element in listOfNames)
{
    NamesWithCommas += element;
    if(listOfNames.IndexOf(element) != listCount -1)
    {
        NamesWithCommas += ", ";
    }
}

NamesWithCommas.Dump();  //LINQPad method to write to console.

तुम भी बस का उपयोग कर सकते हैं string.join:

var joinResult = string.Join(",", listOfNames);

आप c # में भी स्ट्रिंग का उपयोग कर सकते हैं। उदाहरण के लिए: var joinResult = string.Join (",", listOfNames); '
वॉरेन लाफ्रेंस

1
क्या कोई मुझे समझा सकता है कि यह ओ (n * n) चीज क्या है?
एक्सल

@ यह मूल रूप से इसका मतलब है कि परिणाम की गणना करने के लिए आवश्यक संचालन में वृद्धि हुई है, अर्थात यदि nआइटम हैं तो संचालन हैं n * n, या n-squared हैं। en.wikipedia.org/wiki/…
रिचर्ड हैंसेल

2

मुझे विश्वास नहीं है कि फ़ॉरच लूप के वर्तमान पुनरावृत्ति का मान प्राप्त करने का एक तरीका है। खुद को गिनना, सबसे अच्छा तरीका लगता है।

क्या मैं पूछ सकता हूं, आप क्यों जानना चाहेंगे?

ऐसा लगता है कि आप सबसे अधिक तीन चीजों में से एक करना पसंद करेंगे:

1) संग्रह से वस्तु प्राप्त करना, लेकिन इस मामले में आपके पास पहले से ही है।

2) बाद की पोस्ट प्रोसेसिंग के लिए ऑब्जेक्ट्स की गणना करना ... संग्रह में एक गणना गुण है जिसे आप उपयोग कर सकते हैं।

3) ऑब्जेक्ट को लूप में उसके ऑर्डर के आधार पर सेट करना ... हालाँकि आप आसानी से सेट कर सकते हैं कि आपने ऑब्जेक्ट को कलेक्शन में कब जोड़ा है।


4) मैंने जो मामला कई बार मारा है वह कुछ अलग है जो पहले या आखिरी पास पर होना है - यह कहें कि आप जिन वस्तुओं को प्रिंट करने जा रहे हैं, उनकी एक सूची और आपको आइटम के बीच अल्पविराम की आवश्यकता है, लेकिन अंतिम आइटम के बाद नहीं।
लोरेन Pechtel

2

जब तक आपका संग्रह किसी विधि के माध्यम से ऑब्जेक्ट के इंडेक्स को वापस नहीं कर सकता है, एकमात्र तरीका यह है कि आपके उदाहरण में काउंटर का उपयोग किया जाए।

हालांकि, जब अनुक्रमित के साथ काम करते हैं, तो समस्या का एकमात्र उचित उत्तर पाश के लिए उपयोग करना है। कुछ भी कोड जटिलता का परिचय देता है, समय और स्थान की जटिलता का उल्लेख नहीं करना।


2

इस जैसे किसी और के बारे में क्या राय है? यदि myEnumerable रिक्त है, तो myDelimitedString शून्य हो सकता है।

IEnumerator enumerator = myEnumerable.GetEnumerator();
string myDelimitedString;
string current = null;

if( enumerator.MoveNext() )
    current = (string)enumerator.Current;

while( null != current)
{
    current = (string)enumerator.Current; }

    myDelimitedString += current;

    if( enumerator.MoveNext() )
        myDelimitedString += DELIMITER;
    else
        break;
}

यहां कई समस्याएं हैं। ए) अतिरिक्त ब्रेस। बी) स्ट्रिंग समतल + = प्रति लूप पुनरावृत्ति।
enorl76

2

मुझे बस यह समस्या थी, लेकिन मेरे मामले में समस्या के आसपास सोच ने सबसे अच्छा समाधान दिया, अपेक्षित समाधान से संबंधित नहीं।

यह काफी सामान्य मामला हो सकता है, मूल रूप से, मैं एक स्रोत सूची से पढ़ रहा हूं और एक गंतव्य सूची में उन पर आधारित वस्तुओं का निर्माण कर रहा हूं, हालांकि, मुझे यह जांचना होगा कि क्या स्रोत आइटम पहले मान्य हैं और किसी भी पंक्ति को वापस करना चाहते हैं त्रुटि। पहली नज़र में, मैं सूचकांक को वर्तमान संपत्ति पर वस्तु के एन्यूमरेटर में प्राप्त करना चाहता हूं, हालांकि, जैसा कि मैं इन तत्वों की नकल कर रहा हूं, मुझे वर्तमान सूचकांक को वर्तमान गंतव्य से वैसे भी पता है। जाहिर है यह आपके गंतव्य ऑब्जेक्ट पर निर्भर करता है, लेकिन मेरे लिए यह एक सूची थी, और सबसे अधिक संभावना है कि यह ICollection को लागू करेगा।

अर्थात

var destinationList = new List<someObject>();
foreach (var item in itemList)
{
  var stringArray = item.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);

  if (stringArray.Length != 2)
  {
    //use the destinationList Count property to give us the index into the stringArray list
    throw new Exception("Item at row " + (destinationList.Count + 1) + " has a problem.");
  }
  else
  {
    destinationList.Add(new someObject() { Prop1 = stringArray[0], Prop2 = stringArray[1]});
  }
}

हमेशा लागू नहीं होता है, लेकिन अक्सर यह उल्लेख के लायक है, मुझे लगता है।

वैसे भी, बिंदु यह है कि कभी-कभी आपके पास तर्क में एक गैर-स्पष्ट समाधान पहले से ही है ...


2

मुझे यकीन नहीं था कि आप प्रश्न के आधार पर सूचकांक जानकारी के साथ क्या करने की कोशिश कर रहे थे। हालाँकि, C # में, आप आमतौर पर IEnumerable.Select विधि को अनुकूलित कर सकते हैं ताकि आप जो चाहें सूचकांक को बाहर निकाल सकें। उदाहरण के लिए, मैं कुछ इस तरह का उपयोग कर सकता हूं कि क्या कोई मूल्य विषम है या यहां तक ​​कि।

string[] names = { "one", "two", "three" };
var oddOrEvenByName = names
    .Select((name, index) => new KeyValuePair<string, int>(name, index % 2))
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

यह आपको सूची के नाम से एक डिक्शनरी देगा कि क्या सूची में आइटम विषम (1) या सम (0) था।

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