LINQ
Обзор методов и примеры применения
Фильтрация
.Where - определяет фильтр выборки
string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" }; var query = names.Where(name => name.EndsWith("y")); foreach(string d in query){project.SendInfoToLog(d, true);}
.Take - выбирает определенное количество элементов
string[] numbers = { "Один", "Два", "Три", "Четыре", "Пять" }; var query = numbers.Where(b => b.Contains("р")).OrderBy(b => b).Take(2); foreach(string d in query){project.SendInfoToLog(d, true);}
.Skip - пропускает определенное количество элементов
string[] numbers = { "Один", "Два", "Три", "Четыре", "Пять" }; var query = numbers.Where(b => b.Contains("р")).OrderBy(b => b).Skip(1); foreach(string d in query){project.SendInfoToLog(d, true);}
.TakeWhile - возвращает цепочку элементов последовательности, до тех пор, пока условие истинно
int[] numbers = { 3, 5, 2, 234, 4, 1 }; var takeWhileSmall = numbers.TakeWhile(n => n < 100); //{3,5,2} foreach(int d in takeWhileSmall){project.SendInfoToLog(d.ToString(), true);}
.SkipWhile - пропускает элементы в последовательности, пока они удовлетворяют заданному условию, и затем возвращает оставшиеся элементы
int[] numbers = { 3, 5, 2, 234, 4, 1 }; var skipWhileSmall = numbers.SkipWhile(n => n < 100) ; // { 234, 4 , 1 } foreach(int d in skipWhileSmall){project.SendInfoToLog(d.ToString(), true);}
.Distinct - удаляет из последовательности повторяющиеся элементы
Проецирование
.Select - определяет что добавить в выборку
//Объединение элемента с его индексом string[] names = { «Tom», «Dick», «Harry», «Mary», «Jay» }; var query = names.Select ((s, i) => i + "=" + s); //{ "0=Tom", "1=Dick", …)
.SelectMany - проецирует элементы (другой тип) и делает плоской вложенную иерархию элементов
//Разбить элементы string[] fullNames = { "Anne Williams", "John Fred Smith", "Sue Green" }; var query = fullNames.SelectMany(name => name.Split()); foreach(string name in query) project.SendInfoToLog(name + "|", true); //AnneIWilliamsIJohn|Fred|Smith|Sue|Green|
//Перекрёстное соединение списков int [] numbers ={1,2,3}; string[] letters = { "a", "b" }; var query = from n in numbers from l in letters select n.ToString() + l; foreach(string str in query){project.SendInfoToLog(str, true);}//{ "1a", "1b", "2a", "2b", "3a", "3b" }
//Сопоставление элементов друг с другом string[] players = { "Tom", "Jay", "Mary" }; var query = from name1 in players from name2 in players where name1.CompareTo(name2) < 0 orderby name1, name2 select name1 + " vs " + name2; foreach(string str in query){project.SendInfoToLog(str, true);} //{ "Jay vs Mary", "Jay vs Tom", "Mary vs Tom" }
Соединение
.Join (по совпадающим индексам)
.GroupJoin - выполняет одновременно соединение коллекций и группировку элементов по ключу
.Zip - объединяет две коллекции в соответствии с определенным условием
//совмещение последовательностей int[] numbers = { 3, 5, 7 }; string[] words = { "three", "five", "seven", "ignored" }; var zip = numbers.Zip(words, (n, w) => n + "=" + w); //3=three, 5=five...
Упорядочение
.OrderBy - упорядочивает элементы по возрастанию
.ThenBy - делает сортировку по дополнительному критерию (или нескольким) после выполнения операции OrderBy.
//сортировка в алфавитном порядке var query = names.OrderBy(s => s) ;
//сортировка по длине var query = names.OrderBy(s => s.Length);
//одновременно по размеру и в алфавитном порядке var query = names.OrderBy(s => s.Length).ThenBy (s => s);
//сортировка сначала по длине, затем по второму символу и по первому символу var names.OrderBy(s => s.Length).ThenBy(s => s[l]).ThenBy(s => s[0]);
//сортировка без чувствительности к регистру var stroka = list.OrderBy(n => n, StringComparer.CurrentCultureIgnoreCase);
.OrderByDescending и .ThenByDescending сортируют в убывающем порядке.
.Reverse возвращает последовательность в обратном порядке.
Группирование
.ToLookup - группирует элементы по ключу, при этом все элементы добавляются в словарь.
.GroupBy - Группирует последовательность в подпоследовательности. Позволяет группировать элементы по заданным критериям и выполнять над группами дополнительные операции, а так же группировать по одному или нескольким параметрам.
//получить элементы файлов по расширениям в группах string[] files = Directory.GetFiles (@"D:\Trading"); var query = files.GroupBy(file => Path.GetExtension (file)); foreach(var str in query){project.SendInfoToLog("Extension: " + str.Key, true); foreach(var s in str){ project.SendInfoToLog(" --" + s, true); } }
Операции над множествами
.Concat - возвращает все элементы из первой последовательности, за которыми следуют все элементы из второй последовательности.
.Union - делает то же самое, но удаляет любые дубликаты.
int [] seql = {1,2,3}, seq2 = { 3, 4, 5 }; //var concat = seql.Concat(seq2); // { 1, 2, 3, 3, 4, 5 } var union = seql.Union(seq2); // { 1, 2, 3, 4, 5 } foreach(int s in union){project.SendInfoToLog(s.ToString(), true);}
.Intersect - возвращает элементы, имеющиеся в двух последовательностях.
.Except - возвращает элементы из первой последовательности, которые не присутствуют во второй последовательности.
int [] seql = {1,2,3}, seq2 = { 3, 4, 5 }; var concat = seql.Except(seq2); // { 1, 2 } //var union = seql.Intersect(seq2); // { 3 } foreach(int s in concat){project.SendInfoToLog(s.ToString(), true);}
Методы преобразования
Операции над элементами
LastOrDefault - выбирает последний элемент коллекции или возвращает значение по умолчанию
.Single - требует точно одного совпадающего элемента
FirstOrDefault - выбирает первый элемент коллекции или возвращает значение по умолчанию
SingleOrDefault - выбирает первый элемент коллекции или возвращает значение по умолчанию
int[] numbers = {1, 2, 3, 4, 5}; int first = numbers.First(); // 1 int last = numbers.Last() ; // 5 int firstEven = numbers.First(n => n % 2 == 0); // 2 (первое чётное) int lastEven = numbers.Last(n => n % 2 == 0); // 4 (последнее чётное) int firstBigError = numbers.First(n => n > 10); // Генерируется исключение int firstBigNumber = numbers.FirstOrDefault(n => n > 10); // 0 int onlyDivBy3 = numbers.Single(n => n % 3 == 0) ; //3 int divBy2Err = numbers.Single(n => n % 2 == 0); // Ошибка: совпадение дают 2 и 4 int singleError = numbers.Single(n => n > 10); // Ошибка int noMatches = numbers.SingleOrDefault(n => n > 10); // 0 int divBy2Error = numbers.SingleOrDefault(n => n % 2 == 0); // Ошибка
.ElementAt - берёт указанный элемент по индексу
.ElementAtOrDefault - выбирает элемент коллекции по определенному индексу или возвращает значение по умолчанию, если индекс вне допустимого диапазона
int[] numbers ={1, 2, 3, 4, 5}; int third = numbers.ElementAt(2); // 3 int tenthError = numbers.ElementAt(9); // Генерируется исключение int tenth = numbers.ElementAtOrDefault(9); // 0
Методы агрегирования
.Count - возвращает кол-во элементов
.Min и .Max - возвращают наименьший или наибольший элемент из последовательности.
int[] numbers = { 28, 32, 14 }; int smallest = numbers.Min(); // 14; int largest = numbers.Max(); // 32; Если указано выражение селектора, то каждый элемент сначала проецируется: int smallest = numbers.Max(n => n % 10); // 8;
Sum и Average представляют собой операции агрегирования, которые применяются подобно Min и Мах:
decimal [] numbers = { 3, 4, 8 }; decimal sumTotal = numbers.Sum(); // 15 decimal average = numbers.Average(); //5 (среднее значение)
//подсчитать общую длину всех строк в массиве int combinedLength = names.Sum(s => s.Length); // 19
.Aggregate - позволяет указывать специальный алгоритм накопления для реализации необычных агрегаций.
//0 - начальное значение, с которого стартует накопление int [ ] numbers ={1,2,3}; int x = numbers.Aggregate(0, (prod, n) => prod * n) ; // 0*1*2*3 = 0 int у = numbers.Aggregate( (prod, n) => prod * n) ; // 1*2*3 = 6
.Contains - возвращает true, если заданный элемент присутствует в последовательности.
bool hasAThree = new int[] { 2, 3, 4 }.Contains(3); // true
.Any - возвращает true, если указанное выражение дает значение true хотя бы для одного элемента.
bool hasAThree = new int[] { 2, 3, 4 }.Any(n => n == 3); // true
Операция Any может делать все, что делает Contains, и даже больше:
bool hasABigNumber = new int[] { 2, 3, 4 }.Any(n => n > 10); // false
Вызов метода Any без предиката приводит к возвращению true, если последовательность содержит один или большее число элементов.
bool hasABigNumber = new int[] { 2, 3, 4 }.Where(n => n > 10).Any();
.All - определяет, все ли элементы коллекции удовлетворяют определенному условию и возвращает true, если все элементы удовлетворяют предикату.
dataContext.Customers.Where(с => с.Purchases.All(р => р.Price < 100));
.SequenceEqual - сравнивает две последовательности. Для возвращения true обе последовательности должны иметь идентичные элементы, расположенные в одинаковом порядке.
Методы генерации
.Empty используется для создания пустой последовательности значений. Им можно создать пустой массив или список.
.Range - создаёт элементы в диапазоне
foreach (int i in Enumerable.Range (5, 3)) Console.Write(i + " "); //567
.Repeat принимает элемент, подлежащий повторению, и количество повторений.
foreach (bool х in Enumerable.Repeat(true, 3)) Console.Write (x + " "); // True True True
Несколько своих примеров решения задач:
//Вывести все числа от 10 до 50, разделённые запятыми string numbers = string.Join(",", Enumerable.Range(10, 41)); project.SendInfoToLog(numbers, true);
//Вывести только те числа от 10 до 50, которые можно разделить на 3 string numbers = string.Join(", ", Enumerable.Range(10, 41).Where(n => n % 3 == 0)); project.SendInfoToLog(numbers, true);
//Вывести слово "Linq" 10 раз string numbers = string.Join(" ", Enumerable.Repeat("Linq", 10)); project.SendInfoToLog(numbers, true);
//Вывести все слова с буквой "a" var query = "aaa;abb;ccc;dap"; var str = query.Split(';').Where(x => x.Contains("a")); foreach(string d in str){project.SendInfoToLog(d, true);}
//Вывести количество букв 'a' в словах с этой буквой через запятую var query = "aaa;abb;ccc;dap"; string numbers = string.Join(",", query.Split(';').Select(x => x.Count(c => c == 'a'))); project.SendInfoToLog(numbers, true);
//Вывести true, если слово "abb" существует, иначе false var query = "aaa;xabbx;abb;ccc;dap"; var str = (query.Split(';').Select(x => x).Any(c => c == "abb")).ToString(); project.SendInfoToLog(str, true);
//Вывести самое длинное слово в строке var query = "aaa;xabbx;abb;ccc;dap"; var str = query.Split(';').OrderByDescending(z => z.Length).First(); project.SendInfoToLog(str, true);
//Вычислить среднюю длину слов в строке var query = "aaa;xabbx;abb;ccc;dap"; var str = query.Split(';').Average(x => x.Length); project.SendInfoToLog(str.ToString(), true);
//Вывести самое короткое слово в реверсе var query = "aaa;xabbx;abb;ccc;dap;zh"; var str = query.Split(';').OrderBy(n => n.Length).Select(x => new string(x.Reverse().ToArray())).First(); project.SendInfoToLog(str, true);
//Вывести true, если в первом слове, начинающемся с "aa", все буквы - "b", иначе false var query = "baaa;aabb;aaa;xabbx;abb;ccc;dap;zh"; var str = query.Split(';').Where(n => n.StartsWith("aa")).Where(n => n.Substring(2).All(ch => ch == 'b')).Any(); project.SendInfoToLog(str.ToString(), true);