This article is currently in the process of being translated into Chinese (~90% done).
Sorting data: the OrderBy() & ThenBy() methods
现在,我们通过之前的文章了解了如何使用 LINQ 来从数据源中获取数据并使用 Where() 方法对数据进行筛选,接下来所了解的就是对数据进行排序。这里,我们已经使用了对象列表,无论对象是数字还是基于某个类的对象比如 User 类,我们得到最终结果的顺序与他们添加到列表时的顺序完全相同。但是,如同之前所讨论的,LINQ 所操作的数据源可能是 XML 文档或者是数据库。因此,一旦我们获得了所需数据,就应该正确地对数据进行排序,这一点非常重要。幸运的是,LINQ 有若干种易于使用的数据排序方法。我们先来看一个例子:
List<int> numbers = new List<int>()
{
1, 7, 2, 61, 14
};
List<int> sortedNumbers = numbers.OrderBy(number => number).ToList();
foreach (int number in sortedNumbers)
Console.WriteLine(number);
这很简单是不是?只需要调用 OrderBy() 方法就可以提供按对象或者对象内的成员排序,最终返回一个有序列表。当然,你可以按照字符串来进行排序。下面这个例子展示的是按照字符串排序,但以降序方式(从最大的 Z 到最小的 A)来进行排序:
List<string> cityNames = new List<string>()
{
"Amsterdam", "Berlin", "London", "New York"
};
List<string> sortedCityNames = cityNames.OrderByDescending(city => city).ToList();
foreach (string cityName in sortedCityNames)
Console.WriteLine(cityName);
在这个例子中,除了使用 OrderByDescending() 方法来替换原本的 OrderBy() 方法之外,我们执行了与之前完全相同的处理流程。当然,你可以很轻易地处理你地整数列表和字符串列表,这就是小菜一碟!然而,多亏有 LINQ,对于更加复杂的对象进行排序也是非常简单的,下面就是另一个例子:
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqOrder2
{
class Program
{
static void Main(string[] args)
{
List<User> listOfUsers = new List<User>()
{
new User() { Name = "John Doe", Mail = "john@doe.com", Age = 42 },
new User() { Name = "Jane Doe", Mail = "jane@doe.com", Age = 34 },
new User() { Name = "Joe Doe", Mail = "joe@doe.com", Age = 8 },
new User() { Name = "Another Doe", Mail = "another@doe.com", Age = 15 },
};
List<User> usersByAge = listOfUsers.OrderBy(user => user.Age).ToList();
foreach (User user in usersByAge)
Console.WriteLine(user.Name + ": " + user.Age + " years");
}
class User
{
public string Name { get; set; }
public string Mail { get; set; }
public int Age { get; set; }
}
}
}
这个例子比之前更加复杂点,其中,定义了 User 类并初始化了 User 列表,但你如你所见,真正排序的代码依旧短小精悍:当调用 OrderBy() 方法时,我们仍旧只需提供一个参数名并使用该参数访问 User 对象的年龄属性即可。最终,其结果完全按照年龄进行排序。但是如果我们想对多个属性进行排序该怎么处理呢?
ThenBy() 方法和 ThenByDescending() 方法
在上面这个例子中,我们将用户列表按照年龄排序,但是如果列表中存在多个相同年龄的用户呢?这是一个非常常见的场景,即使在我们的小例子中也是存在,比如 Jane 和 John 的年龄相同,而他们的孩子是一对双胞胎。在这种情况下,即使按照年龄对数据源排序后,我们也可以按照某种方式来控制排序的流程。为此,我们需要使用 ThenBy() 方法和 ThenByDescending() 方法。这些方法就正如他们名字那样:在初始排序完成之后控制排序流程。我们可以通过这些来得到这样一个用户列表:首先按照用户年龄排序,其次按照字母顺序排序。
List<User> listOfUsers = new List<User>()
{
new User() { Name = "John Doe", Mail = "john@doe.com", Age = 42 },
new User() { Name = "Jane Doe", Mail = "jane@doe.com", Age = 42 },
new User() { Name = "Joe Doe", Mail = "joe@doe.com", Age = 8 },
new User() { Name = "Jenna Doe", Mail = "another@doe.com", Age = 8 },
};
List<User> sortedUsers = listOfUsers.OrderBy(user => user.Age).ThenBy(user => user.Name).ToList();
foreach (User user in sortedUsers)
Console.WriteLine(user.Name + ": " + user.Age + " years");
看!十分地简单但却相当有效果。如果你的数据比我们给出的例子更加复杂,你甚至可以使用链式多个 ThenBy() 方法调用。当然了,你也可以以任何方式混合和匹配 OrderBy(),OrderByDescending(),ThenBy() 和 ThenByDescending() 方法。
List<User> sortedUsers = listOfUsers.OrderBy(user => user.Age).ThenByDescending(user => user.Name).ToList();
foreach (User user in sortedUsers)
Console.WriteLine(user.Name + ": " + user.Age + " years");
虽然在本教程中,我们大部分篇幅在使用 LINQ 的方法语法,但如之前一样,我也会给出本文某个例子的查询语法写法,以此来展示查询语法的样子。下面是最后一个例子,包含一个 LINQ 查询语法的版本。
// Method syntax
List<User> sortedUsers = listOfUsers.OrderBy(user => user.Age).ThenByDescending(user => user.Name).ToList();
// Query syntax
List<User> sortedUsersQ = (from user in listOfUsers orderby user.Age ascending, user.Name descending select user).ToList();
如你所见,二者在语法上略有不同。排序的方式(升序还是降序)是直接在待排序字段后指定(默认是升序,可以不写,但是我还是写出来来展现二者不同)。此外,这里没有 ”ThenBy“ 这个词。相反,你只需要用逗号分隔多个排序指令。当然了,最后,二者所返回的结果都是一样的。
概述
通过使用 OrderBy() 方法 和 ThenBy() 方法以及他们对应的降序版本,你可以很轻易地将数据按照你所期望的方式进行排序。注意,就像其他 LINQ 方法那样,原始数据并不会进行修改,相反,你会得到原始数据的排序副本,然后使用该副本。