Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ssis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#比较两本字典是否相等_C#_Unit Testing_Dictionary - Fatal编程技术网

C#比较两本字典是否相等

C#比较两本字典是否相等,c#,unit-testing,dictionary,C#,Unit Testing,Dictionary,我想在C#中比较两个字典,其中as键为字符串,as值为ints列表。我假设两个字典是相等的,当它们都有相同的键,并且对于每个键,都是一个具有相同整数的列表(两者不一定以相同的顺序) 我使用了来自和相关问题的答案,但都未通过测试函数的测试套件DoesOrderKeysMatter和DoesOrderValuesMatter 我的测试套件: using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.C

我想在C#中比较两个字典,其中as键为
字符串
,as值为
int
s列表。我假设两个字典是相等的,当它们都有相同的键,并且对于每个键,都是一个具有相同整数的列表(两者不一定以相同的顺序)

我使用了来自和相关问题的答案,但都未通过测试函数的测试套件
DoesOrderKeysMatter
DoesOrderValuesMatter

我的测试套件:

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

所以首先我们需要一个字典的相等比较器。它需要确保它们有匹配的键,如果有,则比较每个键的值:

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();
    }
}
公共类词典比较程序:
质量比较员
{
私人品质比较师;
公共词典比较程序(IEqualityComparer valueComparer=null)
{
this.valueComparer=valueComparer??EqualityComparer.Default;
}
公共布尔等于(字典x、字典y)
{
如果(x.Count!=y.Count)
返回false;
if(x.Keys.Except(y.Keys.Any())
返回false;
if(y.Keys.Except(x.Keys.Any())
返回false;
foreach(x中的var对)
如果(!valueComparer.Equals(pair.Value,y[pair.Key]))
返回false;
返回true;
}
公共int GetHashCode(字典obj)
{
抛出新的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();
    }
}
公共类ListComparer:IEqualityComparer
{
私人品质比较师;
公共列表比较程序(IEqualityComparer valueComparer=null)
{
this.valueComparer=valueComparer??EqualityComparer.Default;
}
公共布尔等于(列表x、列表y)
{
返回x.SetEquals(y,值比较器);
}
public int GetHashCode(列表对象)
{
抛出新的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);
}
public static bool SetEquals(此IEnumerable first,IEnumerable second,
IEqualityComparer(比较器)
{
返回新的HashSet(第二个,comparer??EqualityComparer.Default)
.SetEquals(第一);
}
现在我们可以简单地写下:

new DictionaryComparer<string, List<int>>(new ListComparer<int>())
    .Equals(dict1, dict2);
newdictionarycomparer(newlistcomparer())
.等于(第1条、第2条);

我认为
AreDictionariesEqual()
只需要另一种方法来比较列表

因此,如果条目顺序无关紧要,您可以尝试以下方法:

  static bool ListEquals(List<int> L1, List<int> L2)
{
    if (L1.Count != L2.Count)
        return false;

    return L1.Except(L2).Count() == 0;
}            
    /*
    if it is ok to change List content you may try
    L1.Sort();
    L2.Sort();
    return L1.SequenceEqual(L2);
    */


static bool DictEquals(Dictionary<string, List<int>> D1, Dictionary<string, List<int>> D2)
{
    if (D1.Count != D2.Count)
        return false;

    return D1.Keys.All(k => D2.ContainsKey(k) && ListEquals(D1[k],D2[k]));

}
static bool DictEqualsOrderM(Dictionary<string, List<int>> D1, Dictionary<string, List<int>> D2)
{
    if (D1.Count != D2.Count)
        return false;

    //check keys for equality, than lists.           
    return (D1.Keys.SequenceEqual(D2.Keys) && D1.Keys.All(k => D1[k].SequenceEqual(D2[k])));         
}
static bool listquals(列表L1、列表L2)
{
if(L1.Count!=L2.Count)
返回false;
返回L1.Exception(L2).Count()==0;
}            
/*
如果可以更改列表内容,您可以尝试
L1.Sort();
L2.Sort();
返回L1.SequenceEqual(L2);
*/
静态布尔DictEquals(字典D1、字典D2)
{
如果(D1.Count!=D2.Count)
返回false;
返回D1.Keys.All(k=>D2.ContainsKey(k)和&listquals(D1[k],D2[k]);
}
如果条目顺序很重要,请尝试以下方法:

  static bool ListEquals(List<int> L1, List<int> L2)
{
    if (L1.Count != L2.Count)
        return false;

    return L1.Except(L2).Count() == 0;
}            
    /*
    if it is ok to change List content you may try
    L1.Sort();
    L2.Sort();
    return L1.SequenceEqual(L2);
    */


static bool DictEquals(Dictionary<string, List<int>> D1, Dictionary<string, List<int>> D2)
{
    if (D1.Count != D2.Count)
        return false;

    return D1.Keys.All(k => D2.ContainsKey(k) && ListEquals(D1[k],D2[k]));

}
static bool DictEqualsOrderM(Dictionary<string, List<int>> D1, Dictionary<string, List<int>> D2)
{
    if (D1.Count != D2.Count)
        return false;

    //check keys for equality, than lists.           
    return (D1.Keys.SequenceEqual(D2.Keys) && D1.Keys.All(k => D1[k].SequenceEqual(D2[k])));         
}
静态bool dictequalorderm(字典D1、字典D2)
{
如果(D1.Count!=D2.Count)
返回false;
//检查键是否相等,而不是列表。
返回(D1.Keys.SequenceEqual(D2.Keys)和&D1.Keys.All(k=>D1[k].SequenceEqual(D2[k]);
}

如果已知两个字典使用了等效的
IEqualityComparer
实现,并且其中一个希望将该实现视为等效的所有键视为等效,则它们包含相同数量的项,以及一个(任意选择)将另一个中的所有元素键映射到另一个中的相应值,除非或直到其中一个元素键被修改,否则它们将是等效的。测试这些条件比任何不假设两个词典使用相同的
IEqualityComparer
的方法都要快

如果两个词典没有使用相同的
IEqualityComparer
实现,则通常不应认为它们是等效的,无论它们包含哪些项。例如,带有区分大小写比较器的
字典和带有不区分大小写比较器的字典(两者都包含键值对(“Fred”、“Quimby”)是不等价的,因为后者会将“Fred”映射到“Quimby”,但前者不会


仅当字典使用相同的
IEqualityComparer
实现时,但如果人们对比字典使用的更细粒度的键相等定义感兴趣,并且键的副本没有与每个值一起存储,为了测试原始字典的平等性,有必要建立一个新的字典。最好推迟这一步,直到先前的测试表明字典似乎匹配为止。然后构建一个
字典
,将其中一个字典中的每个键映射到自身,然后在其中查找其他字典的所有键,以确保它们映射到匹配的内容。如果两个字典都使用不区分大小写的比较器,其中一个包含(“Fred”、“Quimby”)而另一个包含(“Fred”、“Quimby”),新的临时字典会将“Fred”映射到“Fred”,比较这两个字符串会发现字典不匹配。

我知道这个问题已经有了公认的答案,但我想提供一个更简单的选择:

using System.Linq;
using System.Collections.Generic;

namespace Foo
{
    public static class DictionaryExtensionMethods
    {
        public static bool ContentEquals<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, Dictionary<TKey, TValue> otherDictionary)
        {
            return (otherDictionary ?? new Dictionary<TKey, TValue>())
                .OrderBy(kvp => kvp.Key)
                .SequenceEqual((dictionary ?? new Dictionary<TKey, TValue>())
                                   .OrderBy(kvp => kvp.Key));
        }
    }
}
使用System.Linq;
使用System.Collections.Generic;
名称空间Foo
{
公共静态类DictionaryExtensionMethods
{
公共静态bool ContentEquals(此字典、字典或其他字典)
{
返回(其他字典??新字典())
.OrderBy(kvp=>kvp.Key)
.SequenceEqual((词典??新词典)
    public void AssertSameDictionary<TKey,TValue>(Dictionary<TKey,TValue> expected,Dictionary<TKey,TValue> actual)
    {
        string d1 = "expected";
        string d2 = "actual";
        Dictionary<TKey,TValue>.KeyCollection keys1= expected.Keys;
        Dictionary<TKey,TValue>.KeyCollection keys2= actual.Keys;
        if (actual.Keys.Count > expected.Keys.Count)
        {
            string tmp = d1;
            d1 = d2;
            d2 = tmp;
            Dictionary<TKey, TValue>.KeyCollection tmpkeys = keys1;
            keys1 = keys2;
            keys2 = tmpkeys;
        }

        foreach(TKey key in keys1)
        {
            Assert.IsTrue(keys2.Contains(key), $"key '{key}' of {d1} dict was not found in {d2}");
        }
        foreach (TKey key in expected.Keys)
        {
            //already ensured they both have the same keys
            Assert.AreEqual(expected[key], actual[key], $"for key '{key}'");
        }
    }
CollectionAssert.AreEqual(
   dict1.OrderBy(kv => kv.Key).ToList(),
   dict2.OrderBy(kv => kv.Key).ToList()
);
public static IDictionary<string, object> ToDictionary(this object source)
    {
        var fields = source.GetType().GetFields(
            BindingFlags.GetField |
            BindingFlags.Public |
            BindingFlags.Instance).ToDictionary
        (
            propInfo => propInfo.Name,
            propInfo => propInfo.GetValue(source) ?? string.Empty
        );

        var properties = source.GetType().GetProperties(
            BindingFlags.GetField |
            BindingFlags.GetProperty |
            BindingFlags.Public |
            BindingFlags.Instance).ToDictionary
        (
            propInfo => propInfo.Name,
            propInfo => propInfo.GetValue(source, null) ?? string.Empty
        );

        return fields.Concat(properties).ToDictionary(key => key.Key, value => value.Value); ;
    }
    public static bool EqualsByValue(this object source, object destination)
    {
        var firstDic = source.ToFlattenDictionary();
        var secondDic = destination.ToFlattenDictionary();
        if (firstDic.Count != secondDic.Count)
            return false;
        if (firstDic.Keys.Except(secondDic.Keys).Any())
            return false;
        if (secondDic.Keys.Except(firstDic.Keys).Any())
            return false;
        return firstDic.All(pair =>
          pair.Value.ToString().Equals(secondDic[pair.Key].ToString())
        );
    }
    public static bool IsAnonymousType(this object instance)
    {

        if (instance == null)
            return false;

        return instance.GetType().Namespace == null;
    }
    public static IDictionary<string, object> ToFlattenDictionary(this object source, string parentPropertyKey = null, IDictionary<string, object> parentPropertyValue = null)
    {
        var propsDic = parentPropertyValue ?? new Dictionary<string, object>();
        foreach (var item in source.ToDictionary())
        {
            var key = string.IsNullOrEmpty(parentPropertyKey) ? item.Key : $"{parentPropertyKey}.{item.Key}";
            if (item.Value.IsAnonymousType())
                return item.Value.ToFlattenDictionary(key, propsDic);
            else
                propsDic.Add(key, item.Value);
        }
        return propsDic;
    }
    static bool AreEqual(IDictionary<string, string> thisItems, IDictionary<string, string> otherItems)
    {
        if (thisItems.Count != otherItems.Count)
        {
            return false;
        }
        var thisKeys = thisItems.Keys;
        foreach (var key in thisKeys)
        {
            if (!(otherItems.TryGetValue(key, out var value) &&
                  string.Equals(thisItems[key], value, StringComparison.OrdinalIgnoreCase)))
            {
                return false;
            }
        }
        return true;
    }
internal class OrderInsensitiveListComparer<TValue>
    : IEqualityComparer<IEnumerable<TValue>>
{
    private readonly IComparer<TValue> comparer;

    public OrderInsensitiveListComparer(IComparer<TValue> comparer = null)
    {
        this.comparer = comparer ?? Comparer<TValue>.Default;
    }

    public bool Equals([AllowNull] IEnumerable<TValue> x, [AllowNull] IEnumerable<TValue> y)
    {
        return x != null
            && y != null
            && Enumerable.SequenceEqual(
                x.OrderBy(value => value, comparer),
                y.OrderBy(value => value, comparer));
    }

    public int GetHashCode([DisallowNull] IEnumerable<TValue> obj)
    {
        return obj.Aggregate(17, (hash, item) => hash * 23 ^ item.GetHashCode());
    }
}
internal class KeyValuePairComparer<TKey, TValue> : IEqualityComparer<KeyValuePair<TKey, TValue>>
{
    private readonly IEqualityComparer<TKey> key;

    private readonly IEqualityComparer<TValue> value;

    public KeyValuePairComparer(
        IEqualityComparer<TKey> key = null,
        IEqualityComparer<TValue> value = null)
    {
        this.key = key ?? EqualityComparer<TKey>.Default;
        this.value = value ?? EqualityComparer<TValue>.Default;
    }

    public bool Equals([AllowNull] KeyValuePair<TKey, TValue> x, [AllowNull] KeyValuePair<TKey, TValue> y)
    {
        // KeyValuePair is a struct, you can't null check
        return key.Equals(x.Key, y.Key) && value.Equals(x.Value, y.Value);
    }

    public int GetHashCode([DisallowNull] KeyValuePair<TKey, TValue> obj)
    {
        return 17 * 23 ^ obj.Key.GetHashCode() * 23 ^ obj.Value.GetHashCode();
    }
}
internal class DictionaryComparer<TValue> : IEqualityComparer<Dictionary<string, TValue>>
{
    private readonly IEqualityComparer<TValue> comparer;

    public DictionaryComparer(
        IEqualityComparer<TValue> comparer = null)
    {
        this.comparer = comparer ?? EqualityComparer<TValue>.Default;
    }

    public bool Equals([AllowNull] Dictionary<string, TValue> x, [AllowNull] Dictionary<string, TValue> y)
    {
        return x != null
            && y != null
            && Equals(x.Comparer, y.Comparer)
            && x.Comparer is StringComparer sorter
            && Enumerable.SequenceEqual(
                x.AsEnumerable().OrderBy(pair => pair.Key, sorter),
                y.AsEnumerable().OrderBy(pair => pair.Key, sorter),
                new KeyValuePairComparer<string, TValue>(x.Comparer, comparer));
    }

    public int GetHashCode([DisallowNull] Dictionary<string, TValue> obj)
    {
        return new OrderInsensitiveListComparer<KeyValuePair<string, TValue>>()
            .GetHashCode(obj.AsEnumerable()) * 23 ^ obj.Comparer.GetHashCode();
    }
}
    private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
    {
        return new DictionaryComparer<List<int>>(
            new OrderInsensitiveListComparer<int>())
                .Equals(dict1, dict2);
    }
    [TestMethod]
    public void DoesOrderValuesMatter()
    {
        Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>(StringComparer.CurrentCulture);
        // more stuff
    }