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.