2011-01-06 14 views
9

का उपयोग करके सूची अलग हो गए। असल में, एक सूची के माध्यम से पुनरावृत्त करें और तत्वों को एक सूची में जोड़ें जब तक कि कुछ शर्त न हो जाए, और बाकी को किसी अन्य सूची में जोड़ें।मैं निम्नलिखित कोड है LINQ

क्या कोई बेहतर तरीका है उदा। linq का उपयोग कर? चेककंडिशन() महंगा है, और सूचियां बहुत बड़ी हो सकती हैं इसलिए मैं कुछ भी नहीं करना चाहूंगा जो सूचियों को दो बार दोहराता है।

+1

मैं अपने कोड समझते हैं, लेकिन 'अगर (e.Current)' मतलब नहीं है। मुझे लगता है कि आप अंतिम 'MoveNext' कॉल (पहले लूप से) के मान को स्टोर करना चाहते हैं और यह देखने के लिए जांचें कि यह सफल था या नहीं। – Ani

+0

अधिक समझने के लिए अपडेट किया गया है (यानी लूप – Anonym

+0

के बाहर उस आइटम को संभालने के बजाय चेककंडिशन सत्य होने के बाद ई-कंटेंट को 'बी' में जोड़ें। हाँ, यह अभी स्पष्ट है। – Ani

उत्तर

7

यहाँ एक समाधान है कि दो बार सूची की गणना करने में हो रहा है, लेकिन यह शर्त दूसरी बार की जांच नहीं करेगा, तो यह तेजी से किया जाना चाहिए:

var a = someList.TakeWhile(x => !CheckCondition(x)).ToList(); 
var b = someList.Skip(a.Count).ToList(); 

यदि someList IList<T> लागू करता है, तो प्रत्येक आइटम को वास्तव में केवल एक बार गणना की जाएगी, इसलिए कोई दंड नहीं होगा।
मैंने सोचा था कि SkipIList<T> के मामले के लिए अनुकूलित किया गया था, लेकिन जाहिरा तौर पर यह नहीं है ... लेकिन आप आसानी से अपने खुद Skip विधि इस अनुकूलन का उपयोग करता है को लागू कर सकता है (Jon Skeet's article इस बारे में देखें)

यह वास्तव में किया जाएगा और अधिक सुरुचिपूर्ण अगर वहाँ था एक TakeUntil विधि ... हम आसानी से यह बना सकते हैं:

public static IEnumerable<TSource> TakeUntil<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
{ 
    foreach(var item in source) 
    { 
     if (predicate(item)) 
      break; 
     yield return item; 
    } 
} 
इस पद्धति से

, कोड हो जाता है:

var a = someList.TakeUntil(CheckCondition).ToList(); 
var b = someList.Skip(a.Count).ToList(); 
+5

+1: अच्छा, लेकिन मुझे लगता है * 'छोड़ें 'में ऑप्टिमाइज़ेशन नहीं है जिसे आप' IList ' के लिए .NET 4 के रूप में बोलते हैं। हालांकि सुनिश्चित नहीं है। – Ani

+0

एनी सही है। 'Skip 'के पास' IList ' के लिए कोई विशिष्ट अनुकूलन नहीं है, इसलिए सूची का पहला भाग हमेशा दो बार घुमाया जाएगा। – LukeH

+0

@ एनी, मैंने अभी जांच की है, ऐसा लगता है कि यह IList के लिए अनुकूलित नहीं है ... मैं अपना जवाब अपडेट करूंगा। –

2

व्यक्तिगत रूप से, मुझे नहीं लगता कि LINQ के लिए यहां कोई आवश्यकता है।

मैं की तरह कुछ करना होगा:

bool conditionHit = false; 

foreach (var item in someList) 
{ 
    if (!conditionHit) 
     conditionHit = CheckCondition(item); 

    var listToBeAdded = conditionHit ? b : a; 
    listToBeAdded.Add(item); 
} 
+2

मुझे ओपी के कोड से बेहतर पसंद है क्योंकि इसमें उस संदिग्ध 'if (e.Current)' नहीं है। आदर्श रूप से आप इसे प्रत्येक पुनरावृत्ति की गणना करने के बजाय दो बार 'सूची में जोड़ा गया' सेट करेंगे (प्रत्येक सूची के लिए एक बार)। – Gabe

+0

@Gabe: धन्यवाद। जो मैं वास्तव में टालना चाहता था वह स्रोत को दोहरा रहा था और 'चेककंडिशन' की अनावश्यक गणना; इन दोनों आवश्यकताओं को समस्या में बताया गया है। ऐसा नहीं लगता था कि ओपी को ध्वज के आधार पर अनावश्यक शाखाओं के साथ कोई समस्या थी। :) – Ani

1

यह एक बार से अधिक पहली सूची में आइटम पर जाने खत्म हो जाएगा, लेकिन केवल पहली बार CheckCondition के माध्यम से कॉल:

var a = someList.TakeWhile(e => !CheckCondition(e)); 
var b = someList.Skip(a.Count()); 
+2

a.Count() पहली क्वेरी को गिनने जा रहा है, जिसके परिणाम प्राप्त करने के लिए * फिर से * की गणना की आवश्यकता होगी ... आपको अंत में ToList को कॉल करने की आवश्यकता है (और आप बिल्कुल मेरे समाधान के साथ समाप्त हो जाएंगे) –

2

तो someList एक ठोस List<T> तो है यह एक एकल केवल आवश्यकता होगी प्रत्येक तत्व के माध्यम से पारित:

var a = someList.TakeWhile(x => !CheckCondition(x)).ToList(); 
var b = someList.GetRange(a.Count, someList.Count - a.Count); 
4

मैं Ani's answer बदलने के लिए नहीं करना चाहता था, लेकिन यहाँ एक मामूली सरलीकरण है।

var listToBeAdded = a; 
foreach (var item in someList) 
{ 
    if (listToBeAdded == a && CheckCondition(item)) 
     listToBeAdded = b; 

    listToBeAdded.Add(item); 
} 
+1

+1 - ग्रेट उत्तर। – ChaosPandion

0

इस (पुन: उपयोग नहीं Linq के अंतर्निहित तरीकों (iterators) रीवाइंड के लिए जाना जाता है), बस ओपी के तर्क (जो मेरा मानना ​​है कि performant है पुन: उपयोग का प्रयास करें, यह अगले पर फिर से मूल्यांकन नहीं करता हालत सूची के आधे) और एक साफ विस्तार विधि में यह पैकिंग और टपल:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 



namespace Craft 
{ 
    class Act 
    { 
     static void Main(string[] args) 
     { 

      var a = new List<string> 
       { "I", "Love", "You", "More", "Today", "Than", "Yesterday" }; 

      var tx = a.SplitByCondition(s => s == "More"); 

      foreach (var s in tx.Item1) 
       Console.WriteLine("First Half : {0}", s); 

      foreach (var s in tx.Item2) 
       Console.WriteLine("Second Half : {0}", s); 

      Console.ReadLine();      
     } 

    }//Act 

    public static class Helper 
    { 

     public static Tuple<List<T>, List<T>> SplitByCondition<T> 
      (this IEnumerable<T> t, Func<T, bool> terminator) 
     { 


      var tx = new Tuple<List<T>, List<T>> 
          (new List<T>(), new List<T>()); 

      var iter = t.GetEnumerator(); 

      while (iter.MoveNext()) 
      { 
       if (terminator(iter.Current)) 
       { 
        tx.Item2.Add(iter.Current); 
        break; 
       } 

       tx.Item1.Add(iter.Current); 
      } 

      while (iter.MoveNext()) 
       tx.Item2.Add(iter.Current); 

      return tx; 
     }  

    }//Helper 

}//Craft 

आउटपुट:

First Half : I 
First Half : Love 
First Half : You 
Second Half : More 
Second Half : Today 
Second Half : Than 
Second Half : Yesterday 
संबंधित मुद्दे