1

I have two dictionaries, one contains the original data and the other contains the new data. I want to compare the two dictionaries and return a dictionaries and return a third that contains updates.

Dictionary<int, Dictionary<string, string>> OriginalDictionary = new Dictionary
{
    {1, new Dictionary<string, string> 
        {{"name", "adam"}, 
         {"age", "15"}
         {"occupation", "student"}},
    {2, new Dictionary<string, string> 
        {{"name", "bob"}, 
         {"age", "40"}
         {"occupation", "doctor"}},
    {3, new Dictionary<string, string> 
        {{"name", "cameron"}, 
         {"age", "32"}
         {"occupation", "teacher"}},
}

Dictionary<int, Dictionary<string, string>> NewDictionary = new Dictionary
{
    {1, new Dictionary<string, string> 
        {{"name", "adam"}, 
         {"age", "15"}
         {"occupation", "student"}},
    {2, new Dictionary<string, string> 
        {{"name", "bob"}, 
         {"age", "40"}
         {"occupation", "lawyer"}}, //this is where it's different
    {3, new Dictionary<string, string> 
        {{"name", "cameron"}, 
         {"age", "32"}
         {"occupation", "teacher"}},
}

I want to get a third dictionary that contains the updates. It can be eithere the whole first level, or breaking down into the second level. The below 2 examples will both work for me.

Dictionary<int, Dictionary<string, string>> UpdateDictionary1 = new Dictionary
{
    {2, new Dictionary<string, string> 
        {{"name", "bob"}, 
         {"age", "40"}
         {"occupation", "lawyer"}} //this is where it's different
}

Dictionary<int, Dictionary<string, string>> UpdateDictionary2 = new Dictionary
{
    {2, new Dictionary<string, string> 
        {{"occupation", "lawyer"}}
}

I have tried the answers from this post How to compare two Dictionaries in C#, but the results I got for UpdateDictionary still contains all the data from NewDictionary. UpdatesDictionary.Count == 3, which my expected output should be UpdatesDictionary.Count == 1. I tried the Where answer and the Except answer and they both weren't working as I want it to be.

UpdateDictionary = OriginalDictionary.Where(entry => NewDictionary[entry.Key] != entry.Value).ToDictionary(entry => entry.Key, entry => entry.Value);

UpdateDictionary = OriginalDictionary.Except(NewDictionary).ToDictionary(x => x.Key, x => x.Value);

Is there some other way I should approach this?

Thanks!

Community
  • 1
  • 1
sora0419
  • 2,308
  • 9
  • 39
  • 58

1 Answers1

2

So first the easy part. Find the keys that were added or removed:

var addedKeys = NewDictionary.Keys.Except(OriginalDictionary.Keys);
var removedKeys = OriginalDictionary.Keys.Except(NewDictionary.Keys);

Next, to find the keys that had an edited dictionary we'll create an equality comparer for dictionaries, simply because trying to inline all of the possible ways that they can differ is going to be too much.

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

Now that we have this, calling it is pretty straightforward:

var addedKeys = NewDictionary.Keys.Except(OriginalDictionary.Keys);
var removedKeys = OriginalDictionary.Keys.Except(NewDictionary.Keys);
var comparer = new DictionaryComparer<string, string>();
var editedValues = OriginalDictionary.Where(pair =>
    !comparer.Equals(pair.Value, NewDictionary[pair.Key]));
Servy
  • 202,030
  • 26
  • 332
  • 449
  • Thank you for the response. The number of edited value still didn't come out right. I'm still getting way more updates than there should be :/ – sora0419 Feb 11 '14 at 16:19
  • @sora0419 Sorry, missed a NOT in there; it was getting all of the equal dictionaries. – Servy Feb 11 '14 at 16:22
  • I was still getting a larger number for count, so I tried to use Trim() to get rid of extra spaces that I wasn't aware of and finally got it working properly (at least for right now I dont see any issue). Thank you so much! – sora0419 Feb 11 '14 at 16:33
  • I believe the solution you suggested generates the output similar to UpdateDictionary1 right? Could you possibly help me with modifying it to it generates an output like UpdateDictionary2? I will need to count the number of each fields that were updated, so the version 2 will make a lot more sense. Sorry I didn't thought of this earlier – sora0419 Feb 11 '14 at 17:42
  • @sora0419 You'll need to create a new type that encapsulates all of the differences between two dictionaries, or at least what you care about those differences. Added keys, removed keys, changed keys, new/old values, etc. All of that is essentially computed in the `Equals` method here, you just need to stick those checks into that new type and return it, instead of just returning true/false. Return the removed keys, return the changed values. Then in the `Where` you'll add your `Any` to filter it down to those with any changes. Should be fairly straightforward, just a little tedious. – Servy Feb 11 '14 at 17:45