views:

77

answers:

3

I want to compare the contents of two Dictionary<string, string> instances regardless of the order of the items they contain. SequenceEquals also compares the order, so I first order the dictionaries by key and then call SequenceEquals.

Is there a method that I can use instead of SequenceEquals that will only compare the contents?

If there isn't, is this the ideal way to do this?

Dictionary<string, string> source = new Dictionary<string, string>();
Dictionary<string, string> target = new Dictionary<string, string>();

source["foo"] = "bar";
source["baz"] = "zed";
source["blah"] = null;

target["baz"] = "zed";
target["blah"] = null;
target["foo"] = "bar";

// sequenceEquals will be false
var sequenceEqual = source.SequenceEqual(target);
// contentsEqual will be true
var contentsEqual = source.OrderBy(x => x.Key).SequenceEqual(target.OrderBy(x => x.Key));
+2  A: 
public static bool DictionaryEqual<TKey, TValue>(
    this IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second)
{
    if (first == second) return true;
    if ((first == null) || (second == null)) return false;
    if (first.Count != second.Count) return false;

    var comparer = EqualityComparer<TValue>.Default;

    foreach (KeyValuePair<TKey, TValue> kvp in first)
    {
        TValue secondValue;
        if (!second.TryGetValue(kvp.Key, out secondValue)) return false;
        if (!comparer.Equals(kvp.Value, secondValue)) return false;
    }
    return true;
}
LukeH
By having unordered dictionaries, your algorithm runs in O(n^2).
Yuriy Faktorovich
@Yuriy: How is it O(n^2), assuming that the hashcodes are half-decent?
LukeH
@LukeH Assuming GetHash was implemented.
Yuriy Faktorovich
I'll concede, I didn't know GetHashCode implementation on Object returned real hash codes.
Yuriy Faktorovich
@Yuriy: If you're using dictionaries then you *either* assume that the hashcodes are half-decent, *or* you make sure that they are. A key lookup in a dictionary is assumed to be roughly O(1). If it's not then you're going to see poor performance all-round, not just in my method.
LukeH
+2  A: 

I don't know if there is an existing method but you could use the following (null checking of args omitted for brevity)

public static bool DictionaryEquals<TKey,TValue>(
  this Dictionary<TKey,TValue> left,
  Dictionary<TKey,TValue> right ) { 

  var comp = EqualityComparer<TValue>.Default;
  if ( left.Count != right.Count ) { 
    return false;
  }
  foreach ( var pair in left ) {
    TValue value;
    if ( !right.TryGetValue(pair.Key, out value) 
         || comp.Equals(pair.Value, value) ) {
      return false;
    }
  } 
  return true;
}

It would be best to add an overload to allow customization of the EqualityComparer<TValue>.

JaredPar
A: 

This will check if all Values from source exists in target, ignoring the Keys

var result = source.All(x => target.Any(y => x.Value == y.Value));
BrunoLM
This misses the case where `target` has additional key value pairs not in `source`
JaredPar
@JaredPar: If `target` has additional pairs then what should happen? Return `false` instead of `true`? Then an additional check for length would fix it right?
BrunoLM
@BrunoLM I would say if the contents are different then they are not equal. A length check would fix the `target` is bigger but not the issue of keys being different.
JaredPar
@JaredPar: I assume the OP want to compare just the contents. `[...] that will only compare the contents?`
BrunoLM