2008-10-26 22 views
15

का उपयोग कर इंडेक्स मानों का संग्रह प्राप्त करना क्या ऐसा करने का कोई बेहतर तरीका है?LINQ क्वेरी

string[] s = {"zero", "one", "two", "three", "four", "five"}; 

var x = 
s 
.Select((a,i) => new {Value = a, Index = i}) 
.Where(b => b.Value.StartsWith("t")) 
.Select(c => c.Index); 

अर्थात मैं एक अधिक कुशल या अधिक सुरुचिपूर्ण तरीका मापदंड से मेल खाने आइटम की स्थिति पाने के लिए देख रहा हूँ।

उत्तर

28

आप आसानी से अपने खुद के विस्तार विधि जोड़ सकते हैं

string[] s = {"zero", "one", "two", "three", "four", "five"}; 
var x = s.IndexesWhere(t => t.StartsWith("t")); 
5

मुझे ठीक लगता है। आप करने के लिए चयन को बदलने के द्वारा एक जोड़े पात्रों की बचत हो सकती:

.Select((Value, Index) => new {Value, Index}) 
+0

धन्यवाद - मुझे नहीं पता था कि आप ऐसा कर सकते हैं - मैंने सोचा था कि आपको फिर से असाइन करना होगा। – Guy

+2

इसे "प्रक्षेपण प्रारंभकर्ता" कहा जाता है - यह मूल रूप से अभिव्यक्ति के भीतर अंतिम उप-अभिव्यक्ति (जो एक फ़ील्ड या प्रॉपर्टी होना चाहिए) लेता है और नाम के लिए इसका उपयोग करता है। तो आप x.GetFoo() बार कर सकते हैं। यह बार = x.GetFoo() बार के बराबर होगा। –

6

तुम सिर्फ एक रास्ता LINQ जानने के लिए, इस पोस्ट की अनदेखी के रूप में उदाहरण के प्रयोग कर रहे हैं।


यह मुझे स्पष्ट नहीं है कि LINQ वास्तव में ऐसा करने का सबसे अच्छा तरीका है। नीचे दिया गया कोड ऐसा लगता है कि यह अधिक कुशल होगा क्योंकि कोई नया अनाम प्रकार बनाने की आवश्यकता नहीं है। अनुमोदित, आपका उदाहरण समझा जा सकता है और तकनीक एक अलग संदर्भ में अधिक उपयोगी हो सकती है, उदाहरण के लिए डेटा संरचना में जहां यह मूल्य पर सूचकांक का लाभ उठा सकता है, लेकिन नीचे दिया गया कोड उचित रूप से सीधे आगे, समझने योग्य (कोई विचार नहीं है आवश्यक) और तर्कसंगत रूप से अधिक कुशल। तब

public static IEnumerable<int> IndexesWhere<T>(this IEnumerable<T> source, Func<T, bool> predicate) 
{ 
    int index=0; 
    foreach (T element in source) 
    { 
     if (predicate(element)) 
     { 
      yield return index; 
     } 
     index++; 
    } 
} 

के साथ उपयोग करें:

string[] s = {"zero", "one", "two", "three", "four", "five"}; 
List<int> matchingIndices = new List<int>(); 

for (int i = 0; i < s.Length; ++i) 
{ 
    if (s[i].StartWith("t")) 
    { 
     matchingIndices.Add(i); 
    } 
} 
+0

उत्तर के लिए धन्यवाद। मैं मानता हूं कि यह अधिक कुशल होगा। जैसा कि आपने अनुमान लगाया है कि यह कुछ जटिल परिसर का सरलीकृत संस्करण है जो इस विशेष मामले में LINQ में "है" किया जाना है। – Guy

+2

और वह सटीक कोड केवल तभी काम करता है जब आप मनमाने ढंग से IENumerable की बजाय सूची का उपयोग कर रहे हों। अन्यथा आपको foreach और एक अलग स्थानीय चर का उपयोग करना होगा। –

2

वहाँ भी संग्रह सूची में FindIndex विधि जिसके लिए आप एक हटाने विधि बनाने है जो संग्रह से सूचकांक वापस कर सकते हैं। आप msdn http://msdn.microsoft.com/en-us/library/x1xzf2ca.aspx में निम्न लिंक का संदर्भ ले सकते हैं।

1

इस बारे में कैसे? यह मूल पोस्टर के समान है लेकिन मैं पहले इंडेक्स का चयन करता हूं और उसके बाद एक संग्रह बनाता हूं जो मानदंडों से मेल खाता है।

var x = s.Select((a, i) => i).Where(i => s[i].StartsWith("t")); 

यह कुछ अन्य उत्तरों की तुलना में कम कुशल है क्योंकि सूची पूरी तरह से दो बार खत्म हो जाती है।

0

मैंने एक सहयोगी के साथ इस रोचक समस्या पर चर्चा की और पहले मैंने सोचा कि जॉनस्केट का समाधान बहुत अच्छा था, लेकिन मेरे सहयोगी ने एक समस्या की ओर इशारा किया, अर्थात् यदि समारोह IEnumerable<T> का विस्तार है, तो इसका उपयोग किया जा सकता है जहां संग्रह उपकरण यह।

एक सरणी के साथ, यह foreach के साथ उत्पादन के क्रम का ध्यान रखा जाएगा (यानी foreach पिछले करने के लिए पहले से पुनरावृति जाएगा) कहने के लिए सुरक्षित है, लेकिन यह जरूरी नहीं कि अन्य संग्रह (सूची, शब्दकोश, आदि) के साथ मामला नहीं होगा, जहां foreachआवश्यक रूप से "प्रविष्टि का आदेश" प्रतिबिंबित नहीं करेगा। फिर भी समारोह वहां है, और यह भ्रामक हो सकता है।

अंत में, मैं tvanfosson के जवाब देने के लिए कुछ इसी तरह के साथ समाप्त हो गया है, लेकिन एक विस्तार विधि, सरणियों के लिए के रूप में:

public static int[] GetIndexes<T>(this T[]source, Func<T, bool> predicate) 
{ 
    List<int> matchingIndexes = new List<int>(); 

    for (int i = 0; i < source.Length; ++i) 
    { 
     if (predicate(source[i])) 
     { 
      matchingIndexes.Add(i); 
     } 
    } 
    return matchingIndexes.ToArray(); 
} 

यहाँ List.ToArray उम्मीद कर रहा है पिछले ऑपरेशन के लिए आदेश का सम्मान करेंगे ...

+0

"फिर भी समारोह वहां है, और यह भ्रामक हो सकता है।" - लेकिन यह किसी भी अन्य LINQ विधियों के लिए समान होगा जो इंडेक्स का उपयोग करते हैं, उदा। 'चयन करें ((आइटम, अनुक्रमणिका) =>' मुझे नहीं लगता कि जब तक आप समझ रहे हैं कि आप क्या प्राप्त कर रहे हैं, तब तक जॉन के कार्य के साथ यह कुछ भी गलत है। – Rup

+1

@Rup मैंने LINQ के साथ और अधिक अनुभव एकत्र किया है और उस उत्तर के बाद से अनन्य इंटरफ़ेस। मैं मानता हूं कि यदि आप जानते हैं कि आप क्या कर रहे हैं, और यदि फ़ंक्शन परिभाषित किया गया है, तो आश्चर्य की बात नहीं होगी। एक संभावित खतरे इंडेक्स को 'AsParallel' के साथ उत्पादित आईन्यूमेरेबल से प्राप्त करना होगा। वे इंडेक्स इस स्थिति को प्रतिबिंबित करेंगे कि संग्रह कैसे बनाया गया था, लेकिन इसे फिर से उत्पादित करने के लिए लगभग एक अलग परिणाम देने की गारंटी दी गई है। इंडेक्स कुछ ऐसी चीज का उल्लेख करेंगे जो मौजूद नहीं है। टीम के काम में (या बाद में फ़ंक्शन का पुन: उपयोग करना), यदि वह व्यवहार स्पष्ट नहीं है, आप परेशानी में होंगे। – MPelletier

+0

मुझे अभी भी लगता है कि जब तक आप संग्रह को संशोधित नहीं करते हैं, तब तक ऑर्डर बदलने की संभावना नहीं है, और जब तक कि यह संरचना के लिए समझ में नहीं आता है तब तक आपको इंडेक्स का उपयोग नहीं करना चाहिए लेकिन मैं समझ नहीं पाया था डी AsParallel - साफ, धन्यवाद। – Rup