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.
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.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With