2012-03-05 21 views
15

संग्रह से सेट को निकालने का सबसे अच्छा तरीका क्या है, लेकिन फिर भी एक अलग संग्रह में हटाए गए आइटम को रखें?संग्रह से आइटम ढूंढें और निकालें

मैंने एक विस्तार विधि लिखा है जो ऐसा करता है, लेकिन मुझे लगता है कि एक बेहतर तरीका होना चाहिए। यहाँ मेरी समारोह है:

public static List<T> FindAndRemove<T>(this List<T> lst, Predicate<T> match) 
{ 
    List<T> ret = lst.FindAll(match); 
    lst.RemoveAll(match); 
    return ret; 
} 

और तुम इस तरह यह प्रयोग करेंगे:

List<String> myList = new List<String>(); 
myList.Add("ABC"); 
myList.Add("DEF"); 
myList.Add("ABC"); 
List<String> removed = myList.FindAndRemove(x => x == "ABC"); 
// myList now contains 1 item (DEF) 
// removed now contains 2 items (ABC, ABC) 

मैं 100% यकीन है कि क्या FindAll और RemoveAll तरीकों में पर्दे के पीछे पर चला जाता है नहीं कर रहा हूँ, लेकिन मुझे लगता है किसी भी तरह से एक सूची से दूसरी सूची में "स्थानांतरण" वस्तुओं का एक बेहतर तरीका होगा।

+1

आपका समाधान सबसे कुशल है। –

+2

आप कार्यान्वयन मेरे लिए ठीक लग रहा है। प्रतिलिपि .NET में एक सस्ता ऑपरेशन है इसलिए "स्थानांतरण" के लिए कोई कारण नहीं है (जब तक कि आपको कुछ थ्रेड/अपवाद सुरक्षा की आवश्यकता न हो, एक वस्तु एक समय में एक से अधिक संग्रह में कभी नहीं हो) – adrianm

+0

मैं सहमत हूं। LINQ में निर्मित का उपयोग जीवन को आसान बनाने का उद्देश्य है .. दृश्यों पर क्या चल रहा है एमएस द्वारा चुने गए सबसे अच्छे समाधान होंगे। चूंकि आप सी # में हैं जो आंखों के लिए ठीक है, लेकिन वीबी आप डेज़ी चेन को अपनी क्वेरी 'रिटर्न = lst.FindAll (मैच) करेंगे। कोड स्टाइल पढ़ने के साथ इन-लाइन रखने के लिए सभी को हटाएं। – ppumkin

उत्तर

1

मैं इस बात से सहमत नहीं हूं कि यह सबसे कुशल है - आप सूची के प्रत्येक तत्व पर दो बार match को बुला रहे हैं।

मैं इस तरह यह करना चाहते हैं:

var ret = new List<T>(); 
    var remaining = new List<T>(); 
    foreach (T t in lst) { 
     if (match(t)) 
     { 
      ret.Add(t); 
     } 
     else 
     { 
      remaining.Add(t); 
     } 
    } 
    lst.Clear(); 
    lst.AddRange(remaining); 
    return ret; 
+0

ओएमजी तो सी ++ दिन .. एक सूची के माध्यम से पुनरावृत्ति .. नहीं। नेट'श बिल्कुल नहीं। – ppumkin

+0

एक बार सूची के माध्यम से इटरेट करना, और आवश्यक परिणाम प्राप्त करने के लिए कम से कम काम करना। क्या पसंद नहीं करना? –

+1

@ppumkin: वैकल्पिक परिणाम प्राप्त करने के लिए एक सूची बनाना और बाद में इसके माध्यम से पुनरावृत्ति करना है, क्योंकि आप किसी फोरैच में एक सूची को म्यूट नहीं कर सकते हैं। – Guvante

0

अपने संग्रह के आकार पर निर्भर करता है, आप एक सूची एक HashSet बजाय के रूप में इसे लागू कर सकते हैं। पर्याप्त रूप से बड़े संग्रह में (मेरे अनुभव में, संग्रह में क्या है, "पर्याप्त" कितना बड़ा निर्भर है), हैशसेट्स सूची के मुकाबले वस्तुओं को खोजने में बहुत तेज हो सकता है।

9

ओप का उत्तर अब तक प्रस्तावित और सुझाए गए समाधानों में से सबसे अच्छा है। यहाँ मेरी मशीन पर समय कर रहे हैं:

public static class Class1 
{ 
    // 21ms on my machine 
    public static List<T> FindAndRemove<T>(this List<T> lst, Predicate<T> match) 
    { 
     List<T> ret = lst.FindAll(match); 
     lst.RemoveAll(match); 
     return ret; 
    } 

    // 538ms on my machine 
    public static List<T> MimoAnswer<T>(this List<T> lst, Predicate<T> match) 
    { 
     var ret = new List<T>(); 
     int i = 0; 
     while (i < lst.Count) 
     { 
      T t = lst[i]; 
      if (!match(t)) 
      { 
       i++; 
      } 
      else 
      { 
       lst.RemoveAt(i); 
       ret.Add(t); 
      } 
     } 
     return ret; 
    } 

    // 40ms on my machine 
    public static IEnumerable<T> GuvanteSuggestion<T>(this IList<T> list, Func<T, bool> predicate) 
    { 
     var removals = new List<Action>(); 

     foreach (T item in list.Where(predicate)) 
     { 
      T copy = item; 
      yield return copy; 
      removals.Add(() => list.Remove(copy)); 
     } 

     // this hides the cost of processing though the work is still expensive 
     Task.Factory.StartNew(() => Parallel.ForEach(removals, remove => remove())); 
    } 
} 

[TestFixture] 
public class Tester : PerformanceTester 
{ 
    [Test] 
    public void Test() 
    { 
     List<int> ints = Enumerable.Range(1, 100000).ToList(); 
     IEnumerable<int> enumerable = ints.GuvanteSuggestion(i => i % 2 == 0); 
     Assert.That(enumerable.Count(), Is.EqualTo(50000)); 
    } 
} 
+0

समय प्रदान करने के लिए धन्यवाद –

0

आपको क्या करने की कोशिश कर किया जाना चाहिए दो नए सूचियों में अपने मूल सूची विभाजन है। कार्यान्वयन किसी भी आईनेमरेबल पर काम नहीं करना चाहिए, केवल सूचियों के साथ, और यह मानना ​​चाहिए कि स्रोत अपरिवर्तनीय है। विभाजन पर इस पोस्ट को देखें: LINQ Partition List into Lists of 8 members। मुझे लगता है कि MoreLinq यह पहले से ही कवर किया गया है।

संबंधित मुद्दे