c# - सूची <T> से तत्वों को निकालने के लिए LINQ का उपयोग करना





.net list (13)


मैं घूम रहा था, अगर हैशसेट के अलावा निकालें और छोड़कर और पेशेवरों के बीच कोई अंतर है, तो मैंने त्वरित प्रदर्शन जांच की है :)

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

namespace ListRemoveTest
{
    class Program
    {
        private static Random random = new Random( (int)DateTime.Now.Ticks );

        static void Main( string[] args )
        {
            Console.WriteLine( "Be patient, generating data..." );

            List<string> list = new List<string>();
            List<string> toRemove = new List<string>();
            for( int x=0; x < 1000000; x++ )
            {
                string randString = RandomString( random.Next( 100 ) );
                list.Add( randString );
                if( random.Next( 1000 ) == 0 )
                    toRemove.Insert( 0, randString );
            }

            List<string> l1 = new List<string>( list );
            List<string> l2 = new List<string>( list );
            List<string> l3 = new List<string>( list );
            List<string> l4 = new List<string>( list );

            Console.WriteLine( "Be patient, testing..." );

            Stopwatch sw1 = Stopwatch.StartNew();
            l1.RemoveAll( toRemove.Contains );
            sw1.Stop();

            Stopwatch sw2 = Stopwatch.StartNew();
            l2.RemoveAll( new HashSet<string>( toRemove ).Contains );
            sw2.Stop();

            Stopwatch sw3 = Stopwatch.StartNew();
            l3 = l3.Except( toRemove ).ToList();
            sw3.Stop();

            Stopwatch sw4 = Stopwatch.StartNew();
            l4 = l4.Except( new HashSet<string>( toRemove ) ).ToList();
            sw3.Stop();


            Console.WriteLine( "L1.Len = {0}, Time taken: {1}ms", l1.Count, sw1.Elapsed.TotalMilliseconds );
            Console.WriteLine( "L2.Len = {0}, Time taken: {1}ms", l1.Count, sw2.Elapsed.TotalMilliseconds );
            Console.WriteLine( "L3.Len = {0}, Time taken: {1}ms", l1.Count, sw3.Elapsed.TotalMilliseconds );
            Console.WriteLine( "L4.Len = {0}, Time taken: {1}ms", l1.Count, sw3.Elapsed.TotalMilliseconds );

            Console.ReadKey();
        }


        private static string RandomString( int size )
        {
            StringBuilder builder = new StringBuilder();
            char ch;
            for( int i = 0; i < size; i++ )
            {
                ch = Convert.ToChar( Convert.ToInt32( Math.Floor( 26 * random.NextDouble() + 65 ) ) );
                builder.Append( ch );
            }

            return builder.ToString();
        }
    }
}

नीचे परिणाम:

Be patient, generating data...
Be patient, testing...
L1.Len = 985263, Time taken: 13411.8648ms
L2.Len = 985263, Time taken: 76.4042ms
L3.Len = 985263, Time taken: 340.6933ms
L4.Len = 985263, Time taken: 340.6933ms

जैसा कि हम देख सकते हैं, उस मामले में सबसे अच्छा विकल्प RemoveAll (हैशसेट) का उपयोग करना है

कहें कि मेरे पास LINQ क्वेरी है जैसे कि:

var authors = from x in authorsList
              where x.firstname == "Bob"
              select x;

यह देखते हुए कि authorsList की List<Author> , मैं Author authors के Author तत्वों को कैसे हटा सकता हूं जो क्वेरी में authors द्वारा लौटाए गए हैं?

या, एक और तरीका डालें, मैं authorsList से authorsList नाम के बराबर बॉब को कैसे हटा सकता हूं?

नोट: यह प्रश्न के प्रयोजनों के लिए एक सरलीकृत उदाहरण है।




आप दो तरीकों से हटा सकते हैं

var output = from x in authorsList
             where x.firstname != "Bob"
             select x;

या

var authors = from x in authorsList
              where x.firstname == "Bob"
              select x;

var output = from x in authorsList
             where !authors.Contains(x) 
             select x;

मुझे एक ही समस्या थी, अगर आप अपनी स्थिति के आधार पर सरल आउटपुट चाहते हैं, तो पहला समाधान बेहतर है।




LINQ की उत्पत्ति प्रोग्रामिंग प्रोग्रामिंग में है, जो ऑब्जेक्ट्स की अपरिवर्तनीयता पर जोर देती है, इसलिए यह मूल सूची को जगह में अपडेट करने का एक अंतर्निहित तरीका प्रदान नहीं करता है।

अपरिवर्तनीयता पर ध्यान दें (किसी अन्य SO उत्तर से लिया गया):

विकिपीडिया (लिंक) से अपरिवर्तनीयता की परिभाषा यहां दी गई है

"ऑब्जेक्ट उन्मुख और कार्यात्मक प्रोग्रामिंग में, एक अपरिवर्तनीय वस्तु एक वस्तु है जिसका निर्माण इसे बनाने के बाद संशोधित नहीं किया जा सकता है।"




यदि आपको वास्तव में वस्तुओं को हटाने की आवश्यकता है तो क्या छोड़कर ()?
आप एक नई सूची के आधार पर हटा सकते हैं, या लिंक को घोंसले से ऑन-द-फ्लाई हटा सकते हैं।

var authorsList = new List<Author>()
{
    new Author{ Firstname = "Bob", Lastname = "Smith" },
    new Author{ Firstname = "Fred", Lastname = "Jones" },
    new Author{ Firstname = "Brian", Lastname = "Brains" },
    new Author{ Firstname = "Billy", Lastname = "TheKid" }
};

var authors = authorsList.Where(a => a.Firstname == "Bob");
authorsList = authorsList.Except(authors).ToList();
authorsList = authorsList.Except(authorsList.Where(a=>a.Firstname=="Billy")).ToList();



सरल समाधान:

static void Main()
{
    List<string> myList = new List<string> { "Jason", "Bob", "Frank", "Bob" };
    myList.RemoveAll(x => x == "Bob");

    foreach (string s in myList)
    {
        //
    }
}



मुझे लगता है कि आप ऐसा कुछ कर सकते हैं

    authorsList = (from a in authorsList
                  where !authors.Contains(a)
                  select a).ToList();

हालांकि मुझे लगता है कि पहले से दिए गए समाधानों को समस्या को और अधिक पठनीय तरीके से हल किया गया है।




बहुत आसान है:

authorsList.RemoveAll((x) => x.firstname == "Bob");



खैर, उन्हें पहले स्थान पर बाहर करना आसान होगा:

authorsList = authorsList.Where(x => x.FirstName != "Bob").ToList();

हालांकि, यह पिछले संग्रह से लेखकों को हटाने की बजाय लेखकों के मूल्य को बदल देगा। वैकल्पिक रूप से, आप RemoveAll उपयोग कर सकते हैं:

authorsList.RemoveAll(x => x.FirstName == "Bob");

यदि आपको वास्तव में किसी अन्य संग्रह के आधार पर ऐसा करने की आवश्यकता है, तो मैं हैशसेट, RemoveAll का उपयोग करता हूं और इसमें शामिल हैं:

var setToRemove = new HashSet<Author>(authors);
authorsList.RemoveAll(x => setToRemove.Contains(x));



आप मानक LINQ ऑपरेटरों के साथ ऐसा नहीं कर सकते क्योंकि LINQ क्वेरी प्रदान करता है, समर्थन अपडेट नहीं करता है।

लेकिन आप एक नई सूची उत्पन्न कर सकते हैं और पुराने को प्रतिस्थापित कर सकते हैं।

var authorsList = GetAuthorList();

authorsList = authorsList.Where(a => a.FirstName != "Bob").ToList();

या आप दूसरे पास में authors में सभी वस्तुओं को हटा सकते हैं।

var authorsList = GetAuthorList();

var authors = authorsList.Where(a => a.FirstName == "Bob").ToList();

foreach (var author in authors)
{
    authorList.Remove(author);
}



यह एक बहुत पुराना सवाल है, लेकिन मुझे ऐसा करने का एक आसान तरीका मिला:

authorsList = authorsList.Except(authors).ToList();

ध्यान दें कि रिटर्न वैरिएबल authorsList की List<T> एक List<T> , इसलिए authorsList IEnumerable<T> को Except() वापस लौटाया गया है Except() को एक List<T> परिवर्तित किया जाना चाहिए।




मान लें कि authorsToRemove एक authorsToRemove IEnumerable<T> है जिसमें authorsList से authorsList जाने वाले तत्व शामिल हैं।

तो ओपी द्वारा निष्कासन हटाने के कार्य को पूरा करने के लिए यहां एक और आसान तरीका है:

authorsList.RemoveAll(authorsToRemove.Contains);



मुझे लगता है कि आपको उस प्रभाव को लेने के लिए केवल लेखक सूची से वस्तुओं को एक नई सूची में असाइन करना होगा।

//assume oldAuthor is the old list
Author newAuthorList = (select x from oldAuthor where x.firstname!="Bob" select x).ToList();
oldAuthor = newAuthorList;
newAuthorList = null;



अधिकांश उत्तर बताते हैं कि एक इंडेक्स कैसे ढूंढें, लेकिन यदि आइटम कई बार सूची में है तो उनकी विधियां एकाधिक अनुक्रमणिका नहीं लौटाती हैं। enumerate() प्रयोग करें:

for i, j in enumerate(['foo', 'bar', 'baz']):
    if j == 'bar':
        print(i)

index() फ़ंक्शन केवल पहली घटना देता है, जबकि enumerate() सभी घटनाओं को वापस कर देता है।

एक सूची समझ के रूप में:

[i for i, j in enumerate(['foo', 'bar', 'baz']) if j == 'bar']

यहां itertools.count() साथ एक और छोटा समाधान भी है (जो कि गणना के समान ही दृष्टिकोण है):

from itertools import izip as zip, count # izip for maximum efficiency
[i for i, j in zip(count(), ['foo', 'bar', 'baz']) if j == 'bar']

यह enumerate() का उपयोग करने की तुलना में बड़ी सूचियों के लिए अधिक कुशल है:

$ python -m timeit -s "from itertools import izip as zip, count" "[i for i, j in zip(count(), ['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 174 usec per loop
$ python -m timeit "[i for i, j in enumerate(['foo', 'bar', 'baz']*500) if j == 'bar']"
10000 loops, best of 3: 196 usec per loop




c# .net linq list