visc
visc

Reputation: 4959

How do I compare two dictionaries and create a new one in one direction

I have two dictionaries of information, one dic comes from a database and another dic comes from local devices.

I have a method that compares what's on the local device to what's in the database. If there is something on the device thats not in the database I want to add it to the database. I don't care if there is something on the database thats not on the device.

Here is a method I wrote to handle the job:

private static PackageHistory GetPkgChangeList(PackageHistory devicePackageHistory, PackageHistory databasePackageHistory)
{
    var changeList = new PackageHistory();

    foreach (var devicePkg in devicePackageHistory.Keys)
    {
        // do we have a database entry for this package
        var databaseEntryList = new List<Tuple<string, DateTime>>();
        if (databasePackageHistory.TryGetValue(devicePkg, out databaseEntryList))
        {
            // compare entries, add missing to list
            foreach (var deviceEntry in devicePackageHistory[devicePkg])
            {
                // TODO: Not sure if the equality is done automatically
                if (!databaseEntryList.Contains(deviceEntry))
                {
                    var changeListEntries = new List<Tuple<string, DateTime>>();
                    if (changeList.TryGetValue(devicePkg, out changeListEntries))
                    {
                        changeListEntries.Add(new Tuple<string, DateTime>(deviceEntry.Item1, deviceEntry.Item2));
                    }
                    else
                    {
                        changeList.Add(devicePkg, new List<Tuple<string, DateTime>> { new Tuple<string, DateTime>(deviceEntry.Item1, deviceEntry.Item2)});
                    }
                }
            }
        }
        else
        {
            // add missing package and its history to change list
            changeList.Add(devicePkg, devicePackageHistory[devicePkg].ConvertAll(entry => new Tuple<string, DateTime>(entry.Item1, entry.Item2)));
        }
    }

    return changeList;
}

Here is the PackageHistory class:

protected class PackageHistory : Dictionary<string, List<Tuple<string, DateTime>>>
{
}

What's happening is I'm going through all the items in the devicePackageHistory dictionary and comparing them with the items in the databasePackageHistory.

If I find an item not in the databasePackageHistory dictionary I add it to the changeList dictionary. This changeList is returned at the end of the function and sent to another method for further processing.

I know the GetPkgChangeList algorithm looks complex and thats why I'm inquiring if there is an easier way to compare two dictionaries with the types I have and create a new dictionary with those changes (the items in the device dictionary not the database dictionary).

I'm working with legacy code so I don't have much wiggle room. How would you go about this?

EDIT: Basically I'm updating my database and collecting this information.

Upvotes: 0

Views: 103

Answers (4)

Raj Karri
Raj Karri

Reputation: 551

If you just need to update things back to database which are not in, Both dictionaries can be merged using this simple statement. Here dictOne is dictionary items from db and dictTwo is dictionary items from device.

var dict = dictOne.Concat(dictTwo).GroupBy(d => d.Key).ToDictionary(d => d.Key, d => d.First().Value);

This statement of code need to be changed a bit for your requirement considering tuples and other stuff.

Upvotes: 0

M. Buga
M. Buga

Reputation: 494

If I correctly understood your code. You can use something like this:

var changeList = new PackageHistory(devicePackageHistory
    .SelectMany(x => x.Value.Select(y => new Tuple<string, Tuple<string, DateTime>>(x.Key, y)))
    .Except(databasePackageHistory.SelectMany(x => x.Value.Select(y => new Tuple<string, Tuple<string, DateTime>>(x.Key, y))))
    .GroupBy(x => x.Item1)
    .ToDictionary(x => x.Key, x => x.Select(y => y.Item2).ToList()));

You will need additional constructor if resulting change set must be of type PackageHistory:

public PackageHistory(IDictionary<string, List<Tuple<string, DateTime>>> source) : base(source)
{
}

Explanation: Both dictionaries are transformed to enumerations of comparable items of type Tuple<string,Tuple<string,DateTime>>. Then items that exist (all tuple items match) in the database are excluded from device enumeration. Resulting items are then grouped back to dictionary by old dictionary key.

Edit: Alternative code without Linq:

var changeList = new PackageHistory(devicePackageHistory);

foreach (var databasePkgKvp in databasePackageHistory)
{
    var changeListEntries = new List<Tuple<string, DateTime>>();
    if (changeList.TryGetValue(databasePkgKvp.Key, out changeListEntries))
    {
        foreach (var databaseEntry in databasePkgKvp.Value)
        {
            changeListEntries.Remove(databaseEntry);
        }
        if (changeListEntries.Count == 0)
        {
            changeList.Remove(databasePkgKvp.Key);
        }
    }
}

Upvotes: 0

Olivier De Meulder
Olivier De Meulder

Reputation: 2501

You could achieve this using linq.

I put together a little example.

void Main()
{
    Dictionary<string, int> primaryDict = new Dictionary<string, int>
    {
        {"key1", 33}, 
        {"key2", 24}, 
        {"key3", 21}, 
        {"key4", 17}, 
        {"key5", 12}
    };

    Dictionary<string, int> secondaryDict = new Dictionary<string, int>
    {
        {"key1", 22}, 
        {"key3", 20}, 
        {"key4", 19}, 
        {"key7", 17}, 
        {"key8", 10}
    };

    var resultDict =  primaryDict.Where(x => !secondaryDict.ContainsKey(x.Key))
                     .ToDictionary(x => x.Key, x => x.Value);

    Console.WriteLine(resultDict);
}

This will return all the elements in the primaryDict that are not in secondaryDict.

Upvotes: 1

Gary McLean Hall
Gary McLean Hall

Reputation: 994

Assuming you can use Linq:

(This is untested, apologies in advance if this doesn't work exactly as written).

private static PackageHistory GetPkgChangeList(PackageHistory devicePackageHistory, PackageHistory databasePackageHistory)
{
    return devicePackageHistory
        .Except(databasePackageHistory, devicePackageHistory.Comparer)
}

The other assumption is that the determining factor for equality between items on the device and in the database is the dictionary key.

Also, slightly unrelated, a Dictionary<string, List<Tuple<string, DateTime>>> looks like it could be simplified to Dictionary<string, Dictionary<string, DateTime>>. A dictionary is basically a list of tuples.

Upvotes: 0

Related Questions