使用LINQ获取两个IEnumebles的交集

时间:2013-02-09 01:50:41

标签: c# linq

我有两个IEnumerable类型的实例,如下所示。

IEnumerable<Type1> type1 = ...;
IEnumerable<Type2> type2 = ...;

Type1Type2都包含一个名为common的成员,所以即使它们属于不同的类,我们仍然可以像这样关联它们。

type1[0].common == type2[4].common

我试图过滤掉type1common中没有相应type2值的元素,并根据每个元素中的一个值创建字典。现在,我通过以下双循环来做到这一点。

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);

现在,我已尝试使用LINQ但.Where.Select.ForEach只是让我很头疼。有没有办法使用LINQ整齐地执行相同的操作?

5 个答案:

答案 0 :(得分:15)

当两个序列有共同点并且您希望根据该共性过滤其产品时,高效查询是一个连接。假设 Type1 Customer Type2 Order 。每个客户都有一个 CustomerID ,每个订单还有一个 CustomerID 。然后你可以这么说。

var query = from customer in customers
            join order in orders 
              on customer.CustomerId equals order.CustomerId
            select new { customer.Name, order.Product };

迭代将为您提供一系列配对,包括每个有订单的客户名称及其所有产品。因此,如果客户Suzy订购了煎饼和披萨,而顾客鲍勃订购了牛排,那么你就会得到这些牛排。

Suzy, pancake
Suzy, pizza
Bob, steak

如果您想要分组,以便每个客户都有他们的订单列表,那就是群组加入。

var query = from customer in customers
            join order in orders 
              on customer.CustomerId equals order.CustomerId 
              into products
            select new { customer.Name, products };

迭代,为您提供第一项为名称,第二项为产品序列的对。

Suzy, { pancake, pizza }
Bob, { steak }

答案 1 :(得分:1)

另一种选择是加入。我在下面做了一个快速的控制台应用程序,但不得不编写我自己的数据。希望我能正确理解你的问题。

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();
    }
}

显示如下:
第一:值1 第一名:value3
第二名:value1
第二名:value3

答案 2 :(得分:0)

假设您仍希望将交叉点保持为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);

答案 3 :(得分:-1)

type1.where(i=>type2.where(j=>j.common == i.common).Count > 0);

这应该只列出那些匹配的列表。

答案 4 :(得分:-1)

我错过了什么,但是会这样做:

type1
 .where(t1 => type2.Any(t2 => t1.common == t2.common)
 .ToDictionary(t1 => t1.Id)

或者像Servy建议的那样

type1
  .Join(type2, a => a.common, b => b.common, (a1,b1) => a1)
  .ToDictionary(t1 => t1.Id)