2010-05-26 14 views
19

मेरे पास दो संग्रह a और b हैं। मैं a या b में आइटमों के सेट की गणना करना चाहता हूं, लेकिन दोनों में नहीं (एक लॉजिकल अनन्य या)।LINQ और सेट अंतर

IEnumerable<T> Delta<T>(IEnumerable<T> a, IEnumerable<T> b) 
{ 
    return a.Except (b).Union (b.Except (a)); 
} 

मुझे आश्चर्य है कि अगर वहाँ दो संग्रह के बीच अंतर का निर्माण करने में अन्य अधिक कुशल या अधिक कॉम्पैक्ट तरीके हैं: LINQ के साथ, मैं इस के साथ आ सकते हैं।

संपादित करें 1: जॉन स्कीट ने पहला समाधान पोस्ट किया जो HashSet पर निर्भर करके वस्तुओं के क्रम को संरक्षित नहीं करता है। मुझे आश्चर्य है कि क्या अन्य दृष्टिकोण हैं जो आउटपुट में a और b के आदेश को सुरक्षित रखेंगे।

HashSet<T> data = new HashSet<T>(a); 
data.SymmetricExceptWith(b); 

संपादित करें:: -

+0

क्या होगा यदि ए या बी में डुप्लिकेट हों? –

+0

मेरे मामले में, 'ए' और' बी 'में डुप्लीकेट नहीं होते हैं, इसलिए यह मेरे लिए चिंता नहीं है। आपके त्वरित उत्तर के लिए –

उत्तर

24

उपयोग HashSet<T> सीधे यह एक SymmetricExceptWith विधि है

HashSet<T> data = new HashSet<T>(a); 
data.IntersectWith(b); 
foreach (T t in a.Concat(b)) 
{ 
    if (!data.Contains(t)) 
    { 
     yield return t; 
    } 
} 

यह निम्न महत्वपूर्ण अंतर है:

आप को बनाए रखना चाहते हैं, तो यहाँ एक विकल्प है
  • a औरदोनों 0 दो बार फिर से दोहराया जाता है। कुछ मामलों में यह एक बहुत ही बुरी चीज हो सकती है - आप उनमें से प्रत्येक पर बफर बनाए रखने के लिए ToList पर कॉल कर सकते हैं।
  • यदि a या b में डुप्लिकेट हैं, तो उन्हें कई बार मिलेगा। यदि आप इससे बचना चाहते हैं तो आप पहले ही उपज वाले मूल्यों का एक सेट रख सकते हैं। इस बिंदु पर, यह के बराबर होगा:

    a.Concat(b).Except(a.Intersect(b)) 
    

अभी भी केवल दो सेट के बजाय संचालन अपने मूल कोड में तीन हालांकि है कि।

+0

धन्यवाद जॉन। हैशसेट तब तक ठीक काम करता है जब तक आप वस्तुओं के मूल क्रम में रूचि नहीं रखते। क्या होगा यदि मैं अंतर में 'ए' और 'बी' में वस्तुओं का क्रम रखना चाहता हूं? –

+0

@ पियर्रे: मैंने अपने उत्तर को कुछ और विकल्पों के साथ संपादित किया है। –

+0

आपके समय के लिए बहुत बहुत धन्यवाद। मेरे मामले में, 'ए' और' बी 'में डुप्लीकेट नहीं होते हैं, इसलिए यह कोई चिंता नहीं है। आपके द्वारा प्रस्तावित LINQ अभिव्यक्ति 'हैशसेट' से जुड़े कोड के टुकड़े की तुलना में अधिक पढ़ने योग्य (और इसलिए रखरखाव योग्य) है। मुझें यह पसंद है! –

3

को देखते हुए a.Except (ख) और b.Except (क) संबंध तोड़ना हैं, तो आप concat बजाय union का उपयोग कर सकते हैं, एक सेट ऑपरेटर बचत (और concat अधिक कुशल है)।

return a.Except (b).Concat (b.Except (a)); 

यह अभी भी प्रत्येक सूची के माध्यम से दो बार चलता है।

+0

धन्यवाद; आप सही हैं, 'कंसट' 'यूनियन 'से तेज़ होगा क्योंकि मेरे इनपुट असंगत हैं; मैंने उस बिंदु को अनदेखा कर दिया था। –

0

हम अपनी कंपनी में एक परियोजना के लिए एक समान की जरूरत थी, इसलिए हम इस विस्तार ने लिखा है:

public class EnumerablePair<T> : IReadOnlyCollection<T> 
{ 
    private IReadOnlyCollection<T> _Left; 
    private IReadOnlyCollection<T> _Right; 
    private IEnumerable<T> _Union; 
    private int _Count; 
    public EnumerablePair(IEnumerable<T> left, IEnumerable<T> right) 
    { 
     _Left = left?.ToList() ?? Enumerable.Empty<T>().ToList(); 
     _Right = right?.ToList() ?? Enumerable.Empty<T>().ToList(); 
     _Count = Left.Count + Right.Count; 
     _Union = Left.Union(Right); 
    } 

    public int Count => _Count; 
    public IReadOnlyCollection<T> Left { get => _Left; } 
    public IReadOnlyCollection<T> Right { get => _Right; } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return _Union.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return _Union.GetEnumerator(); 
    } 
} 

public static class EnumerableExtension 
{ 
    public static EnumerablePair<T> ExclusiveDisjunction<T>(this IEnumerable<T> leftOperand, IEnumerable<T> rightOperand, IEqualityComparer<T> comparer = null) 
    { 
     if (leftOperand == null) 
      throw new ArgumentNullException(nameof(leftOperand), $"{nameof(leftOperand)} is null."); 
     if (rightOperand == null) 
      throw new ArgumentNullException(nameof(rightOperand), $"{nameof(rightOperand)} is null."); 

     // TODO : Can be optimized if one of the IEnumerable parameters is empty. 

     bool leftIsBigger = leftOperand.Count() > rightOperand.Count(); 
     var biggestOperand = leftIsBigger ? leftOperand.ToList() : rightOperand.ToList(); 
     var smallestOperand = leftIsBigger ? rightOperand.ToList() : leftOperand.ToList(); 

     var except1 = biggestOperand.ToList(); 
     var except2 = Enumerable.Empty<T>().ToList(); 

     Func<T, T, bool> areEquals; 
     if (comparer != null) 
      areEquals = (one, theOther) => comparer.Equals(one, theOther); 
     else 
      areEquals = (one, theOther) => one?.Equals(theOther) ?? theOther == null; 

     foreach (T t in smallestOperand) 
      if (except1.RemoveAll(item => areEquals(item, t)) == 0) 
       except2.Add(t); 

     if (leftIsBigger) 
      return new EnumerablePair<T>(except1, except2); 
     return new EnumerablePair<T>(except2, except1); 
    } 
} 

यह (अपनी पसंद पर, एक IEqualityComparer या नहीं का प्रयोग करके) दो संग्रह के तत्वों तुलना करती है।

  • लौटे वस्तु, एक EnumerablePair<T>, दोनों (XOR) वस्तुओं है कि leftOperand या rightOperand में हैं, लेकिन नहीं होता है।
  • EnumerablePair<T>.Left में ऐसी वस्तुएं हैं जो leftOperand में हैं लेकिन rightOperand में नहीं हैं।
  • EnumerablePair<T>.Right में ऐसी वस्तुएं हैं जो rightOperand में हैं लेकिन leftOperand में नहीं हैं।

    var xorList = list1.ExclusiveDisjunction(list2); 
    var leftXor = xorList.Left; 
    var rightXor = xorList.Right; 
    

    xorList, leftXor और rightXorIEnumerable<T> हैं:

आप इस तरह एक्सटेंशन का उपयोग कर सकते हैं।

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