سؤال

أرغب في مقارنة محتويات مجموعتين في طريقة Equals الخاصة بي.لدي قاموس وIList.هل هناك طريقة مدمجة للقيام بذلك؟

تم التعديل:أريد مقارنة قاموسين واثنين من قوائم IL، لذلك أعتقد أن معنى المساواة واضح - إذا كان القاموسان يحتويان على نفس المفاتيح المعينة لنفس القيم، فهما متساويان.

هل كانت مفيدة؟

المحلول

Enumerable.SequenceEqual

تحديد ما إذا كان تسلسلان متساويان من خلال مقارنة عناصرهما باستخدام IEqualityComparer(T) المحدد.

لا يمكنك مقارنة القائمة والقاموس مباشرة، ولكن يمكنك مقارنة قائمة القيم من القاموس بالقائمة

نصائح أخرى

كما اقترح الآخرون ولاحظوا، SequenceEqual حساس للطلب.لحل هذه المشكلة، يمكنك فرز القاموس حسب المفتاح (وهو فريد، وبالتالي يكون الفرز مستقرًا دائمًا) ثم استخدامه SequenceEqual.يتحقق التعبير التالي من تساوي قواميسين بغض النظر عن ترتيبهم الداخلي:

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

يحرر: كما أشار Jeppe Stig Nielsen، فإن بعض الأشياء لها IComparer<T> وهذا لا يتوافق مع بهم IEqualityComparer<T>, ، مما يؤدي إلى نتائج غير صحيحة.عند استخدام المفاتيح مع مثل هذا الكائن، يجب عليك تحديد الصحيح IComparer<T> لتلك المفاتيح.على سبيل المثال، مع مفاتيح السلسلة (التي تظهر هذه المشكلة)، يجب عليك القيام بما يلي للحصول على النتائج الصحيحة:

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

بالإضافة إلى ما ذكر SequenceEqual, ، أيّ

صحيح إذا كانت قائمتين متساوية في الطول وعناصرها المقابلة تقارن متساوية وفقًا للمقارنة

(والذي قد يكون المقارنة الافتراضية، أي.تجاوز Equals())

ومن الجدير بالذكر أنه يوجد في .Net4 تعيين يساوي على ISet الكائنات ، التي

يتجاهل ترتيب العناصر وأي عناصر مكررة.

لذا، إذا كنت تريد الحصول على قائمة بالأشياء، ولكن ليس من الضروري أن تكون بترتيب معين، فاعتبر ذلك أمرًا ISet (مثل أ HashSet) قد يكون الاختيار الصحيح.

نلقي نظرة على 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);

.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

لم أكن أعرف طريقة 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، يبدو أن Microsoft قامت ببناء الوظيفة التي وصفتها لك.

لا يعد هذا إجابة مباشرة على أسئلتك، ولكن توفر كل من أدوات الاختبار وNUnit لـ MS

 CollectionAssert.AreEquivalent

الذي يفعل إلى حد كبير ما تريد.

لمقارنة المجموعات يمكنك أيضًا استخدام LINQ. Enumerable.Intersect إرجاع جميع الأزواج المتساوية.يمكنك المقارنة بين قاموسين مثل هذا:

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

هناك حاجة إلى المقارنة الأولى ل dict2 يمكن أن تحتوي على كافة المفاتيح من dict1 و اكثر.

يمكنك أيضًا استخدام التفكير في الاختلافات باستخدام Enumerable.Except و Enumerable.Union التي تؤدي إلى نتائج مماثلة.ولكن يمكن استخدامها لتحديد الاختلافات الدقيقة بين المجموعات.

ماذا عن هذا المثال:

 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/dictionary-equals

للمجموعات المطلوبة (قائمة، صفيف) استخدم 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>)

لا.لا يحتوي إطار التجميع على أي مفهوم للمساواة.إذا فكرت في الأمر، فلا توجد طريقة لمقارنة المجموعات التي ليست ذاتية.على سبيل المثال، بمقارنة قائمة IList الخاصة بك بالقاموس الخاص بك، هل سيكونان متساويين إذا كانت جميع المفاتيح في قائمة IList، أو كانت جميع القيم في قائمة IList، أو إذا كان كلاهما في قائمة IList؟لا توجد طريقة واضحة لمقارنة هاتين المجموعتين دون معرفة الغرض الذي سيتم استخدامهما من أجله، لذا فإن طريقة تساوي الأغراض العامة ليس لها أي معنى.

لا، لأن إطار العمل لا يعرف كيفية مقارنة محتويات قوائمك.

الق نظرة على هذا:

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

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;
}

لم يكن هناك، ولا يوجد، وربما لا يكون، على الأقل أعتقد ذلك.السبب وراء ذلك هو أن المساواة في التحصيل ربما تكون سلوكًا يحدده المستخدم.

ليس من المفترض أن تكون العناصر في المجموعات بترتيب معين على الرغم من أن لها ترتيبًا طبيعيًا، وهذا ليس ما يجب أن تعتمد عليه خوارزميات المقارنة.لنفترض أن لديك مجموعتين من:

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

هل هم متساوون أم لا؟يجب أن تعرف ولكني لا أعرف ما هي وجهة نظرك.

المجموعات غير مرتبة من الناحية النظرية بشكل افتراضي، حتى توفر الخوارزميات قواعد الفرز.نفس الشيء الذي سيلفت خادم SQL انتباهك إليه هو أنه عندما تحاول إجراء ترقيم الصفحات، فإنه يتطلب منك توفير قواعد الفرز:

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

  • إذا كانت المراجع متساوية، فهو بوريس فقط

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top