Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net LINQ - Filter a dictionary using another dictionary

Tags:

c#

.net

linq

I have two dictionaries of the same type, A and B.

Dictionary<string, IEnumerable<object>>

I'm using object to represent a complex type having a property 'Id'.

I'm looking for all items in A having objects that exist in B (using Id), but under a different key. It's basically to tell if an object has moved keys. A is the new dictionary and B is the old.

Is there a reasonable way to accomplish this using LINQ? I would like the result to be a dictionary of all key-value pairs in A meeting the criteria. Thanks in advance.

like image 281
tmaurst Avatar asked Sep 19 '25 19:09

tmaurst


2 Answers

In terms of searchability, your dictionary has it backwards; it is efficient for looking up an object given a string, but you need to be able to look up the strings for a given object. An efficient data structure for this purpose would be a Lookup<object,string>.

First, use ToLookup() to create a lookup table where the key is the object and the value is the list of keys in both list A and B. Use Union (instead of Concat) to eliminate duplicates.

var lookup = listA
    .Union( listB )
    .ToLookup( pair => pair.Value, pair => pair.Key );

Once you have the lookup, the problem is trivial.

var results = lookup.Where( x => x.Count() > 1);

See this DotNetFiddle for a working example with sample data.

like image 138
John Wu Avatar answered Sep 22 '25 04:09

John Wu


I use Interface IHasId for use Id propert:

public interface IHasId
{
    int Id { get; }
}

And class AAA that inherited the interface:

public class AAA: IHasId
{
    public int Id { get; set; }
}

Here the linq you look for:

Dictionary<string, IEnumerable<IHasId>> A = new Dictionary<string, IEnumerable<IHasId>>();
A.Add("111", new List<IHasId> { new AAA { Id = 1 }, new AAA { Id = 2 } });
A.Add("333", new List<IHasId> { new AAA { Id = 3 } });
Dictionary<string, IEnumerable<IHasId>> B = new Dictionary<string, IEnumerable<IHasId>>();
B.Add("111", new List<IHasId> { new AAA { Id = 1 }});
B.Add("222", new List<IHasId> { new AAA { Id = 2 }});
B.Add("333", new List<IHasId> { new AAA { Id = 3 } });

var res = A.Where(a => a.Value.Any(c => B.Any(v => v.Value
           .Select(x => x.Id).Contains(c.Id) && a.Key != v.Key))).ToList();

In this example it return key 111 that has the object with Id = 2 that moved from key 222 to key 111

If you want the result as dictionary you can change the ToList with ToDictionary:

var res = A.Where(a => a.Value.Any(c => B.Any(v => v.Value
           .Select(x => x.Id).Contains(c.Id) && a.Key != v.Key)))
           .ToDictionary(a=>a.Key, a=>a.Value);

If you want in the new dictionary only the values that has change, like in the example key 111 and value with only the object with Id = 2, you can do it like this:

var res = A.Select(a => new KeyValuePair<string, IEnumerable<IHasId>>(a.Key, 
           a.Value.Where(c => B.Any(v => v.Value.Select(x => x.Id).Contains(c.Id) && a.Key != v.Key))))
           .Where(a=>a.Value.Count() > 0)
           .ToDictionary(a => a.Key, a => a.Value);
like image 31
s-s Avatar answered Sep 22 '25 04:09

s-s