C#比较两个字典的平等 [英] C# Compare two dictionaries for equality

查看:134
本文介绍了C#比较两个字典的平等的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在C#两个字典中使用一个字符串进行比较,并将值作为 int 的列表。我假设两个字典都相同,当它们都具有相同的键和每个键作为值具有相同整数的列表(两者不一定是相同的顺序)。



我同时使用相关的问题,但都失败了测试功能的测试套件 DoesOrderKeysMatter DoesOrderValuesMatter



我的测试套件:

  using System; 
使用Microsoft.VisualStudio.TestTools.UnitTesting;
使用System.Collections.Generic;
使用System.Linq;


命名空间UnitTestProject1
{
[TestClass]
public class ProvideReportTests
{
[TestMethod]
public void AreSameDictionariesEqual()
{
//排列
字典< string,List< int>> dict1 = new Dictionary< string,List< int>>();
列表< int> list1 = new List< int>();
list1.Add(1);
list1.Add(2);
dict1.Add(a,list1);
列表< int> list2 = new List< int>();
list2.Add(3);
list2.Add(4);
dict1.Add(b,list2);

// act
bool dictsAreEqual = false;
dictsAreEqual = AreDictionariesEqual(dict1,dict1);

// assert
Assert.IsTrue(dictsAreEqual,Dictionaries is not equal);

}

[TestMethod]
public void AreDifferentDictionariesNotEqual()
{
//排列
字典< string,List< ; INT>> dict1 = new Dictionary< string,List< int>>();
列表< int> list1 = new List< int>();
list1.Add(1);
list1.Add(2);
dict1.Add(a,list1);
列表< int> list2 = new List< int>();
list2.Add(3);
list2.Add(4);
dict1.Add(b,list2);

字典< string,List< int>> dict2 = new Dictionary< string,List< int>>();

// act
bool dictsAreEqual = true;
dictsAreEqual = AreDictionariesEqual(dict1,dict2);

// assert
Assert.IsFalse(dictsAreEqual,Dictionaries are equal);

}

[TestMethod]
public void DoesOrderKeysMatter()
{
//排列
字典< string,List< ; INT>> dict1 = new Dictionary< string,List< int>>();
列表< int> list1 = new List< int>();
list1.Add(1);
list1.Add(2);
dict1.Add(a,list1);
列表< int> list2 = new List< int>();
list2.Add(3);
list2.Add(4);
dict1.Add(b,list2);

字典< string,List< int>> dict2 = new Dictionary< string,List< int>>();
列表< int> list3 = new List< int>();
list3.Add(3);
list3.Add(4);
dict2.Add(b,list3);
列表< int> list4 = new List< int>();
list4.Add(1);
list4.Add(2);
dict2.Add(a,list4);

// act
bool dictsAreEqual = false;
dictsAreEqual = AreDictionariesEqual(dict1,dict2);

// assert
Assert.IsTrue(dictsAreEqual,Dictionaries is not equal);

}

[TestMethod]
public void DoesOrderValuesMatter()
{
//排列
字典< string,List< ; INT>> dict1 = new Dictionary< string,List< int>>();
列表< int> list1 = new List< int>();
list1.Add(1);
list1.Add(2);
dict1.Add(a,list1);
列表< int> list2 = new List< int>();
list2.Add(3);
list2.Add(4);
dict1.Add(b,list2);

字典< string,List< int>> dict2 = new Dictionary< string,List< int>>();
列表< int> list3 = new List< int>();
list3.Add(2);
list3.Add(1);
dict2.Add(a,list3);
列表< int> list4 = new List< int>();
list4.Add(4);
list4.Add(3);
dict2.Add(b,list4);

// act
bool dictsAreEqual = false;
dictsAreEqual = AreDictionariesEqual(dict1,dict2);

// assert
Assert.IsTrue(dictsAreEqual,Dictionaries is not equal);

}


private bool AreDictionariesEqual(Dictionary< string,List< int>> dict1,Dictionary< string,List< int>> dict2)
{
return dict1.Keys.Count == dict2.Keys.Count&&
dict1.Keys.All(k => dict2.ContainsKey(k)&& object.Equals(dict2 [k],dict1 [k]));

//也失败:
// return dict1.OrderBy(kvp => kvp.Key).SequenceEqual(dict2.OrderBy(kvp => kvp.Key));
}
}
}

什么是正确的方法比较这些字典?还是有我的错误(诚然笨拙的)TestSuite?



更新



我试图将Servy的答案纳入我的测试套件,如下所示,但我收到一些错误(在Visual Studio中用红色的虚线加下划线):




  • SetEquals Equals方法说中的不包含接受第一个参数类型为Generic.List的SetEquals的定义。

  • 在AreDictionariesEqual 它说 DictionaryComparer>是一个类型,但用作变量。



    命名空间UnitTestProject1
    {
    [TestClass]
    public class ProvideReportTests
    {
    [TestMethod]
    // ...与上述相同

      private bool AreDictionariesEqual(Dictionary< string,List< int>> dict1,Dictionary< string,List< int>> dict2)
    {
    DictionaryComparer< string ,List< int>(new ListComparer< int>()dc = new DictionaryComparer< string,List < int>>(new ListComparer< int>();
    return dc.Equals(dict1,dict2);

    }

    }

    public class DictionaryComparer&TK,TValue> :
    IEqualityComparer< Dictionary< TKey,TValue>>
    {
    private IEqualityComparer< TValue> valueComparer;
    public DictionaryComparer(IEqualityComparer< TValue> valueComparer = null)
    {
    this.valueComparer = valueComparer? EqualityComparer< TValue> .DEFAULT;
    }
    public bool Equals(Dictionary< TKey,TValue> x,Dictionary< TKey,TValue> y)
    {
    if(x.Count!= y.Count)
    返回false;
    if(x.Keys.Except(y.Keys).Any())
    return false;
    if(y.Keys.Except(x.Keys).Any())
    return false;
    foreach(var pair in x)
    if(!valueComparer.Equals(pair.Value,y [pair.Key]))
    return false;
    返回true;
    }

    public int GetHashCode(Dictionary< TKey,TValue> obj)
    {
    throw new NotImplementedException();
    }
    }

    public class ListComparer< T> :IEqualityComparer< List< T>>
    {
    private IEqualityComparer< T> valueComparer;
    public ListComparer(IEqualityComparer< T> valueComparer = null)
    {
    this.valueComparer = valueComparer? EqualityComparer< T> .DEFAULT;
    }

    public bool等于(List< T> x,List< T> y)
    {
    return x.SetEquals(y,valueComparer);
    }

    public int GetHashCode(List< T> obj)
    {
    throw new NotImplementedException();
    }
    }

    public static bool SetEquals< T>(此IEnumerable< T>第一,IEnumerable< T>第二,IEqualityComparer< T>比较器)
    {
    返回新的HashSet< T>(第二,比较器?? EqualityComparer< T> .Default)
    .SetEquals(first);
    }

    }



解决方案

首先,我们需要一个字典的平等比较器。它需要确保它们具有匹配的密钥,如果这样做,则比较每个密钥的值:

  public class DictionaryComparer< TKey,TValue> :
IEqualityComparer< Dictionary< TKey,TValue>>
{
private IEqualityComparer< TValue> valueComparer;
public DictionaryComparer(IEqualityComparer< TValue> valueComparer = null)
{
this.valueComparer = valueComparer? EqualityComparer< TValue> .DEFAULT;
}
public bool Equals(Dictionary< TKey,TValue> x,Dictionary< TKey,TValue> y)
{
if(x.Count!= y.Count)
返回false;
if(x.Keys.Except(y.Keys).Any())
return false;
if(y.Keys.Except(x.Keys).Any())
return false;
foreach(var pair in x)
if(!valueComparer.Equals(pair.Value,y [pair.Key]))
return false;
返回true;
}

public int GetHashCode(Dictionary< TKey,TValue> obj)
{
throw new NotImplementedException();
}
}

但这还不够。我们需要使用另一个自定义比较器比较字典的值,而不是默认比较器,因为默认列表比较器不会查看列表的值:

  public class ListComparer< T> :IEqualityComparer< List< T>> 
{
private IEqualityComparer< T> valueComparer;
public ListComparer(IEqualityComparer< T> valueComparer = null)
{
this.valueComparer = valueComparer? EqualityComparer< T> .DEFAULT;
}

public bool等于(List< T> x,List< T> y)
{
return x.SetEquals(y,valueComparer);
}

public int GetHashCode(List< T> obj)
{
throw new NotImplementedException();
}
}

其中使用以下扩展方法:

  public static bool SetEquals< T>(此IEnumerable< T>第一,IEnumerable< T>第二,
IEqualityComparer&T; )
{
返回新的HashSet< T>(第二,比较器?? EqualityComparer< T> .Default)
.SetEquals(first);
}

现在我们可以简单地写道:


$ b新的ListComparer< string,List< int>>(new ListComparer< int>())
.Equals(dict1,dict2); $ b $


I want to compare in C# two dictionaries with as keys a string and as value a list of ints. I assume two dictionaries to be equal when they both have the same keys and for each key as value a list with the same integers (both not necessarily in the same order).

I use both the answers from this and this related question, but both fail my test suite for the test functions DoesOrderKeysMatter and DoesOrderValuesMatter.

My test Suite:

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;


namespace UnitTestProject1
{
    [TestClass]
    public class ProvideReportTests
    {
        [TestMethod]
        public void AreSameDictionariesEqual()
        {
            // arrange
            Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
            List<int> list1 = new List<int>();
            list1.Add(1);
            list1.Add(2);
            dict1.Add("a", list1);
            List<int> list2 = new List<int>();
            list2.Add(3);
            list2.Add(4);
            dict1.Add("b", list2);

            // act
            bool dictsAreEqual = false;
            dictsAreEqual = AreDictionariesEqual(dict1, dict1);

            // assert
            Assert.IsTrue(dictsAreEqual, "Dictionaries are not equal");

        }

        [TestMethod]
        public void AreDifferentDictionariesNotEqual()
        {
            // arrange
            Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
            List<int> list1 = new List<int>();
            list1.Add(1);
            list1.Add(2);
            dict1.Add("a", list1);
            List<int> list2 = new List<int>();
            list2.Add(3);
            list2.Add(4);
            dict1.Add("b", list2);

            Dictionary<string, List<int>> dict2 = new Dictionary<string, List<int>>();

            // act
            bool dictsAreEqual = true;
            dictsAreEqual = AreDictionariesEqual(dict1, dict2);

            // assert
            Assert.IsFalse(dictsAreEqual, "Dictionaries are equal");

        }

        [TestMethod]
        public void DoesOrderKeysMatter()
        {
            // arrange
            Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
            List<int> list1 = new List<int>();
            list1.Add(1);
            list1.Add(2);
            dict1.Add("a", list1);
            List<int> list2 = new List<int>();
            list2.Add(3);
            list2.Add(4);
            dict1.Add("b", list2);

            Dictionary<string, List<int>> dict2 = new Dictionary<string, List<int>>();
            List<int> list3 = new List<int>();
            list3.Add(3);
            list3.Add(4);
            dict2.Add("b", list3);
            List<int> list4 = new List<int>();
            list4.Add(1);
            list4.Add(2);
            dict2.Add("a", list4);

            // act
            bool dictsAreEqual = false;
            dictsAreEqual = AreDictionariesEqual(dict1, dict2);

            // assert
            Assert.IsTrue(dictsAreEqual, "Dictionaries are not equal");

        }

        [TestMethod]
        public void DoesOrderValuesMatter()
        {
            // arrange
            Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
            List<int> list1 = new List<int>();
            list1.Add(1);
            list1.Add(2);
            dict1.Add("a", list1);
            List<int> list2 = new List<int>();
            list2.Add(3);
            list2.Add(4);
            dict1.Add("b", list2);

            Dictionary<string, List<int>> dict2 = new Dictionary<string, List<int>>();
            List<int> list3 = new List<int>();
            list3.Add(2);
            list3.Add(1);
            dict2.Add("a", list3);
            List<int> list4 = new List<int>();
            list4.Add(4);
            list4.Add(3);
            dict2.Add("b", list4);

            // act
            bool dictsAreEqual = false;
            dictsAreEqual = AreDictionariesEqual(dict1, dict2);

            // assert
            Assert.IsTrue(dictsAreEqual, "Dictionaries are not equal");

        }


        private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
        {
            return dict1.Keys.Count == dict2.Keys.Count &&
                   dict1.Keys.All(k => dict2.ContainsKey(k) && object.Equals(dict2[k], dict1[k]));

            // also fails:
            //    return dict1.OrderBy(kvp => kvp.Key).SequenceEqual(dict2.OrderBy(kvp => kvp.Key));
        }
    }
}

What is the correct way to compare these kind of dictionaries? Or is there an error in my (admittedly clumsily written) TestSuite?

Update

I'm trying to incorporate Servy's answer in my test suite, like below, but I get some errors (underlined with a red wiggly line in Visual Studio):

  • SetEquals in the `Equals method says: "does not contain a definition for SetEquals accepting a first argument of type Generic.List.
  • In AreDictionariesEqualit saysDictionaryComparer> is a type but is used as a variable.`

    namespace UnitTestProject1 { [TestClass] public class ProvideReportTests { [TestMethod] // ... same as above

        private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
        {
            DictionaryComparer<string, List<int>>(new ListComparer<int>() dc = new DictionaryComparer<string, List<int>>(new ListComparer<int>();
            return dc.Equals(dict1, dict2);
    
        }
    
    }
    
    public class DictionaryComparer<TKey, TValue> :
        IEqualityComparer<Dictionary<TKey, TValue>>
    {
        private IEqualityComparer<TValue> valueComparer;
        public DictionaryComparer(IEqualityComparer<TValue> valueComparer = null)
        {
            this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
        }
        public bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
        {
            if (x.Count != y.Count)
                return false;
            if (x.Keys.Except(y.Keys).Any())
                return false;
            if (y.Keys.Except(x.Keys).Any())
                return false;
            foreach (var pair in x)
                if (!valueComparer.Equals(pair.Value, y[pair.Key]))
                    return false;
            return true;
        }
    
        public int GetHashCode(Dictionary<TKey, TValue> obj)
        {
            throw new NotImplementedException();
        }
    }
    
    public class ListComparer<T> : IEqualityComparer<List<T>>
    {
        private IEqualityComparer<T> valueComparer;
        public ListComparer(IEqualityComparer<T> valueComparer = null)
        {
            this.valueComparer = valueComparer ?? EqualityComparer<T>.Default;
        }
    
        public bool Equals(List<T> x, List<T> y)
        {
            return x.SetEquals(y, valueComparer);
        }
    
        public int GetHashCode(List<T> obj)
        {
            throw new NotImplementedException();
        }
    }
    
    public static bool SetEquals<T>(this IEnumerable<T> first, IEnumerable<T> second, IEqualityComparer<T> comparer)
        {
            return new HashSet<T>(second, comparer ?? EqualityComparer<T>.Default)
                .SetEquals(first);
        }
    

    }

解决方案

So first we need an equality comparer for dictionaries. It needs to ensure that they have matching keys and, if they do, compare the values of each key:

public class DictionaryComparer<TKey, TValue> :
    IEqualityComparer<Dictionary<TKey, TValue>>
{
    private IEqualityComparer<TValue> valueComparer;
    public DictionaryComparer(IEqualityComparer<TValue> valueComparer = null)
    {
        this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
    }
    public bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
    {
        if (x.Count != y.Count)
            return false;
        if (x.Keys.Except(y.Keys).Any())
            return false;
        if (y.Keys.Except(x.Keys).Any())
            return false;
        foreach (var pair in x)
            if (!valueComparer.Equals(pair.Value, y[pair.Key]))
                return false;
        return true;
    }

    public int GetHashCode(Dictionary<TKey, TValue> obj)
    {
        throw new NotImplementedException();
    }
}

but this isn't enough on its own. We need to compare the values of the dictionary using another custom comparer, not the default comparer as the default list comparer won't look at the values of the list:

public class ListComparer<T> : IEqualityComparer<List<T>>
{
    private IEqualityComparer<T> valueComparer;
    public ListComparer(IEqualityComparer<T> valueComparer = null)
    {
        this.valueComparer = valueComparer ?? EqualityComparer<T>.Default;
    }

    public bool Equals(List<T> x, List<T> y)
    {
        return x.SetEquals(y, valueComparer);
    }

    public int GetHashCode(List<T> obj)
    {
        throw new NotImplementedException();
    }
}

Which uses the following extension method:

public static bool SetEquals<T>(this IEnumerable<T> first, IEnumerable<T> second,
    IEqualityComparer<T> comparer)
{
    return new HashSet<T>(second, comparer ?? EqualityComparer<T>.Default)
        .SetEquals(first);
}

Now we can simply write:

new DictionaryComparer<string, List<int>>(new ListComparer<int>())
    .Equals(dict1, dict2);

这篇关于C#比较两个字典的平等的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆