【发布时间】:2010-06-24 22:58:00
【问题描述】:
我有两个收藏:
List<int> ids;
List<User> users;
User 有 id、name 等。
我想内部加入这两个集合并返回一个新的List<int>,其中第一个集合中的 id 也在第二个集合中(用户 ID)。
我是 LINQ 新手,不知道从哪里开始。
谢谢。
【问题讨论】:
我有两个收藏:
List<int> ids;
List<User> users;
User 有 id、name 等。
我想内部加入这两个集合并返回一个新的List<int>,其中第一个集合中的 id 也在第二个集合中(用户 ID)。
我是 LINQ 新手,不知道从哪里开始。
谢谢。
【问题讨论】:
您不需要使用 join 来执行此操作:
List<int> commonIds = ids.Intersect(users.Select(u => u.Id)).ToList();
编辑:针对 cmets 中的问题,您可以在不使用 Join 的情况下获取用户列表:
var matchingUsers = users.Where(u => ids.Contains(u.Id));
然而,这是非常低效的,因为Where 子句必须扫描每个用户的 id 列表。我认为 Join 将是处理这种情况的最佳方式:
List<User> matchingUsers = users.Join(ids, u => u.Id, id => id, (user, id) => user).ToList();
【讨论】:
List<long> commonIds = ids.Intersect<long>( users.Select<User, long>(u => (long)u.Id) ).ToList<long>(); 工作正常
复制自Microsoft docs:
在关系数据库术语中,内连接会生成一个结果集,其中第一个集合的每个元素对于第二个集合中的每个匹配元素都出现一次。如果第一个集合中的元素没有匹配的元素,则它不会出现在结果集中。 C#中join子句调用的Join方法实现了内连接。
本主题向您展示如何执行内连接的四种变体:
一个简单的内部联接,用于关联来自两个数据源的元素 基于一个简单的键。
一种内部联接,用于关联来自两个数据源的元素,基于 一个复合键。复合键,它是一个键,由 多个值,使您能够基于更多关联元素 不止一个属性。
一个多重连接,其中连续的连接操作被附加到 彼此。
使用组连接实现的内连接。
示例 简单的键连接示例
以下示例创建两个集合,其中包含两种用户定义类型的对象,即 Person 和 Pet。该查询使用 C# 中的 join 子句将 Person 对象与所有者为该 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 为“Huff”的 Person 对象不会出现在结果集中,因为不存在 Pet.Owner 等于该 Person 的 Pet 对象。 例子 复合键连接示例
您可以使用复合键来比较基于多个属性的元素,而不是仅基于一个属性来关联元素。为此,请为每个集合指定键选择器函数,以返回包含要比较的属性的匿名类型。如果您标记属性,它们在每个键的匿名类型中必须具有相同的标签。属性也必须以相同的顺序出现。
以下示例使用 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# 中的第一个连接子句基于匹配 Cat.Owner 的 Person 对象来匹配人和猫。它返回一系列匿名类型,其中包含 Person 对象和 Cat.Name。
C# 中的第二个连接子句将第一个连接返回的匿名类型与提供的狗列表中的 Dog 对象相关联,基于由 Person 类型的 Owner 属性和动物的第一个字母组成的复合键姓名。它返回一系列匿名类型,其中包含来自每个匹配对的 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方法中添加一行代码调用 你粘贴的方法。
运行程序。
【讨论】: