एक सूची में आइटम प्राप्त करने के लिए LINQ का उपयोग करें <>, जो दूसरी सूची में नहीं हैं <>


525

मुझे लगता है कि ऐसा करने के लिए एक सरल LINQ क्वेरी है, मैं बिल्कुल निश्चित नहीं हूं कि कैसे।

कोड के इस टुकड़े को देखते हुए:

class Program
{
    static void Main(string[] args)
    {
        List<Person> peopleList1 = new List<Person>();
        peopleList1.Add(new Person() { ID = 1 });
        peopleList1.Add(new Person() { ID = 2 });
        peopleList1.Add(new Person() { ID = 3 });

        List<Person> peopleList2 = new List<Person>();
        peopleList2.Add(new Person() { ID = 1 });
        peopleList2.Add(new Person() { ID = 2 });
        peopleList2.Add(new Person() { ID = 3 });
        peopleList2.Add(new Person() { ID = 4 });
        peopleList2.Add(new Person() { ID = 5 });
    }
}

class Person
{
    public int ID { get; set; }
}

मैं एक LINQ क्वेरी करना चाहूँगा जो मुझे उस सभी लोगों को दे सके peopleList2जो इसमें नहीं हैंpeopleList1

यह उदाहरण मुझे दो लोगों को देना चाहिए (आईडी = ४ और आईडी = ५)


3
शायद यह एक अच्छा विचार है कि आईडी को आसानी से बनाया जाए क्योंकि किसी वस्तु की पहचान उसके लाइव समय के साथ नहीं बदलनी चाहिए। जब तक निश्चित रूप से आपके परीक्षण- या ORM- ढांचे की आवश्यकता होती है, तब तक इसे परिवर्तनशील होना चाहिए।
कोडइन्चोस

जवाबों:


911

यह निम्नलिखित LINQ अभिव्यक्ति का उपयोग कर संबोधित किया जा सकता है:

var result = peopleList2.Where(p => !peopleList1.Any(p2 => p2.ID == p.ID));

LINQ के माध्यम से इसे व्यक्त करने का एक वैकल्पिक तरीका, जिसे कुछ डेवलपर्स अधिक पठनीय पाते हैं:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

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


34
आप जानते हैं कि O (n + m) समय में आसानी से हल की जा सकने वाली समस्या का O (n * m) समाधान है?
निकी

32
@ मिक्की, ओपी ने एक समाधान के लिए कहा जो कि लिनक का उपयोग करता है। हो सकता है कि वह लिनक सीखने की कोशिश कर रहा हो। यदि प्रश्न सबसे कुशल तरीके से किया गया होता, तो मेरा प्रश्न जरूरी नहीं होता।
क्लॉस बिसकोव पेडरसन

46
@ मिक्की, अपने आसान समाधान साझा करने के लिए देखभाल?
रुबियो

18
यह समतुल्य है और मुझे अनुसरण करना आसान लगता है: var result = peopleList2.Where (p => peopleList1.All (P2 => P2.ID! = P.ID));
एंटोनोक

28
@ मेनोल - किसी सवाल का सही जवाब देने वाले व्यक्ति की आलोचना करना थोड़ा अनुचित हो सकता है। लोगों को उन सभी तरीकों और संदर्भों का अनुमान लगाने की आवश्यकता नहीं होनी चाहिए जो भविष्य के लोग जवाब पर ठोकर खा सकते हैं। वास्तव में, आपको यह निर्देश देना चाहिए कि निक्की - जो यह बताने के लिए समय लेती है कि वे इसे प्रदान किए बिना एक विकल्प के बारे में जानते थे।
क्रिस रोजर्स

396

यदि आप लोगों की समानता को ओवरराइड करते हैं तो आप भी उपयोग कर सकते हैं:

peopleList2.Except(peopleList1)

ExceptWhere(...Any)यह वेरिएंट की तुलना में काफी तेज होना चाहिए क्योंकि यह दूसरी सूची को हैशटेबल में डाल सकता है। Where(...Any)के क्रम है O(peopleList1.Count * peopleList2.Count)जबकि आधार पर वेरिएंट HashSet<T>(लगभग) का एक क्रम है O(peopleList1.Count + peopleList2.Count)

Exceptअंतर्निहित रूप से डुप्लिकेट निकालता है। यह आपके मामले को प्रभावित नहीं करना चाहिए, लेकिन समान मामलों के लिए एक मुद्दा हो सकता है।

या यदि आप तेज कोड चाहते हैं, लेकिन समानता को ओवरराइड नहीं करना चाहते हैं:

var excludedIDs = new HashSet<int>(peopleList1.Select(p => p.ID));
var result = peopleList2.Where(p => !excludedIDs.Contains(p.ID));

यह संस्करण डुप्लिकेट को नहीं निकालता है।


यह तभी काम करेगा जब Equalsआईडी की तुलना करने के लिए ओवरराइड किया गया हो।
क्लॉस बाइसकोव पेडरसेन

34
इसलिए मैंने लिखा है कि आपको समानता को ओवरराइड करने की आवश्यकता है। लेकिन मैंने एक उदाहरण जोड़ा है जो इसके बिना भी काम करता है।
कोडइन्चोस

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

2
यह बहुत अच्छा काम करता है यदि आप स्ट्रिंग्स (या अन्य बेस ऑब्जेक्ट्स) की सूची के बारे में बात कर रहे हैं, जो कि मैं इस धागे पर आने पर खोज रहा था।
दान कॉर्न

@DanKorn समान, मूल तुलना, इंट, ऑब्जेक्ट्स रेफ, स्ट्रिंग्स के लिए यह एक सरल समाधान है।
भूलभुलैया

73

या यदि आप इसे नकार के बिना चाहते हैं:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));

मूल रूप से यह कहता है कि सभी लोगों से प्राप्त करें List2 जहां लोगों में सभी idList1 peoplesList2 में आईडी से अलग हैं।

स्वीकृत उत्तर से बस थोड़ा सा अलग दृष्टिकोण :)


5
यह विधि (50,000 से अधिक वस्तुओं की सूची) किसी भी विधि की तुलना में काफी तेज थी!
डेवएन

5
यह सिर्फ तेज हो सकता है क्योंकि यह आलसी है। ध्यान दें कि यह अभी तक कोई वास्तविक काम नहीं कर रहा है। यह तब तक नहीं है जब तक आप उस सूची की गणना नहीं कर लेते हैं कि यह वास्तव में काम करता है (ToList को कॉल करके या फ़ॉरच लूप के हिस्से के रूप में इसका उपयोग करके)
Xtros

32

चूंकि धाराप्रवाह वाक्यविन्यास का उपयोग करने के लिए सभी समाधान, यहां रुचि रखने वालों के लिए क्वेरी अभिव्यक्ति सिंटैक्स में एक समाधान है:

var peopleDifference = 
  from person2 in peopleList2
  where !(
      from person1 in peopleList1 
      select person1.ID
    ).Contains(person2.ID)
  select person2;

मुझे लगता है कि यह कुछ के लिए ब्याज के दिए गए उत्तरों से काफी अलग है, यहां तक ​​कि यह सोचा कि यह सबसे अधिक संभावना सूचियों के लिए उप-रूपी होगा। अब अनुक्रमित आईडी वाले तालिकाओं के लिए, यह निश्चित रूप से जाने का रास्ता होगा।


धन्यवाद। पहले उत्तर दें कि क्वेरी एक्सप्रेशन सिंटैक्स के साथ परेशान करता है।
जेनेरिक नाम

15

पार्टी के लिए देर से लेकिन एक अच्छा समाधान है जो एसक्यूएल संगत के लिए भी Linq है:

List<string> list1 = new List<string>() { "1", "2", "3" };
List<string> list2 = new List<string>() { "2", "4" };

List<string> inList1ButNotList2 = (from o in list1
                                   join p in list2 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inList2ButNotList1 = (from o in list2
                                   join p in list1 on o equals p into t
                                   from od in t.DefaultIfEmpty()
                                   where od == null
                                   select o).ToList<string>();

List<string> inBoth = (from o in list1
                       join p in list2 on o equals p into t
                       from od in t.DefaultIfEmpty()
                       where od != null
                       select od).ToList<string>();

यंग्स टू http://www.dotnet-tricks.com/Tutorial/linq/UXPF181012-SQL-Joins-ith-C


12

क्लाऊस का जवाब बहुत अच्छा था, लेकिन रिस्पर आपको "सरलीकृत LINQ अभिव्यक्ति" के लिए कहेंगे:

var result = peopleList2.Where(p => peopleList1.All(p2 => p2.ID != p.ID));


यह ध्यान देने योग्य है कि यह चाल काम नहीं करेगी यदि दो वस्तुओं को बाँधने वाली एक से अधिक संपत्ति है (एसक्यूएल समग्र कुंजी सोचें)।
ट्रेक ऑक्ट

Alrekr - अगर आपके कहने का मतलब है "आपको अधिक गुणों की तुलना करने की आवश्यकता होगी यदि अधिक गुणों की तुलना करने की आवश्यकता है" तो मैं कहूंगा कि यह बहुत स्पष्ट है।
लुकास मॉर्गन

8

यह अनगिनत विस्तार आपको तुलना करने के लिए उपयोग करने के लिए कुंजी खोजने के लिए उपयोग करने के लिए आइटम की एक सूची और एक फ़ंक्शन को परिभाषित करने की अनुमति देता है।

public static class EnumerableExtensions
{
    public static IEnumerable<TSource> Exclude<TSource, TKey>(this IEnumerable<TSource> source,
    IEnumerable<TSource> exclude, Func<TSource, TKey> keySelector)
    {
       var excludedSet = new HashSet<TKey>(exclude.Select(keySelector));
       return source.Where(item => !excludedSet.Contains(keySelector(item)));
    }
}

आप इसे इस तरह से उपयोग कर सकते हैं

list1.Exclude(list2, i => i.ID);

@BrianT के पास कोड होने से, मैं इसे आपके कोड का उपयोग करने के लिए कैसे परिवर्तित कर सकता हूं?
निके मैनारिन

0

यहां एक कार्यशील उदाहरण है कि आईटी कौशल प्राप्त करें जो एक नौकरी उम्मीदवार के पास पहले से नहीं है।

//Get a list of skills from the Skill table
IEnumerable<Skill> skillenum = skillrepository.Skill;
//Get a list of skills the candidate has                   
IEnumerable<CandSkill> candskillenum = candskillrepository.CandSkill
       .Where(p => p.Candidate_ID == Candidate_ID);             
//Using the enum lists with LINQ filter out the skills not in the candidate skill list
IEnumerable<Skill> skillenumresult = skillenum.Where(p => !candskillenum.Any(p2 => p2.Skill_ID == p.Skill_ID));
//Assign the selectable list to a viewBag
ViewBag.SelSkills = new SelectList(skillenumresult, "Skill_ID", "Skill_Name", 1);

0

सबसे पहले, संग्रह से आईडी निकालें जहां स्थिति

List<int> indexes_Yes = this.Contenido.Where(x => x.key == 'TEST').Select(x => x.Id).ToList();

दूसरा, चयन करने के लिए अलग आईडी का चयन करने के लिए "तुलना" इस्टामेंट का उपयोग करें

List<int> indexes_No = this.Contenido.Where(x => !indexes_Yes.Contains(x.Id)).Select(x => x.Id).ToList();

जाहिर है आप x.key! = "TEST" का उपयोग कर सकते हैं, लेकिन केवल एक उदाहरण है


0

एक बार जब आप जेनेरिक FuncEqualityComparer लिखते हैं, तो आप इसे हर जगह उपयोग कर सकते हैं।

peopleList2.Except(peopleList1, new FuncEqualityComparer<Person>((p, q) => p.ID == q.ID));

public class FuncEqualityComparer<T> : IEqualityComparer<T>
{
    private readonly Func<T, T, bool> comparer;
    private readonly Func<T, int> hash;

    public FuncEqualityComparer(Func<T, T, bool> comparer)
    {
        this.comparer = comparer;
        if (typeof(T).GetMethod(nameof(object.GetHashCode)).DeclaringType == typeof(object))
            hash = (_) => 0;
        else
            hash = t => t.GetHashCode(); 
    }

    public bool Equals(T x, T y) => comparer(x, y);
    public int GetHashCode(T obj) => hash(obj);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.