Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Базовые технологии платформы .NET.docx
Скачиваний:
11
Добавлен:
18.08.2019
Размер:
423.24 Кб
Скачать

3. Операторы упорядочивания.

Операторы OrderBy() и OrderByDescending() выполняют сортировку коллекции по возрастанию или убыванию соответственно. Имеется версия данных операторов, принимающая в качестве дополнительного аргумента объект, реализующий IComparer<T>.

IOrderedEnumerable<T> OrderBy<T, K>(this IEnumerable<T> source,

Func<T, K> keySelector);

IOrderedEnumerable<T> OrderByDescending<T, K>(

this IEnumerable<T> source, Func<T, K> keySelector);

Интерфейс IOrderedEnumerable<T> является наследником IEnumerable<T> и описывает упорядоченную последовательность элементов с указанием на ключ сортировки. Если после выполнения сортировки по одному ключу требуется дополнительная сортировка по другому ключу, нужно воспользоваться операторами ThenBy() и ThenByDescending(). Имеется также оператор Reverse(), обращающий коллекцию.

Пример использования операторов упорядочивания:

var r1 = Enumerable.Reverse(gr);

var r2 = gr.OrderBy(student => student.Age);

var r3 = gr.OrderByDescending(student => student.Age)

.ThenBy(student => student.Name);

Чтобы получить коллекцию r1, метод расширения использовался как обычный статический метод класса, так как у List<T> имеется собственный метод Reverse(). В коллекции r3 студенты упорядочены по убыванию возраста, а при совпадении возрастов – по фамилиям в алфавитном порядке.

4. Оператор группировки GroupBy().

Оператор группировки GroupBy() разбивает коллекцию на группы элементов с одинаковым значением некоторого ключа. Оператор GroupBy() имеет перегруженные версии, позволяющие указать селектор ключа, преобразователь элементов в группе, объект, реализующий IEqualityComparer<T> для сравнения ключей. Простейшая версия оператора группировки имеет следующий вид:

IEnumerable<IGrouping<K, T>> GroupBy<T, K>(this IEnumerable<T> src,

Func<T, K> keySelector);

Здесь последний параметр указывает на функцию, которая строит по элементу поле-ключ. Обычно эта функция просто выбирает одно из полей объекта. Интерфейс IGrouping<K, T> унаследован от IEnumerable<T> и содержит дополнительное типизированное свойство Key – ключ группировки.

Рассмотрим применение оператора группировки. Сгруппируем студентов по возрасту и для каждой группы возрастов выведем ключ и элементы:

var r1 = gr.GroupBy(student => student.Age);

foreach (IGrouping<int, Student> group in r1)

{

Console.WriteLine(group.Key);

foreach (Student student in group)

{

Console.WriteLine(student.Name);

}

}

5. Операторы соединения.

Операторы соединения применяются, когда требуется соединить две коллекции, элементы которых имеют общие атрибуты. Основным оператором соединения является оператор Join().

IEnumerable<V> Join<T, U, K, V>(this IEnumerable<T> outer,

IEnumerable<U> inner,

Func<T, K> outerKeySelector,

Func<U, K> innerKeySelector,

Func<T, U, V> resultSelector);

Оператор Join() требует задания двух коллекций (внешней и внутренней) и трёх функций. Первая функция порождает ключ из элемента внешней коллекции, вторая – из элемента внутренней коллекции, а третья функция продуцирует объект коллекции-результата. При выполнении соединения Join() итерируется по внешней коллекции и ищет соответствия с элементами внутренней коллекции. При этом возможны следующие ситуации:

1. Найдено одно соответствие – в результат включается один элемент.

2. Найдено множественное соответствие – результат содержит по элементу для каждого соответствия.

3. Соответствий не найдено – элемент не входит в результат.

Рассмотрим примеры использования оператора Join(). Для этого опишем коллекцию cit объектов класса Citizen.

public class Citizen

{

public int BirthYear { get; set; }

public string IDNumber { get; set; }

}

int year = DateTime.Now.Year;

var cit = new List<Citizen> {

new Citizen {BirthYear = year - 17, IDNumber = "KLM897"},

new Citizen {BirthYear = year - 18, IDNumber = "WEF442"},

new Citizen {BirthYear = year - 18, IDNumber = "HHH888"},

new Citizen {BirthYear = year - 25, IDNumber = "XYZ012"}};

Выполним оператор Join():

var r1 = gr.Join(cit,

student => year - student.Age,

citizen => citizen.BirthYear,

(student, citizen) => new {student.Name, citizen.IDNumber});

// r1 содержит следующие объекты:

// {Name = "Smirnov", IDNumber = "WEF442"}

// {Name = "Smirnov", IDNumber = "HHH888"}

// {Name = "Kuznetsov", IDNumber = "WEF442"}

// {Name = "Kuznetsov", IDNumber = "HHH888"}

Оператор GroupJoin() порождает набор, группируя элементы внутренней коллекции при нахождении соответствия с элементом внешней коллекции: Если же соответствие не найдено, в результат включается пустая группа.

IEnumerable<V> GroupJoin()<T, U, K, V>(this IEnumerable<T> outer,

IEnumerable<U> inner,

Func<T, K> outerKeySelector,

Func<U, K> innerKeySelector,

Func<T, IEnumerable<U>, V> resultSelector);

Ниже приведён пример использования GroupJoin().

var r3 = cit.GroupJoin(gr,

citizen => citizen.BirthYear,

student => year - student.Age,

(citizen, group) => new {citizen.IDNumber,

Names = group.Select(st => st.Name)});

foreach (var data in r3)

{

Console.WriteLine(data.IDNumber);

foreach (string s in data.Names)

{ // 1-я и 4-я группы пусты,

Console.WriteLine(s); // 2-я и 3-я содержат по два элемента

}

}

Оператор Zip() порождает набор на основе двух исходных коллекций, выполняя заданное генерирование элементов. Длина результирующей коллекции равна длине меньшей из двух исходных коллекций.

IEnumerable<V> Zip<T, U, V>(this IEnumerable<T> first,

IEnumerable<U> second,

Func<T, U, V> resultSelector);

Дадим простейший пример использования Zip():

int[] numbers = {1, 2, 3, 4};

string[] words = {"one", "two", "three"};

var res = numbers.Zip(words, (f, s) => f + "=" + s); // 3 элемента