Reputation: 691
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.
Upvotes: 4
Views: 1381
Reputation: 79
Use the Join operator (see join clause (C# Reference)):
var dictionary = (
from a in (from entry in A from Value in entry.Value select new { entry.Key, Value })
join b in (from entry in B from Value in entry.Value select new { entry.Key, Value })
on ((dynamic)a.Value).Id equals ((dynamic)b.Value).Id
where a.Key != b.Key
select a
).ToDictionary(a => a.Key, a => a.Value);
Upvotes: 0
Reputation: 52210
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.
Upvotes: 3
Reputation: 380
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);
Upvotes: 2
Reputation: 2576
If you need A entries with original objects, it could be:
var result = A.Where(a => B.Any(b => b.Key != a.Key && b.Value.Intersect(a.Value).Any()));
If you need A entries with only matching objects from B, it could be:
var result = A.Select(a => new KeyValuePair<string, IEnumerable<object>>(a.Key, B.Where(b => b.Key != a.Key).SelectMany(b => b.Value.Intersect(a.Value)))).Where(x => x.Value.Any());
You can provide a custom equality comparer for Intersect
to match items by Id or whatever.
Use new Dictionary<string, IEnumerable<object>>(result)
if you need it as a dictionary.
Upvotes: 1