क्या संग्रह की तुलना करने के लिए एक अंतर्निहित पद्धति है?


178

मैं अपने बराबर विधि में संग्रह के एक जोड़े की सामग्री की तुलना करना चाहूंगा। मेरे पास एक शब्दकोश और एक आईएलस्ट है। क्या ऐसा करने के लिए एक अंतर्निहित विधि है?

संपादित: मैं दो शब्दकोशों और दो ILists की तुलना करना चाहता हूं, इसलिए मुझे लगता है कि समानता का अर्थ स्पष्ट है - यदि दो शब्दकोशों में समान मानों में मैप की गई कुंजियाँ हैं, तो वे समान हैं।


आदेश के बावजूद या नहीं IList? प्रश्न अस्पष्ट है।
नवफाल

Enumerable.SequenceEqualऔर ISet.SetEqualsइस कार्यक्षमता के संस्करण प्रदान करते हैं। यदि आप ऑर्डर-अज्ञेयवादी बनना चाहते हैं और डुप्लिकेट वाले संग्रह के साथ काम करना चाहते हैं, तो आपको अपना स्वयं का रोल करना होगा। इस पोस्ट
चेसमैडलियन

जवाबों:


185

Enumerable.SequenceEqual

यह निर्धारित करता है कि एक निर्दिष्ट IEqualityComparer (T) का उपयोग करके उनके तत्वों की तुलना करके दो अनुक्रम समान हैं या नहीं।

आप सीधे सूची और शब्दकोश की तुलना नहीं कर सकते, लेकिन आप सूची के मूल्यों की सूची के साथ तुलना कर सकते हैं


52
समस्या यह है कि SequenceEqual तत्वों को उसी क्रम में होने की उम्मीद करता है। शब्दकोश वर्ग गणना करते समय चाबियाँ या मूल्यों के आदेश की गारंटी नहीं देता है, इसलिए यदि आप SequenceEqual का उपयोग करने जा रहे हैं, तो आपको पहले .Keys और .Values ​​को सॉर्ट करना होगा!
ओरियन एडवर्ड्स

3
@ कथन: ... जब तक आप आदेशों के अंतर का पता लगाना चाहते हैं, निश्चित रूप से :-)
schoetbi

30
@schoetbi: आप ऑर्डर की गारंटी नहीं देने वाले कंटेनर में अंतर का पता लगाना क्यों चाहेंगे ?
मत्ती वीरकुंकेन

4
@schoetbi: यह एक IEnumerable से एक निश्चित तत्व लेने के लिए है। हालांकि, एक शब्दकोश, नहीं गारंटी क्रम ऐसा करता है .Keysऔर .Valuesकिसी भी क्रम में वे की तरह महसूस में कुंजी और मूल्यों वापस आ सकते हैं, और जैसा कि शब्दकोश में अच्छी तरह से संशोधित किया गया है कि ताकि परिवर्तन की संभावना है। मेरा सुझाव है कि आप एक शब्दकोश क्या है और क्या नहीं है पर पढ़ते हैं।
मत्ती वीरकुंकेन

5
MS 'TestTools और NUnit CollectionAssert.AreEquivalent
tymtam

44

जैसा कि दूसरों ने सुझाव दिया है और नोट किया है, SequenceEqualआदेश-संवेदनशील है। इसे हल करने के लिए, आप शब्दकोश को कुंजी द्वारा सॉर्ट कर सकते हैं (जो अद्वितीय है, और इस प्रकार सॉर्ट हमेशा स्थिर है) और फिर उपयोग करें SequenceEqual। निम्न अभिव्यक्ति की जाँच करता है कि दो शब्दकोश उनके आंतरिक क्रम की परवाह किए बिना समान हैं:

dictionary1.OrderBy(kvp => kvp.Key).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key))

EDIT: जैसा कि जेपी स्टिग नीलसन ने कहा है, कुछ वस्तुएं ऐसी होती हैं जो IComparer<T>उनके IEqualityComparer<T>गलत परिणाम देने में असंगत होती हैं । ऐसे ऑब्जेक्ट के साथ कुंजियों का उपयोग करते समय, आपको IComparer<T>उन कुंजियों के लिए एक सही निर्दिष्ट करना होगा । उदाहरण के लिए, स्ट्रिंग कुंजियों के साथ (जो इस समस्या को प्रदर्शित करता है), आपको सही परिणाम प्राप्त करने के लिए निम्न कार्य करना चाहिए:

dictionary1.OrderBy(kvp => kvp.Key, StringComparer.Ordinal).SequenceEqual(dictionary2.OrderBy(kvp => kvp.Key, StringComparer.Ordinal))

यदि कुंजी प्रकार नहीं होगा तो क्या होगा CompareTo? आपका समाधान तब विस्फोट होगा। क्या होगा यदि कुंजी प्रकार में एक डिफ़ॉल्ट तुलनित्र है जो अपनी डिफ़ॉल्ट समानता तुलनित्र के साथ असंगत है? यह मामला है string, आप जानते हैं। एक उदाहरण के रूप में ये शब्दकोश (अंतर्निहित डिफ़ॉल्ट समानता तुलना के साथ) आपके परीक्षण को विफल कर देंगे (सभी संस्कृति infos के तहत मुझे पता है):var dictionary1 = new Dictionary<string, int> { { "Strasse", 10 }, { "Straße", 20 }, }; var dictionary2 = new Dictionary<string, int> { { "Straße", 20 }, { "Strasse", 10 }, };
जेपी स्टिग नीलसन

@JeppeStigNielsen: के बीच असंगति के लिए IComparerऔर IEqualityComparer- मैं इस मुद्दे के बारे में पता नहीं था, बहुत दिलचस्प! मैंने संभावित समाधान के साथ उत्तर को अपडेट किया। की कमी के बारे में CompareTo, मुझे लगता है कि डेवलपर को यह सुनिश्चित करना चाहिए कि OrderBy()विधि को प्रदान किया गया प्रतिनिधि कुछ तुलनीय है। मुझे लगता है कि यह किसी भी उपयोग के लिए सच है या OrderBy(), शब्दकोश तुलना के बाहर भी।
एलोन गुरिलनेक

15

उल्लिखित अनुक्रम के अलावा , जो

यह सच है कि अगर दो सूचियाँ समान लंबाई की हैं और उनके संबंधित तत्व एक तुलना के अनुसार समान हैं

(जो डिफ़ॉल्ट तुलनाकर्ता हो सकता है, यानी ओवरराइड Equals())

यह उल्लेखनीय है कि .Net4 में ISetऑब्जेक्ट्स पर SetEquals है, जो

तत्वों और किसी डुप्लिकेट तत्वों के आदेश को अनदेखा करता है।

इसलिए यदि आप वस्तुओं की एक सूची रखना चाहते हैं, लेकिन उन्हें एक विशिष्ट क्रम में होने की आवश्यकता नहीं है, तो विचार करें कि एक ISet(जैसे HashSet) सही विकल्प हो सकता है।


7

Enumerable.SequenceEqual विधि पर एक नज़र डालें

var dictionary = new Dictionary<int, string>() {{1, "a"}, {2, "b"}};
var intList = new List<int> {1, 2};
var stringList = new List<string> {"a", "b"};
var test1 = dictionary.Keys.SequenceEqual(intList);
var test2 = dictionary.Values.SequenceEqual(stringList);

13
यह विश्वसनीय नहीं है क्योंकि SequenceEqual मूल्यों को विश्वसनीय क्रम में शब्दकोश से बाहर आने की उम्मीद करता है - शब्दकोश आदेश के बारे में ऐसी कोई गारंटी नहीं देता है, और शब्दकोश ।Keys [1, 2] के बजाय [2, 1] के रूप में अच्छी तरह से बाहर आ सकते हैं। और आपका परीक्षण विफल हो जाएगा
ओरियन एडवर्ड्स

5

.NET संग्रह की तुलना करने के लिए किसी भी शक्तिशाली उपकरण को खो देता है। मैंने एक सरल समाधान विकसित किया है जिसे आप नीचे दिए गए लिंक पर पा सकते हैं:

http://robertbouillon.com/2010/04/29/comparing-collections-in-net/

यह आदेश की परवाह किए बिना एक समानता प्रदर्शन करेगा:

var list1 = new[] { "Bill", "Bob", "Sally" };
var list2 = new[] { "Bob", "Bill", "Sally" };
bool isequal = list1.Compare(list2).IsSame;

यह देखने के लिए जाँच करेगा कि क्या आइटम जोड़े गए / हटाए गए:

var list1 = new[] { "Billy", "Bob" };
var list2 = new[] { "Bob", "Sally" };
var diff = list1.Compare(list2);
var onlyinlist1 = diff.Removed; //Billy
var onlyinlist2 = diff.Added;   //Sally
var inbothlists = diff.Equal;   //Bob

यह देखेगा कि शब्दकोश में कौन से आइटम बदले गए:

var original = new Dictionary<int, string>() { { 1, "a" }, { 2, "b" } };
var changed = new Dictionary<int, string>() { { 1, "aaa" }, { 2, "b" } };
var diff = original.Compare(changed, (x, y) => x.Value == y.Value, (x, y) => x.Value == y.Value);
foreach (var item in diff.Different)
  Console.Write("{0} changed to {1}", item.Key.Value, item.Value.Value);
//Will output: a changed to aaa

10
बेशक .NET के पास संग्रह की तुलना करने के लिए शक्तिशाली उपकरण हैं (वे सेट-आधारित संचालन हैं)। .Removedके रूप में ही है list1.Except(list2), .Addedहै list2.Except(list1), .Equalहै list1.Intersect(list2)और .Differentहै original.Join(changed, left => left.Key, right => right.Key, (left, right) => left.Value == right.Value)। आप LINQ के साथ लगभग किसी भी तुलना कर सकते हैं।
एलोन गुरिलनेक

3
सुधार: .Differentहै original.Join(changed, left => left.Key, right => right.Key, (left, right) => new { Key = left.Key, NewValue = right.Value, Different = left.Value == right.Value).Where(d => d.Different)। और आप जोड़ सकते हैं OldValue = left.Valueयदि आपको पुराने मूल्य की भी आवश्यकता है।
एलोन गुरिलनेक

3
@AllonGuralnek आपके सुझाव अच्छे हैं, लेकिन वे उस मामले को नहीं संभालते हैं जहाँ सूची सही सेट नहीं है - जहाँ सूची में एक ही वस्तु कई बार होती है। {1, 2} और {1, 2, 2} की तुलना में कुछ भी नहीं जोड़ा / हटा दिया जाएगा।
नियाल कनॉटघटन

1
@ क्रिसपिन आर्काइव.ऑर्ग रॉबर्ट बाउलोन (और आपका) दोस्त है । ; ^)
रफिन

4

मैं Enumerable.SequenceEqual विधि के बारे में नहीं जानता था (आप हर दिन कुछ सीखते हैं ....), लेकिन मैं एक विस्तार विधि का उपयोग करने का सुझाव देने जा रहा था; कुछ इस तरह:

    public static bool IsEqual(this List<int> InternalList, List<int> ExternalList)
    {
        if (InternalList.Count != ExternalList.Count)
        {
            return false;
        }
        else
        {
            for (int i = 0; i < InternalList.Count; i++)
            {
                if (InternalList[i] != ExternalList[i])
                    return false;
            }
        }

        return true;

    }

दिलचस्प रूप से पर्याप्त, SequenceEqual के बारे में पढ़ने के लिए 2 सेकंड लेने के बाद, ऐसा लगता है कि Microsoft ने आपके लिए वर्णित फ़ंक्शन का निर्माण किया है।


4

यह सीधे आपके सवालों का जवाब नहीं दे रहा है, लेकिन दोनों MS 'TestTools और NUnit प्रदान करते हैं

 CollectionAssert.AreEquivalent

जो आप चाहते हैं बहुत सुंदर है।


मेरे NUnit परीक्षण के लिए यह देख रहा था
Blem

1

संग्रह की तुलना करने के लिए आप LINQ का भी उपयोग कर सकते हैं। Enumerable.Intersectसभी जोड़े जो समान हैं, लौटाता है। आप इस तरह से दो शब्दकोशों की तुलना कर सकते हैं:

(dict1.Count == dict2.Count) && dict1.Intersect(dict2).Count() == dict1.Count

पहली तुलना की आवश्यकता है क्योंकि dict2इसमें सभी कुंजियाँ हो सकती हैं dict1और बहुत कुछ।

तुम भी का उपयोग कर बदलाव के बारे में सोच सकते हैं Enumerable.Exceptऔर Enumerable.Unionइसी तरह के परिणाम के लिए नेतृत्व। लेकिन सेट के बीच सटीक अंतर निर्धारित करने के लिए इस्तेमाल किया जा सकता है।


1

इस उदाहरण के बारे में कैसे:

 static void Main()
{
    // Create a dictionary and add several elements to it.
    var dict = new Dictionary<string, int>();
    dict.Add("cat", 2);
    dict.Add("dog", 3);
    dict.Add("x", 4);

    // Create another dictionary.
    var dict2 = new Dictionary<string, int>();
    dict2.Add("cat", 2);
    dict2.Add("dog", 3);
    dict2.Add("x", 4);

    // Test for equality.
    bool equal = false;
    if (dict.Count == dict2.Count) // Require equal count.
    {
        equal = true;
        foreach (var pair in dict)
        {
            int value;
            if (dict2.TryGetValue(pair.Key, out value))
            {
                // Require value be equal.
                if (value != pair.Value)
                {
                    equal = false;
                    break;
                }
            }
            else
            {
                // Require key be present.
                equal = false;
                break;
            }
        }
    }
    Console.WriteLine(equal);
}

सौजन्य: https://www.dotnetperls.com/dEDIA-equals


value! = pair.Value संदर्भ तुलना कर रहा है, इसके बजाय बराबरी का उपयोग करें
kofifus

1

आदेशित संग्रह (सूची, एरे) के उपयोग के लिए SequenceEqual

HashSet उपयोग के लिए SetEquals

आप कर सकते हैं शब्दकोश के लिए:

namespace System.Collections.Generic {
  public static class ExtensionMethods {
    public static bool DictionaryEquals<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> d1, IReadOnlyDictionary<TKey, TValue> d2) {
      if (object.ReferenceEquals(d1, d2)) return true; 
      if (d2 is null || d1.Count != d2.Count) return false;
      foreach (var (d1key, d1value) in d1) {
        if (!d2.TryGetValue(d1key, out TValue d2value)) return false;
        if (!d1value.Equals(d2value)) return false;
      }
      return true;
    }
  }
}

(एक अधिक अनुकूलित समाधान छँटाई का उपयोग करेगा लेकिन इसके लिए आवश्यकता होगी IComparable<TValue>)


0

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


0

नहीं, क्योंकि ढांचा यह नहीं जानता कि आपकी सूचियों की सामग्री की तुलना कैसे करें।

कृपया एक नज़र इसे देखिये:

http://blogs.msdn.com/abhinaba/archive/2005/10/11/479537.aspx


3
तत्वों की तुलना कैसे करें, यह बताने के लिए पहले से ही कई विकल्प नहीं हैं? IComparer<T>अधिभावी object.Equals, IEquatable<T>, IComparable<T>...
स्टीफन स्टैनेगर

0
public bool CompareStringLists(List<string> list1, List<string> list2)
{
    if (list1.Count != list2.Count) return false;

    foreach(string item in list1)
    {
        if (!list2.Contains(item)) return false;
    }

    return true;
}

0

वहाँ नहीं था, है और नहीं भी हो सकता है, कम से कम मुझे तो विश्वास होगा। संग्रह समानता के पीछे का कारण संभवतः एक उपयोगकर्ता परिभाषित व्यवहार है।

संग्रह में तत्वों को एक विशेष क्रम में नहीं माना जाता है, हालांकि उनके पास स्वाभाविक रूप से एक आदेश है, यह नहीं है कि तुलनात्मक एल्गोरिदम पर क्या भरोसा करना चाहिए। कहो कि आपके पास दो संग्रह हैं:

{1, 2, 3, 4}
{4, 3, 2, 1}

वे समान हैं या नहीं? तुम्हें पता होना चाहिए, लेकिन मुझे नहीं पता कि तुम्हारी बात क्या है।

जब तक एल्गोरिदम सॉर्टिंग नियम प्रदान नहीं करते, तब तक संग्रह डिफ़ॉल्ट रूप से अनियंत्रित होते हैं। एसक्यूएल सर्वर आपके ध्यान में लाएगा, जब आप पेजिंग करने की कोशिश कर रहे हैं, तो इसके लिए आपको सॉर्टिंग नियम प्रदान करने होंगे:

https://docs.microsoft.com/en-US/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017

फिर भी एक और दो संग्रह:

{1, 2, 3, 4}
{1, 1, 1, 2, 2, 3, 4}

फिर से, वे समान हैं या नहीं? आप ही बताओ ..

किसी संग्रह की तत्व पुनरावृत्ति विभिन्न परिदृश्यों में अपनी भूमिका निभाती है और कुछ संग्रह जैसे Dictionary<TKey, TValue>दोहराए गए तत्वों की अनुमति भी नहीं देते हैं।

मेरा मानना ​​है कि इस प्रकार की समानता अनुप्रयोग परिभाषित है और रूपरेखा इसलिए संभव कार्यान्वयन के सभी प्रदान नहीं किया है।

वैसे, सामान्य मामलों Enumerable.SequenceEqualमें यह काफी अच्छा है, लेकिन यह निम्नलिखित मामले में गलत है:

var a = new Dictionary<String, int> { { "2", 2 }, { "1", 1 }, };
var b = new Dictionary<String, int> { { "1", 1 }, { "2", 2 }, };
Debug.Print("{0}", a.SequenceEqual(b)); // false

मैं इस तरह के सवालों के कुछ जवाब पढ़ता हूं (आप उनके लिए गूगल कर सकते हैं) और सामान्य तौर पर मैं क्या उपयोग करूंगा:

public static class CollectionExtensions {
    public static bool Represents<T>(this IEnumerable<T> first, IEnumerable<T> second) {
        if(object.ReferenceEquals(first, second)) {
            return true;
        }

        if(first is IOrderedEnumerable<T> && second is IOrderedEnumerable<T>) {
            return Enumerable.SequenceEqual(first, second);
        }

        if(first is ICollection<T> && second is ICollection<T>) {
            if(first.Count()!=second.Count()) {
                return false;
            }
        }

        first=first.OrderBy(x => x.GetHashCode());
        second=second.OrderBy(x => x.GetHashCode());
        return CollectionExtensions.Represents(first, second);
    }
}

इसका मतलब है कि एक संग्रह मूल तत्वों को ध्यान में रखे बिना दोहराए गए समय सहित उनके तत्वों में दूसरे का प्रतिनिधित्व करता है। कार्यान्वयन के कुछ नोट:

  • GetHashCode()सिर्फ आदेश के लिए है समानता के लिए नहीं; मुझे लगता है कि इस मामले में यह पर्याप्त है

  • Count() वास्तव में संग्रह की गणना नहीं करेगा और सीधे संपत्ति के कार्यान्वयन में गिर जाएगा ICollection<T>.Count

  • यदि संदर्भ समान हैं, तो यह सिर्फ बोरिस है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.