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


654

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

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

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

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

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

जवाबों:


1138

ठीक है, उन्हें पहली बार में बाहर करना आसान होगा:

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

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

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

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

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

14
किसी अन्य संग्रह के लिए हैशसेट का उपयोग करने का कारण क्या है?
123 456 789 0

54
@LeoLuis: यह Containsचेक को तेज़ बनाता है , और यह सुनिश्चित करता है कि आप केवल एक बार अनुक्रम का मूल्यांकन करें।
जॉन स्कीट

2
@LeoLuis: हाँ, एक सीक्वेंस से HashSet का निर्माण केवल एक बार इसका मूल्यांकन करता है। सुनिश्चित नहीं हैं कि "कमजोर संग्रह सेट" से आपका क्या मतलब है।
जॉन स्कीट

2
@ AndréChristofferAndersen: "आउटडेटेड" से आपका क्या तात्पर्य है? यह अभी भी काम करता है। यदि आपको मिल गया है List<T>, तो इसका उपयोग करना ठीक है।
जॉन स्कीट

4
@ AndréChristofferAndersen: यह उपयोग करने के लिए बेहतर होगाauthorsList = authorsList.Where(x => x.FirstName != "Bob")
जॉन स्कीट

133

इसे पूरा करने के लिए सूची <T> .RemoveAll का उपयोग करना बेहतर होगा।

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

8
@ लीड कोप्सी: आपके उदाहरण में लैम्ब्डा पैरामीटर कोष्ठक में संलग्न है, अर्थात (x)। क्या इसका कोई तकनीकी कारण है? क्या इसे अच्छा अभ्यास माना जाता है?
मैट डेविस 5

24
यह> 1 पैरामीटर के साथ आवश्यक है। एकल पैरामीटर के साथ, यह वैकल्पिक है, लेकिन यह स्थिरता बनाए रखने में मदद करता है।
रीड कोपसे

48

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

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();

Except()LINQ-statement के बीच जाने का एकमात्र तरीका है IEnumerableनहीं है Remove()और न ही RemoveAll()
जरी तुर्किया

29

आप 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);
}

12
RemoveAll()LINQ ऑपरेटर नहीं है।
डैनियल ब्रुकनर

मैं क्षमाप्रार्थी हूं। आप 100% सही हैं। दुर्भाग्य से, मैं अपने डाउनवोट को उलट नहीं सकता। उसके लिए माफ़ करना।
शाई कोहेन

Removeयह भी एक List< T> विधि है, एक System.Linq.Enumerable विधि नहीं है ।
डेविडआरआर

@Daniel, मुझे सही करें अगर मैं गलत हूं तो हम बच सकते हैं। दूसरा विकल्प के लिए कंडोम कहां से ()। यानी नीचे दिया गया कोड काम करेगा। var AuthorList = GetAuthorList (); var लेखक = AuthorList.Where (a => a .irstName == "बॉब"); foreach (लेखकों में var लेखक) {authorList.Remove (लेखक); }
साईं

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

20

सरल समाधान:

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

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

कैसे "बॉब" और "जेसन" को हटाने के लिए मेरा मतलब है स्ट्रिंग सूची में कई?
नियो

19

मैं सोच रहा था अगर वहाँ के बीच कोई अंतर है, RemoveAllऔर Exceptऔर का उपयोग करने के पेशेवरों HashSet, इसलिए मैं जल्दी प्रदर्शन की जांच किया है :)

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(HashSet)


यह कोड: "l2.RemoveAll (नया HashSet <string> (toRemove)) .Ctaintains;" संकलन नहीं करना चाहिए ... और यदि आपके परीक्षण सही हैं, तो वे दूसरे स्थान पर हैं जो जॉन स्कीट ने पहले ही सुझाए थे।
पास्कल

2
l2.RemoveAll( new HashSet<string>( toRemove ).Contains );ठीक ठीक FYI करें
AzNjoE

9

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

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

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


7

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

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;

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


मैं "बॉब" या "बिली" की जांच कैसे कर सकता हूं?
Si8

6

कहें कि authorsToRemoveएक IEnumerable<T>ऐसा तत्व है जिसे आप हटाना चाहते हैंauthorsList

तब यहाँ ओपी द्वारा पूछे गए निष्कासन कार्य को पूरा करने का एक और सरल तरीका है:

authorsList.RemoveAll(authorsToRemove.Contains);

5

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

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

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


4

नीचे सूची से तत्व को हटाने का उदाहरण दिया गया है।

 List<int> items = new List<int>() { 2, 2, 3, 4, 2, 7, 3,3,3};

 var result = items.Remove(2);//Remove the first ocurence of matched elements and returns boolean value
 var result1 = items.RemoveAll(lst => lst == 3);// Remove all the matched elements and returns count of removed element
 items.RemoveAt(3);//Removes the elements at the specified index

1

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

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

यहाँ विकिपीडिया से अपरिवर्तनीयता की परिभाषा दी गई है ।

ऑब्जेक्ट-ओरिएंटेड और फ़ंक्शनल प्रोग्रामिंग में, एक अपरिवर्तनीय ऑब्जेक्ट एक ऑब्जेक्ट है, जिसकी स्थिति निर्मित होने के बाद इसे संशोधित नहीं किया जा सकता है।


0

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

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

0

कोड धाराप्रवाह रखने के लिए (यदि कोड अनुकूलन महत्वपूर्ण नहीं है) और आपको सूची में कुछ और संचालन करने की आवश्यकता होगी:

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

या

authorsList = authorsList.Where(x => !setToRemove.Contains(x)).<do_some_further_Linq>;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.