Reputation:
I have two instances of type IEnumerable
as follows.
IEnumerable<Type1> type1 = ...;
IEnumerable<Type2> type2 = ...;
Both Type1
and Type2
contain a member called common
, so even though they are of different class, we can still relate them like this.
type1[0].common == type2[4].common
I'm trying to filter away those elements of type1
that have no corresponding common
value in type2
and create a dictionary based on one value from each. Right now, I do that by the following double loop.
Dictionary<String, String> intersection = ...;
foreach (Type1 t1 in type1)
foreach(Type2 t2 in type2)
if (t1.common == t2.common)
intersection.Add(t1.Id, t2.Value);
Now, I've tried with LINQ but all the .Where
, .Select
and .ForEach
just gave me headache. Is there a way to neatly perform the same operation using LINQ?
Upvotes: 5
Views: 1959
Reputation: 660024
When two sequences have something in common and you want to filter their product based on that commonality, the efficient query is a join. Suppose Type1 is Customer and Type2 is Order. Every customer has a CustomerID, and every order also has a CustomerID. Then you can say this.
var query = from customer in customers
join order in orders
on customer.CustomerId equals order.CustomerId
select new { customer.Name, order.Product };
Iterating that will give you a sequence of pairs consisting of every customer name that has an order, and all of their products. So if customer Suzy ordered a pancake and a pizza and customer Bob ordered a steak, you'd get these pairs.
Suzy, pancake
Suzy, pizza
Bob, steak
If instead you want those grouped so that every customer has a list of their orders, that's a group join.
var query = from customer in customers
join order in orders
on customer.CustomerId equals order.CustomerId
into products
select new { customer.Name, products };
Iterating that gives you pairs where the first item is the name and the second item is a sequence of products.
Suzy, { pancake, pizza }
Bob, { steak }
Upvotes: 15
Reputation: 35477
I my be missing something, but would this do:
type1
.where(t1 => type2.Any(t2 => t1.common == t2.common)
.ToDictionary(t1 => t1.Id)
Or as Servy suggested
type1
.Join(type2, a => a.common, b => b.common, (a1,b1) => a1)
.ToDictionary(t1 => t1.Id)
Upvotes: -1
Reputation: 8095
Assuming that you still want to keep the intersection as a Dictionary<string, string>
:
IEnumerable<Type1> list1;
IEnumerable<Type2> list2;
Dictionary<string, string> intersection =
(from item1 in list1
from item2 in list2
where item1.common = item2.common
select new { Key = item1.Id, Value = item2.Value })
.ToDictionary(x => x.Key, x => x.Value);
Upvotes: 0
Reputation: 84
Another option would be joining. I did a quick console app below, but had to make up my own data. Hope I understood your question correctly.
public class Type1
{
public string ID { get; set; }
public Guid common { get; set; }
}
public class Type2
{
public string Value { get; set; }
public Guid common { get; set; }
}
class Program
{
static void Main(string[] args)
{
Guid CommonGuid = Guid.NewGuid();
IEnumerable<Type1> EnumType1 = new List<Type1>()
{
new Type1() {
ID = "first",
common = CommonGuid
},
new Type1() {
ID = "second",
common = CommonGuid
},
new Type1() {
ID = "third",
common = Guid.NewGuid()
}
} as IEnumerable<Type1>;
IEnumerable<Type2> EnumType2 = new List<Type2>()
{
new Type2() {
Value = "value1",
common = CommonGuid
},
new Type2() {
Value = "value2",
common = Guid.NewGuid()
},
new Type2() {
Value = "value3",
common = CommonGuid
}
} as IEnumerable<Type2>;
//--The part that matters
EnumType1 //--First IEnumerable
.Join( //--Command
EnumType2, //--Second IEnumerable
outer => outer.common, //--Key to join by from EnumType1
inner => inner.common, //--Key to join by from EnumType2
(inner, outer) => new { ID = inner.ID, Value = outer.Value }) //--What to do with matching "rows"
.ToList() //--Not necessary, just used so that I can use the foreach below
.ForEach(item =>
{
Console.WriteLine("{0}: {1}", item.ID, item.Value);
});
Console.ReadKey();
}
}
Displayed below:
first: value1
first: value3
second: value1
second: value3
Upvotes: 1
Reputation: 3415
type1.where(i=>type2.where(j=>j.common == i.common).Count > 0);
This should get you a list of only those that match.
Upvotes: -1