1

I've got this, but it's so short I'm nearly sure I'm missing something:

public static bool ValueEquals<TKey, TValue>
    (this IDictionary<TKey, TValue> source, IDictionary<TKey, TValue> toCheck)
{
    if (object.ReferenceEquals(source, toCheck))
        return true;
    if (source == null || toCheck == null || source.Count != toCheck.Count)
        return false;
    return source.OrderBy(t => t.Key).SequenceEqual(toCheck.OrderBy(t => t.Key));
}

So basically, if they have an equal reference, return true. If either of them are null or their counts are different, return false. Then return if the sequences (ordered by their keys and then their values) are the same. I must be missing something, as it's far too short to be good enough.

It'sNotALie.
  • 22,289
  • 12
  • 68
  • 103
  • Sorry! I accidentally pressed enter! It's not finished yet! – It'sNotALie. Jun 05 '13 at 15:54
  • 2
    Just to clarify, you want the same values *for the same keys*? (Your question title only mentions values, which is different.) – Jon Skeet Jun 05 '13 at 15:56
  • @JonSkeet I meant values as in the general sense, not the `Dictionary` value sense. So if I had two dictionaries of `string` and `bool`, and they were `{ "true", true }, { "false", false }` and the other one was `{ "false", false }, { "true", true }`, they would be equal. Sorry for the ambiguity. – It'sNotALie. Jun 05 '13 at 15:58
  • So does your code work, or does it not work? If it does, then what's your problem? – Servy Jun 05 '13 at 15:59
  • Does `OrderBy(t => t.Key).ThenBy(t => t.Value)` have any meaning if we know in advance that each key is unique? – Rotem Jun 05 '13 at 16:00
  • @Rotem The `ThenBy` is useless, yes. The `OrderBy` part is important given the method he's using. – Servy Jun 05 '13 at 16:00
  • @Servy I think it does work, but I'm not sure. That's the question here, is there anything that would make this fail? – It'sNotALie. Jun 05 '13 at 16:01
  • @Rotem That's a point you've got there. Thanks! – It'sNotALie. Jun 05 '13 at 16:01
  • I think the only way where `true` will be equal to `"true"` is if your `TValue` is known as `string`. – Vitor Canova Jun 05 '13 at 16:01
  • 1
    @newStackExchangeInstance So did you try it out, run some different tests on various values, or did you just write the code and then ask this question? – Servy Jun 05 '13 at 16:02
  • @VitorCanova It was an example. I was comparing the two dictionaries :) – It'sNotALie. Jun 05 '13 at 16:02
  • @Servy I've tried it out a bit, but not enough to be sure. – It'sNotALie. Jun 05 '13 at 16:02
  • @newStackExchangeInstance But looks like you want to match different types too. – Vitor Canova Jun 05 '13 at 16:03
  • @VitorCanova Yes I do, that's why it's a generic method ;) – It'sNotALie. Jun 05 '13 at 16:04
  • How to you intend to compare decimal `1d` and string `"1"`? You expected to this compares result `true`? This will be a very long algorithm. :'( – Vitor Canova Jun 05 '13 at 16:07
  • 1
    @VitorCanova Should've gone with a different example. What I meant is: are two dictionaries the same, not taking into account order and references? – It'sNotALie. Jun 05 '13 at 16:08
  • 1
    Got it. You want something like this: `source.Values.Except(toCheck.Values);` and verify if returns a `0` elements; – Vitor Canova Jun 05 '13 at 16:13
  • @VitorCanova That wouldn't work, the OP's should. He want to make sure that each dictionary has exactly the same pairs, not just the same values. Your solution isn't checking the keys. – Servy Jun 05 '13 at 16:14
  • @Servy Sorry but I understood by the title that he wants compare the values only. – Vitor Canova Jun 05 '13 at 16:20
  • @VitorCanova And if you read past the title you'd see that he wants to ensure that both dictionarys have exactly the same keys, and the same values associated with each of those keys. Oh, and the code you showed there would falsely return true if `toCheck` had any values not in `source`. – Servy Jun 05 '13 at 16:24
  • @Servy Sorry but I was trapped by the question indeed. If I ask you when you are in your "Happy Birthday" and you asnwer with the year you was born you are not really "answer the quenstion". Maybe the OP could edit the title to clarify. The second comment did the same assertion I did. It is not completely clear. Don't take it personally, your aswer below was awesome. – Vitor Canova Jun 05 '13 at 16:38

1 Answers1

4

Yes, your code will work, so long as the keys all implement IComparable and both the keys and values have an Equals method that compares what you want it to compare. If either the keys or the values don't have appropriate implementations of those methods then this won't work.

Your method also doesn't provide functionality for custom IComparer or IEqualityComparer objects to be passed in to account for the cases where the object doesn't have a desirable implementation of one of those methods. Whether this is an issue in your particular case we can't say.

Your solution also needs to sort all of the values, which is somewhat less efficient than other possible implementations of a set equals, but it's not dramatically worse, so if you don't have particularly large collections that shouldn't be a huge issue.

A method with comparable functionality to yours but improved speed would be (keeping the first two checks you have):

return !source.Except(toCheck).Any();

Since this method doesn't rely on sorting it also provides the benefit of not needing TKey to implement IComparable.

A key reason that both this method and your method works is due to the fact that KeyValuePair overrides it's definition of Equals and GetHashCode to to be based on it's own reference, but rather on the key and value it wraps. Two KeyValuePairs are equal if both the keys and values are equal, and the hash code incorporates the hash code of both the key and the value.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Speed could be improved still further, I would think, if one enumerated through one dictionary, tried to read each value from the other, and ensured that a value was read and that it matched the value in the source. No need to build the intersection set. – supercat Jun 05 '13 at 17:37
  • @supercat That would make it faster in the case where they're different and slower in the case where they're the same, but I'll include both as you're right, it may be worth doing. – Servy Jun 05 '13 at 17:41
  • Why would validating one dictionary against the other be slower when they match? Isn't `Intersect` going to have to do a dictionary lookup for every item in `source`? – supercat Jun 05 '13 at 17:46
  • @supercat Yeah, you're right. I didn't realize at first that, since the counts are the same and they sets cannot have duplicate keys, it's not possible for `A.Except(B)` to be empty and `B.Except(A)` to be non-empty. Since it doesn't need the second except it wouldn't ever be slower, so I've just removed the Intersect as it's always as the same or worse. – Servy Jun 05 '13 at 18:02