उनमें वस्तुओं के क्रम के बावजूद समानता के लिए दो संग्रहों की तुलना करना


162

मैं दो संग्रह (C # में) की तुलना करना चाहूंगा, लेकिन मैं इसे कुशलता से लागू करने का सबसे अच्छा तरीका सुनिश्चित नहीं हूं।

मैंने Enumerable.SequenceEqual के बारे में दूसरे सूत्र को पढ़ा है , लेकिन यह बिल्कुल वैसा नहीं है जैसा मैं देख रहा हूं।

मेरे मामले में, दो संग्रह समान होंगे यदि वे दोनों समान आइटम (कोई बात नहीं आदेश) होते हैं।

उदाहरण:

collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};

collection1 == collection2; // true

मैं आमतौर पर एक संग्रह के प्रत्येक आइटम के माध्यम से लूप करता हूं और देखता हूं कि क्या यह दूसरे संग्रह में मौजूद है, तो दूसरे संग्रह के प्रत्येक आइटम के माध्यम से लूप करें और देखें कि क्या यह पहले संग्रह में मौजूद है। (मैं लंबाई की तुलना करके शुरू करता हूं)।

if (collection1.Count != collection2.Count)
    return false; // the collections are not equal

foreach (Item item in collection1)
{
    if (!collection2.Contains(item))
        return false; // the collections are not equal
}

foreach (Item item in collection2)
{
    if (!collection1.Contains(item))
        return false; // the collections are not equal
}

return true; // the collections are equal

हालांकि, यह पूरी तरह से सही नहीं है, और यह समानता के लिए दो संग्रहों की तुलना करने के लिए शायद सबसे प्रभावी तरीका नहीं है।

एक उदाहरण जो मैं सोच सकता हूं वह गलत होगा:

collection1 = {1, 2, 3, 3, 4}
collection2 = {1, 2, 2, 3, 4}

जो मेरे कार्यान्वयन के साथ बराबर होगा। क्या मुझे प्रत्येक आइटम के मिलने की संख्या को गिनना चाहिए और यह सुनिश्चित करना चाहिए कि दोनों संग्रहों में गिनती बराबर है?


उदाहरण कुछ प्रकार के हैं C # (चलो इसे छद्म-सी # कहते हैं), लेकिन आप जिस भाषा में भी अपनी इच्छा रखते हैं, उसका जवाब दें, इससे कोई फर्क नहीं पड़ता।

नोट: मैंने सरलता के लिए उदाहरणों में पूर्णांक का उपयोग किया था, लेकिन मैं संदर्भ-प्रकार की वस्तुओं का भी उपयोग करने में सक्षम होना चाहता हूं (वे कुंजी के रूप में सही ढंग से व्यवहार नहीं करते हैं क्योंकि केवल वस्तु के संदर्भ की तुलना की जाती है, सामग्री नहीं)।


1
एल्गोरिथ्म के बारे में कैसे? किसी चीज की तुलना, जेनेरिक सूचियों की तुलना लिनेक आदि से संबंधित सभी उत्तर हैं। क्या हमने किसी से वादा किया है कि हम कभी भी पुराने फैशन प्रोग्रामर के रूप में एल्गोरिथम का उपयोग नहीं करेंगे?
नूरी YILMAZ

आप इक्विटी के लिए जाँच नहीं कर रहे हैं आप समानता के लिए जाँच कर रहे हैं। यह नाइटिक है लेकिन एक महत्वपूर्ण अंतर है। और बहुत समय पहले की बात है। यह एक अच्छा Q + A है।
सीएडी

आपको इस पोस्ट में रुचि हो सकती है , जो नीचे वर्णित शब्दकोश-आधारित पद्धति के एक ट्यून किए गए संस्करण पर चर्चा करता है। सबसे सरल शब्दकोश दृष्टिकोण के साथ एक मुद्दा यह है कि वे ठीक से नल को नहीं संभालते हैं क्योंकि .NET की डिक्शनरी क्लास शून्य कुंजियों की अनुमति नहीं देती है।
चेसमैडलियन

जवाबों:


112

यह पता चला है कि Microsoft ने पहले से ही अपने परीक्षण ढांचे में इसे कवर किया है: CollectionAssert.AreEquivalent

टिप्पणियों

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

प्रतिक्षेपक का उपयोग करते हुए, मैंने इसी समानता तुलनित्र बनाने के लिए AreEquivalent () के पीछे कोड को संशोधित किया। यह मौजूदा उत्तरों की तुलना में अधिक पूर्ण है, क्योंकि यह नल को ध्यान में रखता है, IEqualityComparer को लागू करता है और इसमें कुछ दक्षता और बढ़त मामले की जांच होती है। प्लस, यह Microsoft :)

public class MultiSetComparer<T> : IEqualityComparer<IEnumerable<T>>
{
    private readonly IEqualityComparer<T> m_comparer;
    public MultiSetComparer(IEqualityComparer<T> comparer = null)
    {
        m_comparer = comparer ?? EqualityComparer<T>.Default;
    }

    public bool Equals(IEnumerable<T> first, IEnumerable<T> second)
    {
        if (first == null)
            return second == null;

        if (second == null)
            return false;

        if (ReferenceEquals(first, second))
            return true;

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

            if (firstCollection.Count == 0)
                return true;
        }

        return !HaveMismatchedElement(first, second);
    }

    private bool HaveMismatchedElement(IEnumerable<T> first, IEnumerable<T> second)
    {
        int firstNullCount;
        int secondNullCount;

        var firstElementCounts = GetElementCounts(first, out firstNullCount);
        var secondElementCounts = GetElementCounts(second, out secondNullCount);

        if (firstNullCount != secondNullCount || firstElementCounts.Count != secondElementCounts.Count)
            return true;

        foreach (var kvp in firstElementCounts)
        {
            var firstElementCount = kvp.Value;
            int secondElementCount;
            secondElementCounts.TryGetValue(kvp.Key, out secondElementCount);

            if (firstElementCount != secondElementCount)
                return true;
        }

        return false;
    }

    private Dictionary<T, int> GetElementCounts(IEnumerable<T> enumerable, out int nullCount)
    {
        var dictionary = new Dictionary<T, int>(m_comparer);
        nullCount = 0;

        foreach (T element in enumerable)
        {
            if (element == null)
            {
                nullCount++;
            }
            else
            {
                int num;
                dictionary.TryGetValue(element, out num);
                num++;
                dictionary[element] = num;
            }
        }

        return dictionary;
    }

    public int GetHashCode(IEnumerable<T> enumerable)
    {
        if (enumerable == null) throw new ArgumentNullException(nameof(enumerable));

        int hash = 17;

        foreach (T val in enumerable.OrderBy(x => x))
            hash = hash * 23 + (val?.GetHashCode() ?? 42);

        return hash;
    }
}

नमूना उपयोग:

var set = new HashSet<IEnumerable<int>>(new[] {new[]{1,2,3}}, new MultiSetComparer<int>());
Console.WriteLine(set.Contains(new [] {3,2,1})); //true
Console.WriteLine(set.Contains(new [] {1, 2, 3, 3})); //false

या यदि आप सीधे दो संग्रहों की तुलना करना चाहते हैं:

var comp = new MultiSetComparer<string>();
Console.WriteLine(comp.Equals(new[] {"a","b","c"}, new[] {"a","c","b"})); //true
Console.WriteLine(comp.Equals(new[] {"a","b","c"}, new[] {"a","b"})); //false

अंत में, आप अपनी पसंद के समानता समानता का उपयोग कर सकते हैं:

var strcomp = new MultiSetComparer<string>(StringComparer.OrdinalIgnoreCase);
Console.WriteLine(strcomp.Equals(new[] {"a", "b"}, new []{"B", "A"})); //true

7
मुझे 100% यकीन नहीं है, लेकिन मुझे लगता है कि आपका जवाब रिवर्स इंजीनियरिंग के खिलाफ माइक्रोसॉफ्ट के उपयोग की शर्तों का उल्लंघन करता है।
इयान डलास

1
नमस्ते ओहद, कृपया विषय में निम्नलिखित लंबी बहस पढ़ें, stackoverflow.com/questions/371328/… यदि आप ऑब्जेक्ट हैशकोड बदलते हैं, तो हैशसेट में इसका उपयोग हैशसेट उचित कार्रवाई के साथ बाधित होगा और इसका अपवाद हो सकता है। नियम इस प्रकार है: यदि दो वस्तुएं समान हैं - तो उनके पास समान हैश कोड होना चाहिए। यदि दो वस्तुओं में समान हैशकोड होता है - तो उनके बराबर होना आवश्यक नहीं है। हैशकोड को पूरे ऑब्जेक्ट के जीवनकाल के लिए समान रहना चाहिए! यही कारण है कि आप ICompareable और IEqualrity को प्रभावित करते हैं।
जेम्स रोएटर

2
@JamesRoeiter शायद मेरी टिप्पणी भ्रामक थी। जब एक शब्दकोश मुठभेड़ों एक hashCode यह पहले से ही होता है, इसके लिए जाँच करता है वास्तविक समानता एक साथ EqualityComparer(या तो एक आप की आपूर्ति या EqualityComparer.Default, आप जाँच कर सकते हैं परावर्तक या संदर्भ स्रोत इस सत्यापित करने के लिए)। यह सच है, अगर यह विधि बदल रही है (और विशेष रूप से थियरी हैशकोड बदल जाती है) तो परिणाम अप्रत्याशित हैं, लेकिन इसका मतलब यह है कि यह विधि इस संदर्भ में सुरक्षित नहीं है।
ओहद श्नाइडर

1
@JamesRoeiter मान लीजिए कि x और y ऐसी दो वस्तुएं हैं जिनकी हम तुलना करना चाहते हैं। यदि उनके पास अलग-अलग हैशकोड हैं, तो हम जानते हैं कि वे अलग-अलग हैं (क्योंकि समान आइटम में समान हैशकोड हैं), और उपरोक्त कार्यान्वयन सही है। यदि उनके पास समान हैशकोड है, तो शब्दकोश कार्यान्वयन निर्दिष्ट (या यदि कोई भी निर्दिष्ट नहीं किया गया है) का उपयोग करके वास्तविक समानता की जांच करेगा और फिर से कार्यान्वयन सही है। EqualityComparerEqualityComparer.Default
ओहद श्नाइडर

1
@Cadbloke का नाम इंटरफ़ेस Equalsकी वजह से रखा गया IEqualityComparer<T>है। आपको जो देखना चाहिए वह तुलना करने वाले का नाम है । इस मामले में यह MultiSetComparerसमझ में आता है।
ओहद श्नाइडर

98

एक सरल और काफी कुशल समाधान दोनों संग्रहों को क्रमबद्ध करना और फिर समानता के लिए उनकी तुलना करना है:

bool equal = collection1.OrderBy(i => i).SequenceEqual(
                 collection2.OrderBy(i => i));

यह एल्गोरिथ्म O (N * logN) है, जबकि ऊपर दिया गया आपका समाधान O (N ^ 2) है।

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


1
आपको बस एक System.Linq का उपयोग करना होगा; सबसे पहले यह काम करने के लिए
जूनियर मेहे

यदि यह कोड लूप के भीतर है और संग्रह 1 अपडेट हो जाता है और संग्रह 2 अछूता रहता है, तब भी जब दोनों संग्रह में एक ही वस्तु होती है, तो डिबगर इस "बराबर" चर के लिए गलत दिखाएगा।
जूनियर मेहे

5
@Chaulky - मेरा मानना ​​है कि ऑर्डरबी की जरूरत है। देखें: dotnetfiddle.net/jA8iwE
ब्रेट

"उपरोक्त" के रूप में संदर्भित अन्य उत्तर कौन सा था? संभवतः stackoverflow.com/a/50465/3195477 ?
उउदद्ल्र्र्र्स

32

एक शब्दकोश "तानाशाह" बनाएं और फिर पहले संग्रह में प्रत्येक सदस्य के लिए, तानाशाही [सदस्य] ++ करें;

फिर, उसी तरह दूसरे संग्रह पर लूप करें, लेकिन प्रत्येक सदस्य के लिए तानाशाही [सदस्य] -।

अंत में, शब्दकोश में सभी सदस्यों पर पाश:

    private bool SetEqual (List<int> left, List<int> right) {

        if (left.Count != right.Count)
            return false;

        Dictionary<int, int> dict = new Dictionary<int, int>();

        foreach (int member in left) {
            if (dict.ContainsKey(member) == false)
                dict[member] = 1;
            else
                dict[member]++;
        }

        foreach (int member in right) {
            if (dict.ContainsKey(member) == false)
                return false;
            else
                dict[member]--;
        }

        foreach (KeyValuePair<int, int> kvp in dict) {
            if (kvp.Value != 0)
                return false;
        }

        return true;

    }

संपादित करें: जहां तक ​​मैं बता सकता हूं कि यह सबसे कुशल एल्गोरिदम के समान क्रम पर है। यह एल्गोरिथ्म O (N) है, यह मानते हुए कि शब्दकोश O (1) लुकअप का उपयोग करता है।


यह लगभग वही है जो मैं चाहता हूं। हालाँकि, मैं ऐसा करने में सक्षम होना चाहूंगा, भले ही मैं पूर्णांक का उपयोग नहीं कर रहा हूं। मैं संदर्भ वस्तुओं का उपयोग करना चाहूंगा, लेकिन वे शब्दकोशों में कुंजियों के रूप में ठीक से व्यवहार नहीं करते हैं।
mbillard 19

मोनो, यदि आपके आइटम तुलनीय नहीं हैं, तो आपका प्रश्न गलत है। यदि उन्हें शब्दकोश में कुंजियों के रूप में उपयोग नहीं किया जा सकता है, तो कोई समाधान उपलब्ध नहीं है।
स्कोलिमा

1
मुझे लगता है कि मोनो का मतलब था कि चाबी छांटने योग्य नहीं हैं। लेकिन डैनियल का समाधान स्पष्ट रूप से एक हैशटेबल के साथ लागू किया जाना है, न कि एक पेड़ के रूप में, और जब तक एक समतुल्यता परीक्षण और एक हैश फ़ंक्शन है, तब तक काम करेगा।
erickson

मदद के लिए पाठ्यक्रम का उत्थान किया गया है, लेकिन इसे स्वीकार नहीं किया गया है क्योंकि यह एक महत्वपूर्ण बिंदु (जिसे मैं अपने उत्तर में कवर करता हूं) याद आ रहा है।
mbillard

1
एफडब्ल्यूआईडब्ल्यू, आप अपने अंतिम फॉरेस्ट लूप को सरल कर सकते हैं और इसके साथ स्टेटमेंट लौटा सकते हैं:return dict.All(kvp => kvp.Value == 0);
टायसन विलियम्स

18

यह मेरी (डी। जेनिंग्स से काफी प्रभावित) तुलनात्मक विधि का सामान्य कार्यान्वयन है (C # में):

/// <summary>
/// Represents a service used to compare two collections for equality.
/// </summary>
/// <typeparam name="T">The type of the items in the collections.</typeparam>
public class CollectionComparer<T>
{
    /// <summary>
    /// Compares the content of two collections for equality.
    /// </summary>
    /// <param name="foo">The first collection.</param>
    /// <param name="bar">The second collection.</param>
    /// <returns>True if both collections have the same content, false otherwise.</returns>
    public bool Execute(ICollection<T> foo, ICollection<T> bar)
    {
        // Declare a dictionary to count the occurence of the items in the collection
        Dictionary<T, int> itemCounts = new Dictionary<T,int>();

        // Increase the count for each occurence of the item in the first collection
        foreach (T item in foo)
        {
            if (itemCounts.ContainsKey(item))
            {
                itemCounts[item]++;
            }
            else
            {
                itemCounts[item] = 1;
            }
        }

        // Wrap the keys in a searchable list
        List<T> keys = new List<T>(itemCounts.Keys);

        // Decrease the count for each occurence of the item in the second collection
        foreach (T item in bar)
        {
            // Try to find a key for the item
            // The keys of a dictionary are compared by reference, so we have to
            // find the original key that is equivalent to the "item"
            // You may want to override ".Equals" to define what it means for
            // two "T" objects to be equal
            T key = keys.Find(
                delegate(T listKey)
                {
                    return listKey.Equals(item);
                });

            // Check if a key was found
            if(key != null)
            {
                itemCounts[key]--;
            }
            else
            {
                // There was no occurence of this item in the first collection, thus the collections are not equal
                return false;
            }
        }

        // The count of each item should be 0 if the contents of the collections are equal
        foreach (int value in itemCounts.Values)
        {
            if (value != 0)
            {
                return false;
            }
        }

        // The collections are equal
        return true;
    }
}

12
अच्छा काम है, लेकिन ध्यान दें: 1. डैनियल जेनिंग्स समाधान के विपरीत, यह ओ (एन) नहीं है, बल्कि ओ (एन ^ 2) है, क्योंकि बार संग्रह पर फॉरच लूप के अंदर फ़ंक्शन पाया गया; 2. आप ICollection के बजाय IEnumerable <T> को स्वीकार करने की विधि को सामान्य कर सकते हैं <T> कोड में कोई और संशोधन नहीं करने के लिए
Ohad Schneider

The keys of a dictionary are compared by reference, so we have to find the original key that is equivalent to the "item"- यह सच नहीं है। एल्गोरिथ्म गलत धारणाओं पर आधारित है और काम करते समय, यह बहुत अक्षम है।
एंटोनिन लेसेसे


7

यदि आप कंधों का उपयोग करते हैं , तो आप कंटेन्स्ट के साथ कंधे के बल का उपयोग कर सकते हैं।

collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};

collection1.ShouldAllBe(item=>collection2.Contains(item)); // true

और अंत में, आप एक एक्सटेंशन लिख सकते हैं।

public static class ShouldlyIEnumerableExtensions
{
    public static void ShouldEquivalentTo<T>(this IEnumerable<T> list, IEnumerable<T> equivalent)
    {
        list.ShouldAllBe(l => equivalent.Contains(l));
    }
}

अपडेट करें

एक वैकल्पिक पैरामीटर कंधे की विधि पर मौजूद है ।

collection1.ShouldBe(collection2, ignoreOrder: true); // true

1
मैंने अभी नवीनतम संस्करण पर पाया है कि कंधे विधि bool ignoreOrderपर एक पैरामीटर है ।
पियर-लियोनेल सर्ग

5

संपादित करें: मुझे एहसास हुआ कि जैसे ही मुझे पता चला कि यह वास्तव में केवल सेट के लिए काम करता है - यह ठीक से संग्रह के साथ सौदा नहीं करेगा जिसमें डुप्लिकेट आइटम हैं। उदाहरण के लिए {1, 1, 2} और {2, 2, 1} को इस एल्गोरिथम के दृष्टिकोण से बराबर माना जाएगा। यदि आपके संग्रह सेट हैं (या उनकी समानता को उस तरह से मापा जा सकता है), हालांकि, मुझे आशा है कि आप नीचे उपयोगी पाएंगे।

मेरे द्वारा उपयोग किया जाने वाला समाधान है:

return c1.Count == c2.Count && c1.Intersect(c2).Count() == c1.Count;

Linq कवर्स के नीचे डिक्शनरी का काम करता है, इसलिए यह O (N) भी है। (ध्यान दें, यह O (1) है यदि संग्रह समान आकार के नहीं हैं)।

मैंने डैनियल द्वारा सुझाए गए "सेटएक्वाल" पद्धति का उपयोग करके एक पवित्रता की जांच की, इगोर द्वारा सुझाई गई ऑर्डरबाय / सीक्वेंसएक्वाल्स विधि और मेरा सुझाव। परिणाम नीचे हैं, इगोर के लिए O (N * LogN) और मेरा और डैनियल के लिए O (N) दिखा रहा है।

मुझे लगता है कि Linq intersect कोड की सादगी इसे बेहतर समाधान बनाती है।

__Test Latency(ms)__
N, SetEquals, OrderBy, Intersect    
1024, 0, 0, 0    
2048, 0, 0, 0    
4096, 31.2468, 0, 0    
8192, 62.4936, 0, 0    
16384, 156.234, 15.6234, 0    
32768, 312.468, 15.6234, 46.8702    
65536, 640.5594, 46.8702, 31.2468    
131072, 1312.3656, 93.7404, 203.1042    
262144, 3765.2394, 187.4808, 187.4808    
524288, 5718.1644, 374.9616, 406.2084    
1048576, 11420.7054, 734.2998, 718.6764    
2097152, 35090.1564, 1515.4698, 1484.223

इस कोड के साथ एकमात्र मुद्दा यह है कि यह केवल तभी काम करता है जब मूल्य प्रकारों की तुलना करते हैं या बिंदुओं को संदर्भ प्रकारों की तुलना करते हैं। मेरे पास संग्रह में एक ही ऑब्जेक्ट के दो अलग-अलग उदाहरण हो सकते हैं, इसलिए मुझे यह निर्दिष्ट करने में सक्षम होने की आवश्यकता है कि प्रत्येक की तुलना कैसे करें। क्या आप प्रतिच्छेद विधि के लिए एक तुलना प्रतिनिधि को पास कर सकते हैं?
mbillard

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

Intersect पद्धति एक अलग संग्रह लौटाती है। A = {1,1,2} और b = {2,2,1}, a.Intersect (b) .Count ()! = A.Count दिया जाता है, जो आपकी अभिव्यक्ति को सही ढंग से गलत होने का कारण बनता है। {1,2} .Count! = {1,1,2}। लिंक देखें [/ लिंक] (ध्यान दें कि दोनों पक्ष तुलना से पहले अलग हैं।)
ग्रिफिन

5

कोई दोहराए जाने और कोई आदेश नहीं देने के मामले में, निम्नलिखित EqualityComparer का उपयोग शब्दकोश कुंजी के रूप में संग्रह की अनुमति देने के लिए किया जा सकता है:

public class SetComparer<T> : IEqualityComparer<IEnumerable<T>> 
where T:IComparable<T>
{
    public bool Equals(IEnumerable<T> first, IEnumerable<T> second)
    {
        if (first == second)
            return true;
        if ((first == null) || (second == null))
            return false;
        return first.ToHashSet().SetEquals(second);
    }

    public int GetHashCode(IEnumerable<T> enumerable)
    {
        int hash = 17;

        foreach (T val in enumerable.OrderBy(x => x))
            hash = hash * 23 + val.GetHashCode();

        return hash;
    }
}

यहाँ ToHashSet () कार्यान्वयन है जिसका मैंने उपयोग किया है। हैश कोड एल्गोरिथ्म (जॉन स्कीट के माध्यम से) प्रभावी जावा से आता है।


तुलनात्मक वर्ग के लिए अनुक्रमिक बिंदु क्या है? : ओ इसके अलावा, आप ISet<T>सेट के लिए अभिप्रेत करने के लिए इनपुट को बदल सकते हैं (यानी कोई डुप्लिकेट नहीं)।
नवफाल

@nawfal धन्यवाद, मुझे नहीं पता कि मैं क्या सोच रहा था जब मैंने इसे Serializable चिह्नित किया था ... जैसा कि ISet, यहां विचार IEnumerableएक सेट के रूप में व्यवहार करना था (क्योंकि आपको एक IEnumerableशुरुआत करने के लिए मिला था ), हालांकि 0 से अधिक upvotes पर विचार 5 साल जो सबसे तेज विचार नहीं हो सकता है: P
Ohad Schneider

4
static bool SetsContainSameElements<T>(IEnumerable<T> set1, IEnumerable<T> set2) {
    var setXOR = new HashSet<T>(set1);
    setXOR.SymmetricExceptWith(set2);
    return (setXOR.Count == 0);
}

समाधान के लिए .NET 3.5 और System.Collections.Genericनेमस्पेस की आवश्यकता होती है । Microsoft के अनुसार , SymmetricExceptWithएक है ओ (n + m) आपरेशन, के साथ n पहले सेट में तत्वों और की संख्या का प्रतिनिधित्व मीटर दूसरे में तत्वों की संख्या का प्रतिनिधित्व। यदि आवश्यक हो तो आप हमेशा इस समारोह में एक समानता तुलना जोड़ सकते हैं।


3

क्यों नहीं उपयोग .Except ()

// Create the IEnumerable data sources.
string[] names1 = System.IO.File.ReadAllLines(@"../../../names1.txt");
string[] names2 = System.IO.File.ReadAllLines(@"../../../names2.txt");
// Create the query. Note that method syntax must be used here.
IEnumerable<string> differenceQuery =   names1.Except(names2);
// Execute the query.
Console.WriteLine("The following lines are in names1.txt but not names2.txt");
foreach (string s in differenceQuery)
     Console.WriteLine(s);

http://msdn.microsoft.com/en-us/library/bb397894.aspx


2
Exceptडुप्लिकेट आइटमों की गिनती के लिए काम नहीं करेगा। यह सेट {1,2,2} और {1,1,2} के लिए सही लौटेगा।
क्रिश्चियन डियाकोन्सकु

@CristiDiaconescu आप किसी भी डुप्लिकेट को हटाने के लिए एक ".डिस्टेक्ट ()" सबसे पहले कर सकते हैं
कोरयेम

ओपी पूछ रहा है [1, 1, 2] != [1, 2, 2]। उपयोग Distinctकरने से वे समान दिखेंगे।
क्रिश्चियन डियाकोन्सक्यू

2

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

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

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

मूल पोस्ट यहाँ


1

एरिकसन लगभग सही है: चूंकि आप डुप्लिकेट की गिनती पर मैच करना चाहते हैं, आप एक बैग चाहते हैं । जावा में, यह कुछ इस तरह दिखता है:

(new HashBag(collection1)).equals(new HashBag(collection2))

मुझे यकीन है कि C # में एक अंतर्निहित सेट कार्यान्वयन है। मैं उसका उपयोग पहले करूंगा; यदि प्रदर्शन एक समस्या है, तो आप हमेशा एक अलग सेट कार्यान्वयन का उपयोग कर सकते हैं, लेकिन समान सेट इंटरफ़ेस का उपयोग करें।


1

किसी के लिए उपयोगी होने की स्थिति में, ओएचडीएससी के उत्तर का मेरा विस्तार विधि संस्करण है

static public class EnumerableExtensions 
{
    static public bool IsEquivalentTo<T>(this IEnumerable<T> first, IEnumerable<T> second)
    {
        if ((first == null) != (second == null))
            return false;

        if (!object.ReferenceEquals(first, second) && (first != null))
        {
            if (first.Count() != second.Count())
                return false;

            if ((first.Count() != 0) && HaveMismatchedElement<T>(first, second))
                return false;
        }

        return true;
    }

    private static bool HaveMismatchedElement<T>(IEnumerable<T> first, IEnumerable<T> second)
    {
        int firstCount;
        int secondCount;

        var firstElementCounts = GetElementCounts<T>(first, out firstCount);
        var secondElementCounts = GetElementCounts<T>(second, out secondCount);

        if (firstCount != secondCount)
            return true;

        foreach (var kvp in firstElementCounts)
        {
            firstCount = kvp.Value;
            secondElementCounts.TryGetValue(kvp.Key, out secondCount);

            if (firstCount != secondCount)
                return true;
        }

        return false;
    }

    private static Dictionary<T, int> GetElementCounts<T>(IEnumerable<T> enumerable, out int nullCount)
    {
        var dictionary = new Dictionary<T, int>();
        nullCount = 0;

        foreach (T element in enumerable)
        {
            if (element == null)
            {
                nullCount++;
            }
            else
            {
                int num;
                dictionary.TryGetValue(element, out num);
                num++;
                dictionary[element] = num;
            }
        }

        return dictionary;
    }

    static private int GetHashCode<T>(IEnumerable<T> enumerable)
    {
        int hash = 17;

        foreach (T val in enumerable.OrderBy(x => x))
            hash = hash * 23 + val.GetHashCode();

        return hash;
    }
}

यह कितना अच्छा प्रदर्शन करता है, कोई विचार?
नवाफल

मैं केवल छोटे संग्रह के लिए इसका उपयोग करता हूं, इसलिए बिग-ओ जटिलता या बेंचमार्किंग के बारे में नहीं सोचा है। HaveMismatchedElements अकेले O (M * N) है, इसलिए यह बड़े संग्रहों के लिए अच्छा प्रदर्शन नहीं कर सकता है।
एरिक जे।

यदि IEnumerable<T>प्रश्न हैं तो कॉल Count()करना अच्छा विचार नहीं है। ओहद के मूल उत्तर की जाँच का दृष्टिकोण कि वे ICollection<T>बेहतर विचार हैं।
नवफाल

1

यहाँ एक समाधान है जो इस एक पर एक सुधार है ।

public static bool HasSameElementsAs<T>(
        this IEnumerable<T> first, 
        IEnumerable<T> second, 
        IEqualityComparer<T> comparer = null)
    {
        var firstMap = first
            .GroupBy(x => x, comparer)
            .ToDictionary(x => x.Key, x => x.Count(), comparer);

        var secondMap = second
            .GroupBy(x => x, comparer)
            .ToDictionary(x => x.Key, x => x.Count(), comparer);

        if (firstMap.Keys.Count != secondMap.Keys.Count)
            return false;

        if (firstMap.Keys.Any(k1 => !secondMap.ContainsKey(k1)))
            return false;

        return firstMap.Keys.All(x => firstMap[x] == secondMap[x]);
    }

0

इस समस्या के कई समाधान हैं। यदि आप डुप्लिकेट के बारे में परवाह नहीं करते हैं, तो आपको दोनों को सॉर्ट करने की आवश्यकता नहीं है। पहले सुनिश्चित करें कि उनके पास समान आइटम हैं। इसके बाद संग्रह में से एक को छाँटें। फिर छांटे गए संग्रह में दूसरे संग्रह से प्रत्येक आइटम की खोज करें। यदि आपको किसी दिए गए आइटम को रोकना नहीं है और वह गलत है। इस की जटिलता: - पहला संग्रह छांटना: एन लॉग (एन) - प्रत्येक आइटम को दूसरे से पहले में खोजना: एनलॉग (एन) तो आप 2 * एन * लॉग (एन) के साथ समाप्त होते हैं यह मानते हुए कि वे मेल खाते हैं और आप सब कुछ देखते हैं। यह दोनों को छांटने की जटिलता के समान है। इसके अलावा अगर आपको इसमें कोई अंतर है तो पहले बंद करने का लाभ देता है। हालाँकि, ध्यान रखें कि यदि आप इस तुलना में कदम रखने से पहले दोनों को क्रमबद्ध करते हैं और आप एक qsort जैसी किसी चीज़ का उपयोग करके छँटाई करते हैं, तो छँटाई अधिक महंगी होगी। इसके लिए अनुकूलन हैं। एक अन्य विकल्प, जो छोटे संग्रह के लिए बहुत अच्छा है, जहां आप जानते हैं कि तत्वों की सीमा बिटमास्क इंडेक्स का उपयोग करना है। यह आपको O (n) प्रदर्शन देगा। एक अन्य विकल्प हैश का उपयोग करना और इसे देखना है। छोटे संग्रहों के लिए आमतौर पर छँटाई या बिटमास्क इंडेक्स करना बहुत बेहतर होता है। हैशटेबल से बदतर इलाके का नुकसान होता है इसलिए इसका ध्यान रखें। फिर, अगर आप डॉन डुप्लिकेट के बारे में परवाह है। यदि आप डुप्लिकेट के लिए खाता बनाना चाहते हैं, तो दोनों को क्रमबद्ध करें।


0

कई मामलों में एकमात्र उपयुक्त उत्तर इगोर ओस्ट्रोव्स्की में से एक है, अन्य उत्तर वस्तुओं के हैश कोड पर आधारित हैं। लेकिन जब आप किसी वस्तु के लिए एक हैश कोड उत्पन्न करते हैं तो आप केवल उसके IMMUTABLE फ़ील्ड के आधार पर करते हैं - जैसे ऑब्जेक्ट Id फ़ील्ड (डेटाबेस निकाय के मामले में) - GetHashCode को ओवरराइड करने के लिए महत्वपूर्ण क्यों है जब समान विधि को अस्वीकार कर दिया जाता है?

इसका मतलब यह है, कि यदि आप दो संग्रहों की तुलना करते हैं, तो परिणाम तुलना पद्धति का सही हो सकता है, भले ही विभिन्न मदों के क्षेत्र गैर-बराबर हों। संग्रह की गहरी तुलना करने के लिए, आपको इगोर की विधि का उपयोग करने और आईक्वालिटी को लागू करने की आवश्यकता है।

कृपया मुझे और mr.Schnider की टिप्पणियों को उनके सबसे अधिक मतदान वाले पोस्ट पर पढ़ें।

जेम्स


0

IEnumerable<T>(यदि सेट वांछनीय नहीं हैं) और "आदेश की अनदेखी" में डुप्लिकेट के लिए अनुमति दे रहे हैं आप एक का उपयोग करने में सक्षम होना चाहिए .GroupBy()

मैं जटिलता माप का विशेषज्ञ नहीं हूं, लेकिन मेरी अल्पविकसित समझ यह है कि यह O (n) होना चाहिए। मैं O (n ^ 2) को एक अन्य O (n) ऑपरेशन जैसे O (n) ऑपरेशन के प्रदर्शन से आता हूं ListA.Where(a => ListB.Contains(a)).ToList()। ListB में प्रत्येक आइटम का मूल्यांकन ListA में प्रत्येक आइटम के खिलाफ समानता के लिए किया जाता है।

जैसा कि मैंने कहा, जटिलता पर मेरी समझ सीमित है, इसलिए अगर मैं गलत हूं तो मुझे इस पर सही करें।

public static bool IsSameAs<T, TKey>(this IEnumerable<T> source, IEnumerable<T> target, Expression<Func<T, TKey>> keySelectorExpression)
    {
        // check the object
        if (source == null && target == null) return true;
        if (source == null || target == null) return false;

        var sourceList = source.ToList();
        var targetList = target.ToList();

        // check the list count :: { 1,1,1 } != { 1,1,1,1 }
        if (sourceList.Count != targetList.Count) return false;

        var keySelector = keySelectorExpression.Compile();
        var groupedSourceList = sourceList.GroupBy(keySelector).ToList();
        var groupedTargetList = targetList.GroupBy(keySelector).ToList();

        // check that the number of grouptings match :: { 1,1,2,3,4 } != { 1,1,2,3,4,5 }
        var groupCountIsSame = groupedSourceList.Count == groupedTargetList.Count;
        if (!groupCountIsSame) return false;

        // check that the count of each group in source has the same count in target :: for values { 1,1,2,3,4 } & { 1,1,1,2,3,4 }
        // key:count
        // { 1:2, 2:1, 3:1, 4:1 } != { 1:3, 2:1, 3:1, 4:1 }
        var countsMissmatch = groupedSourceList.Any(sourceGroup =>
                                                        {
                                                            var targetGroup = groupedTargetList.Single(y => y.Key.Equals(sourceGroup.Key));
                                                            return sourceGroup.Count() != targetGroup.Count();
                                                        });
        return !countsMissmatch;
    }

0

यह सरल समाधानIEnumerable लागू करने के लिए सामान्य प्रकार को मजबूर करता है IComparable। की OrderByपरिभाषा के कारण।

यदि आप ऐसी कोई धारणा नहीं बनाना चाहते हैं, लेकिन फिर भी इस समाधान का उपयोग करना चाहते हैं, तो आप निम्नलिखित कोड का उपयोग कर सकते हैं:

bool equal = collection1.OrderBy(i => i?.GetHashCode())
   .SequenceEqual(collection2.OrderBy(i => i?.GetHashCode()));

0

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

उपयोग:

using Microsoft.VisualStudio.TestTools.UnitTesting;

// define collection1, collection2, ...

Assert.Equal(collection1.OrderBy(c=>c).ToCsv(), collection2.OrderBy(c=>c).ToCsv());

सहायक विस्तार विधि:

public static string ToCsv<T>(
    this IEnumerable<T> values,
    Func<T, string> selector,
    string joinSeparator = ",")
{
    if (selector == null)
    {
        if (typeof(T) == typeof(Int16) ||
            typeof(T) == typeof(Int32) ||
            typeof(T) == typeof(Int64))
        {
            selector = (v) => Convert.ToInt64(v).ToStringInvariant();
        }
        else if (typeof(T) == typeof(decimal))
        {
            selector = (v) => Convert.ToDecimal(v).ToStringInvariant();
        }
        else if (typeof(T) == typeof(float) ||
                typeof(T) == typeof(double))
        {
            selector = (v) => Convert.ToDouble(v).ToString(CultureInfo.InvariantCulture);
        }
        else
        {
            selector = (v) => v.ToString();
        }
    }

    return String.Join(joinSeparator, values.Select(v => selector(v)));
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.