LINQ内连接

时间:2010-06-24 22:58:00

标签: c# .net linq

我有两个系列:

List<int> ids;
List<User> users;

User有id,name等等。

我想内连接这两个集合并返回一个新的List<int>,其中包含第一个集合中的ID,这些ID也位于第二个集合中(用户ID)。

我是LINQ的新手,不知道从哪里开始。

感谢。

2 个答案:

答案 0 :(得分:10)

您无需使用join来执行此操作:

List<int> commonIds = ids.Intersect(users.Select(u => u.Id)).ToList();

编辑:在回复评论中的问题时,您可以在不使用Join的情况下获得用户列表:

var matchingUsers = users.Where(u => ids.Contains(u.Id));

然而,这是非常低效的,因为Where子句必须扫描每个用户的id列表。我认为加入将是处理这种情况的最佳方式:

List<User> matchingUsers = users.Join(ids, u => u.Id, id => id, (user, id) => user).ToList();

答案 1 :(得分:5)

在关系数据库术语中,内部联接生成一个结果集,其中第一个集合的每个元素对第二个集合中的每个匹配元素都出现一次。如果第一个集合中的元素没有匹配的元素,则它不会出现在结果集中。 Join方法由C#中的join子句调用,它实现了一个内连接。

本主题介绍如何执行内部联接的四种变体:

  • 一个简单的内连接,用于关联两个数据源中的元素 基于一个简单的密钥。

  • 内部联接,它基于关联来自两个数据源的元素 复合键。复合键,是由键组成的键 多个值,使您可以基于更多关联元素 比一个财产。

  • 附加连续连接操作的多重连接 彼此。

  • 使用群组加入实现的内部联接。

实施例 简单的键连接示例

以下示例创建两个集合,其中包含两个用户定义类型Person和Pet的对象。该查询使用C#中的join子句将Person对象与其Owner为Person的Pet对象进行匹配。 C#中的select子句定义了生成的对象的外观。在此示例中,生成的对象是匿名类型,由所有者的名字和宠物的名称组成。 C#

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

/// <summary>
/// Simple inner join.
/// </summary>
public static void InnerJoinExample()
{
    Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
    Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
    Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
    Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
    Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };

    Pet barley = new Pet { Name = "Barley", Owner = terry };
    Pet boots = new Pet { Name = "Boots", Owner = terry };
    Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
    Pet bluemoon = new Pet { Name = "Blue Moon", Owner = rui };
    Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

    // Create two lists.
    List<Person> people = new List<Person> { magnus, terry, charlotte, arlene, rui };
    List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

    // Create a collection of person-pet pairs. Each element in the collection
    // is an anonymous type containing both the person's name and their pet's name.
    var query = from person in people
                join pet in pets on person equals pet.Owner
                select new { OwnerName = person.FirstName, PetName = pet.Name };

    foreach (var ownerAndPet in query)
    {
        Console.WriteLine("\"{0}\" is owned by {1}", ownerAndPet.PetName, ownerAndPet.OwnerName);
    }
}

// This code produces the following output:
//
// "Daisy" is owned by Magnus
// "Barley" is owned by Terry
// "Boots" is owned by Terry
// "Whiskers" is owned by Charlotte
// "Blue Moon" is owned by Rui

请注意,LastName为&#34; Huff&#34;的Person对象不会出现在结果集中,因为没有Pet对象的Pet.Owner等于该Person。 例 复合键加入示例

您可以使用复合键基于多个属性比较元素,而不是仅基于一个属性关联元素。为此,请为每个集合指定键选择器函数,以返回包含要比较的属性的匿名类型。如果标记属性,则每个密钥的匿名类型必须具有相同的标签。属性也必须以相同的顺序出现。

以下示例使用Employee对象列表和Student对象列表来确定哪些员工也是学生。这两种类型都具有String类型的FirstName和LastName属性。从每个列表的元素创建连接键的函数返回一个匿名类型,该类型由每个元素的FirstName和LastName属性组成。连接操作将这些复合键进行相等性比较,并返回每个列表中的对象对,其中第一个名称和姓氏都匹配。 C#

class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeID { get; set; }
}

class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int StudentID { get; set; }
}

/// <summary>
/// Performs a join operation using a composite key.
/// </summary>
public static void CompositeKeyJoinExample()
{
    // Create a list of employees.
    List<Employee> employees = new List<Employee> {
        new Employee { FirstName = "Terry", LastName = "Adams", EmployeeID = 522459 },
         new Employee { FirstName = "Charlotte", LastName = "Weiss", EmployeeID = 204467 },
         new Employee { FirstName = "Magnus", LastName = "Hedland", EmployeeID = 866200 },
         new Employee { FirstName = "Vernette", LastName = "Price", EmployeeID = 437139 } };

    // Create a list of students.
    List<Student> students = new List<Student> {
        new Student { FirstName = "Vernette", LastName = "Price", StudentID = 9562 },
        new Student { FirstName = "Terry", LastName = "Earls", StudentID = 9870 },
        new Student { FirstName = "Terry", LastName = "Adams", StudentID = 9913 } };

    // Join the two data sources based on a composite key consisting of first and last name,
    // to determine which employees are also students.
    IEnumerable<string> query = from employee in employees
                                join student in students
                                on new { employee.FirstName, employee.LastName }
                                equals new { student.FirstName, student.LastName }
                                select employee.FirstName + " " + employee.LastName;

    Console.WriteLine("The following people are both employees and students:");
    foreach (string name in query)
        Console.WriteLine(name);
}

// This code produces the following output:
//
// The following people are both employees and students:
// Terry Adams
// Vernette Price

实施例 多个连接示例

可以将任意数量的连接操作相互附加以执行多个连接。 C#中的每个连接子句都将指定的数据源与先前连接的结果相关联。

以下示例创建三个集合:Person对象列表,Cat对象列表和Dog对象列表。

C#中的第一个join子句根据与Cat.Owner匹配的Person对象匹配人和猫。它返回一系列包含Person对象和Cat.Name的匿名类型。

C#中的第二个连接子句将第一次连接返回的匿名类型与提供的狗列表中的Dog对象相关联,这是基于一个复合键,它由Person类型的Owner属性和动物的第一个字母组成。 #39;的名字。它返回一系列匿名类型,其中包含每个匹配对的Cat.Name和Dog.Name属性。因为这是内部联接,所以只返回第一个数据源中第二个数据源中具有匹配项的对象。 C#

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

class Cat : Pet
{ }

class Dog : Pet
{ }

public static void MultipleJoinExample()
{
    Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
    Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
    Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
    Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
    Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };
    Person phyllis = new Person { FirstName = "Phyllis", LastName = "Harris" };

    Cat barley = new Cat { Name = "Barley", Owner = terry };
    Cat boots = new Cat { Name = "Boots", Owner = terry };
    Cat whiskers = new Cat { Name = "Whiskers", Owner = charlotte };
    Cat bluemoon = new Cat { Name = "Blue Moon", Owner = rui };
    Cat daisy = new Cat { Name = "Daisy", Owner = magnus };

    Dog fourwheeldrive = new Dog { Name = "Four Wheel Drive", Owner = phyllis };
    Dog duke = new Dog { Name = "Duke", Owner = magnus };
    Dog denim = new Dog { Name = "Denim", Owner = terry };
    Dog wiley = new Dog { Name = "Wiley", Owner = charlotte };
    Dog snoopy = new Dog { Name = "Snoopy", Owner = rui };
    Dog snickers = new Dog { Name = "Snickers", Owner = arlene };

    // Create three lists.
    List<Person> people =
        new List<Person> { magnus, terry, charlotte, arlene, rui, phyllis };
    List<Cat> cats =
        new List<Cat> { barley, boots, whiskers, bluemoon, daisy };
    List<Dog> dogs =
        new List<Dog> { fourwheeldrive, duke, denim, wiley, snoopy, snickers };

    // The first join matches Person and Cat.Owner from the list of people and
    // cats, based on a common Person. The second join matches dogs whose names start
    // with the same letter as the cats that have the same owner.
    var query = from person in people
                join cat in cats on person equals cat.Owner
                join dog in dogs on 
                new { Owner = person, Letter = cat.Name.Substring(0, 1) }
                equals new { dog.Owner, Letter = dog.Name.Substring(0, 1) }
                select new { CatName = cat.Name, DogName = dog.Name };

    foreach (var obj in query)
    {
        Console.WriteLine(
            "The cat \"{0}\" shares a house, and the first letter of their name, with \"{1}\".", 
            obj.CatName, obj.DogName);
    }
}

// This code produces the following output:
//
// The cat "Daisy" shares a house, and the first letter of their name, with "Duke".
// The cat "Whiskers" shares a house, and the first letter of their name, with "Wiley".

实施例 使用分组连接示例

进行内部连接

以下示例说明如何使用组连接实现内部联接。

在query1中,Person对象列表根据与Pet.Owner属性匹配的Person分组连接到Pet对象列表。组连接创建一个中间组的集合,其中每个组由一个Person对象和一系列匹配的Pet对象组成。

通过向查询添加第二个from子句,将这个序列序列组合(或展平)为一个更长的序列。最终序列的元素类型由select子句指定。在此示例中,该类型是一个匿名类型,由每个匹配对的Person.FirstName和Pet.Name属性组成。

query1的结果等同于使用join子句而不使用into子句来执行内连接所获得的结果集。 query2变量演示此等效查询。 C#

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

/// <summary>
/// Performs an inner join by using GroupJoin().
/// </summary>
public static void InnerGroupJoinExample()
{
    Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
    Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
    Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
    Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

    Pet barley = new Pet { Name = "Barley", Owner = terry };
    Pet boots = new Pet { Name = "Boots", Owner = terry };
    Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
    Pet bluemoon = new Pet { Name = "Blue Moon", Owner = terry };
    Pet daisy = new Pet { Name = "Daisy", Owner = magnus };

    // Create two lists.
    List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
    List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };

    var query1 = from person in people
                 join pet in pets on person equals pet.Owner into gj
                 from subpet in gj
                 select new { OwnerName = person.FirstName, PetName = subpet.Name };

    Console.WriteLine("Inner join using GroupJoin():");
    foreach (var v in query1)
    {
        Console.WriteLine("{0} - {1}", v.OwnerName, v.PetName);
    }

    var query2 = from person in people
                 join pet in pets on person equals pet.Owner
                 select new { OwnerName = person.FirstName, PetName = pet.Name };

    Console.WriteLine("\nThe equivalent operation using Join():");
    foreach (var v in query2)
        Console.WriteLine("{0} - {1}", v.OwnerName, v.PetName);
}

// This code produces the following output:
//
// Inner join using GroupJoin():
// Magnus - Daisy
// Terry - Barley
// Terry - Boots
// Terry - Blue Moon
// Charlotte - Whiskers
//
// The equivalent operation using Join():
// Magnus - Daisy
// Terry - Barley
// Terry - Boots
// Terry - Blue Moon
// Charlotte - Whiskers

编译代码

  • 在Visual Studio中创建一个新的控制台应用程序项目。

  • 如果尚未引用System.Core.dll,请添加对它的引用。

  • 包含System.Linq命名空间。

  • 将示例中的代码复制并粘贴到program.cs文件中, 在Main方法下面。在Main方法中添加一行代码进行调用 你粘贴的方法。

  • 运行程序。