This article is currently in the process of being translated into Russian (~98% 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 и проинициализированным списком пользователей, но, как можно видеть, непосредственно сортировка всё равно остаётся столь же простой и чёткой: когда вызывается OrderBy(), мы всё так же вставляем имя параметра и затем используем его, чтобы получить доступ к свойству Age класса 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();
Как вы можете видеть, синтаксис немного другой: направление сортировки (ascending или descending) указывается сразу после поля, по которому указана сортировка (сортировка по возрастанию ascending является сортировкой по умолчанию, но в примере указана, чтобы просто показать разницу). Также, в запросе нет "ThenBy", вместо инструкции сортировки просто указаны через запятую. Конечно, в конечном счёте, оба запроса дают одинаковый результат.
Резюме
Используя методы OrderBy() и ThenBy() (также, как их аналоги, сортирующий по убыванию), вы можете легко получить ваши данные, отсортированный необходимым вам образом. И помните, также как в любом другом LINQ-методе, исходные данные не изменяются, вместо этого вы получаете отсортированную копию исходного источника данных, с которым работаете.