Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make value comparison between two Dictionary<string, List<string>> objects

I have the following data structure:

Dictionary<string, List<string>>

how can i make comparison to make sure that the values are equal between two different objects?

i.e:

    Dictionary<string, List<string>> expected = new Dictionary<string, List<string>>();
    expected.Add("CREDIT", new List<string> { "K   R   EH   D   IH   T" });
    expected.Add("CARD", new List<string> { "K   AA   R   D" });

    Dictionary<string, List<string>> actual;
    actual = target.GetTermDictionary();
    if (!Enumerable.SequenceEqual(expected, actual))
    {
        Assert.Fail();
    }

I don't think that SequanceEqual is good here..

Thanks

like image 864
user829174 Avatar asked Dec 14 '11 09:12

user829174


People also ask

How to compare 2 list of dictionary in Python?

3. Python sort() method and == operator to compare lists. We can club the Python sort() method with the == operator to compare two lists. Python sort() method is used to sort the input lists with a purpose that if the two input lists are equal, then the elements would reside at the same index positions.

How to compare 2 String in c#?

Equals() method is a method of String class. This method takes two strings to be compared as parameters. It returns a logical value, true or false with the help of which we can determine whether the given strings are the same or not.

How do I check if two dictionaries are equal in C#?

Equals(Object) Method which is inherited from the Object class is used to check if a specified Dictionary object is equal to another Dictionary object or not. Syntax: public virtual bool Equals (object obj); Here, obj is the object which is to be compared with the current object.

Can we compare two dictionaries in C#?

It is possible to compare two Dictionary instances for equality using a custom method with key-value pair checking.


2 Answers

First shortcut on quick trues and falses:

if(ReferenceEqual(actual, expected))
  return true;
if(actual == null || expected == null || actual.Count != expected.Count)
  return false;

This also handles null-checking so everthing else we do can't throw a null reference exception. You can skip all of this bar comparing the counts if you have it just after the creation as in your example, but should keep it in if you put this in a separate method, just in case.

We can't just call SequenceEqual on the two dictionaries, because we aren't guaranteed to get the keys back in the same order. With other types for the value we could do:

return actual.OrderBy(kvp => kvp.Key).SequenceEqual(expected.OrderBy(kvp => kvp.Key));

But this won't work because the two sequence-equal List<string> values won't be considered equal to the DefaultEqualityComparer<List<string>>.Equals() method that this will call into.

We could create an IEqualityComparer<KeyValuePair<string, List<string>>> if we were hell-bound on using SequenceEqual, but it's probably simpler to do the non-Linq approach, even though Linq is normally simpler and more concise (once you find the way to do it. Hence:

List<string> expectedVal;
foreach(KeyValuePair<string, List<string> kvp in actual)
{
  if(!expected.TryGetValue(kvp.key, out expectedVal) || kvp.Value.Count != expectedVal.Count || !kvp.Value.SequenceEquals(expectedVal))
    return false;

}
return true;

Variants can deal with different views of equality. For example, we may use kvp.Value.OrderBy(x => x).SequenceEquals(expectedVal.OrderBy(x => x)) if we wanted to consider two lists of the same items in different orders as equal.

In summary, the lot together:

if(ReferenceEqual(actual, expected))
  return true;
if(actual == null || expected == null || actual.Count != expected.Count)
  return false;
List<string> expectedVal;
foreach(KeyValuePair<string, List<string> kvp in actual)
{
  if(!expected.TryGetValue(kvp.key, out expectedVal) || kvp.Value.Count != expectedVal.Count || !kvp.Value.SequenceEquals(expectedVal))
    return false;

}
return true;

Edit: Just for fun, the way that uses SequenceEquals:

internal class KvpSLSEq : IEqualityComparer<KeyValuePair<string, List<string>>>
{
  public bool Equals(KeyValuePair<string, List<string>> x, KeyValuePair<string, List<string>> y)
  {
    return x.Key == y.Key && x.Value.Count == y.Value.Count && x.Value.SequenceEquals(y.Value);
  }
  public int GetHashCode(KeyValuePair<string, List<string>> obj)
  {
    //you could just throw NotImplementedException unless you'll reuse this elsewhere.
    int hash = obj.Key.GetHashCode;
    foreach(string val in obj.Value)
       hash = hash * 31 + (val == null ? 0 : val.GetHashCode());
  }
}

This done we can use the concise:

actual.OrderBy(kvp => kvp.Key).SequenceEqual(expected.OrderBy(kvp => kvp.Key), new KvpSLSEq());

But it's only really concise if KvpSLSEq will be used elsewhere as well.

like image 152
Jon Hanna Avatar answered Oct 13 '22 02:10

Jon Hanna


I don't think there's built in method, but you can compare the list values within each dictionary entry. Something like this:

// Check actual doesn't contain excess keys
if (actual.Keys.Count != expected.Keys.Count)
{
    return false;
}

foreach(var key in expected.Keys)
{
    if (!actual.ContainsKey(key) || !actual[key].SequenceEqual(expected[key]))
    {
       return false;
    }
}

return true;

Take a look here: Comparing 2 Dictionary<string, string> Instances and here: Is there a built-in method to compare collections in C#?

like image 25
Amittai Shapira Avatar answered Oct 13 '22 02:10

Amittai Shapira