Проверка наличия и получение элементов
Ряд методов в LINQ позволяют проверить наличие элементов в коллекции и получить их.
All
Метод All() проверяет, соответствуют ли все элементы условию. Если все элементы соответствуют условию, то возвращается true. Например:
string[] people = { "Tom", "Tim", "Bob", "Sam" };
// проверяем, все ли элементы имеют длину в 3 символа
bool allHas3Chars = people.All(s => s.Length == 3); // true
Console.WriteLine(allHas3Chars);
// проверяем, все ли строки начинаются на T
bool allStartsWithT = people.All(s => s.StartsWith("T")); // false
Console.WriteLine(allStartsWithT);
В первом случае проверяем, все ли строки в массиве people имеют длину в три символа. Поскольку это условие верно, то метод All возвращает true. Во втором случае смотрим, все ли строки начинаются с буквы "T". это условие ложно, поэтому метод All возвращает false.
Any
Метод Any() действует подобным образом, только возвращает true, если хотя бы один элемент коллекции определенному условию:
string[] people = { "Tom", "Tim", "Bob", "Sam" };
// проверяем, все ли элементы имеют длину больше 3 символов
bool allHasMore3Chars = people.Any(s => s.Length > 3); // false
Console.WriteLine(allHasMore3Chars);
// проверяем, все ли строки начинаются на T
bool allStartsWithT = people.Any(s => s.StartsWith("T")); // true
Console.WriteLine(allStartsWithT);
Первое выражение вернет false, поскольку все строки имеют длину в 3 символа. Второе выражение возвратит true, так как у нас есть в коллекции есть строки, которые начинаются на букву T.
Contains
Метод Contains() возвращает true, если коллекция содержит определенный элемент.
string[] people = { "Tom", "Tim", "Bob", "Sam" };
// проверяем, есть ли строка Tom
bool hasTom = people.Contains("Tom"); // true
Console.WriteLine(hasTom);
// проверяем, есть ли строка Mike
bool hasMike = people.Contains("Mike"); // false
Console.WriteLine(hasMike);
Стоит отметить, что для сравнения объектов применяется реализация метода Equals. Соответственно если мы работаем с объектами своих типов, то мы можем реализовать данный метод:
Person[] people = { new Person("Tom"), new Person("Sam"), new Person("Bob")};
var tom = new Person("Tom");
var mike = new Person("Mike");
// проверяем, есть ли Tom
bool hasTom = people.Contains(tom); // true
Console.WriteLine(hasTom);
// проверяем, есть ли строка Mike
bool hasMike = people.Contains(mike); // false
Console.WriteLine(hasMike);
class Person
{
public string Name { get;}
public Person(string name) => Name = name;
public override bool Equals(object? obj)
{
if (obj is Person person) return Name == person.Name;
return false;
}
public override int GetHashCode() => Name.GetHashCode();
}
Но стоит отметить, что Contains не всегда может вернуть ожидаемые данные. Например:
string[] people = { "tom", "Tim", "bOb", "Sam" };
// проверяем, есть ли строка Tom
bool hasTom = people.Contains("Tom"); // false
Console.WriteLine(hasTom);
// проверяем, есть ли строка Bob
bool hasMike = people.Contains("Bob"); // false
Console.WriteLine(hasMike);
В данном случае в массиве нет строки "Tom", а есть строка "tom". Поэтому вызов people.Contains("Tom") возвратит false. Но подобное поведение не всегда может быть желательным. И в этом случае мы можем задать логику сравнения с помощью реализации интерфейса IComparer и затем передать ее в качестве второго параметра в метод Contains:
using System.Diagnostics.CodeAnalysis;
string[] people = { "tom", "Tim", "bOb", "Sam" };
// проверяем, есть ли строка Tom
bool hasTom = people.Contains("Tom", new CustomStringComparer()); // true
Console.WriteLine(hasTom);
// проверяем, есть ли строка Bob
bool hasMike = people.Contains("Bob", new CustomStringComparer()); // true
Console.WriteLine(hasMike);
class CustomStringComparer : IEqualityComparer<string>
{
public bool Equals(string? x, string? y)
{
if (x is null || y is null) return false;
return x.ToLower() == y.ToLower();
}
public int GetHashCode(string obj) => obj.ToLower().GetHashCode();
}
Интерфейс IEqualityComparer типизируется типом сравниваемых данных (в данном случае типом String). Для реализации этого интерфейса необходимо определить методы Equals и GetHashCode. В методе Equals сравниваем строки в нижнем регистре, а в методе GetHashCode возвращаем возвращаем хеш-код строки в нижнем регистре.
First/FirstOrdefault
Метод First() возвращает первый элемент последовательности:
string[] people = { "Tom", "Bob", "Tim", "Sam" };
// проверяем, есть ли строка Tom
var first = people.First(); // Tom
Console.WriteLine(first);
Также в метод First можно передать метод, который представляет условие. В этом случае метод возвращает первый элемент, который соответствует условию:
string[] people = { "Tom", "Bob", "Kate", "Tim", "Mike", "Sam" };
// первая строка, длина которой равна 4 символам
var firstWith4Chars = people.First(s=> s.Length == 4); // Kate
Console.WriteLine(firstWith4Chars);
Здесь выбираем первый элемент, длина которого - 4 символа.
Стоит учитывать, что если коллекция пуста или в коллекции нет элементов, который соответствуют условию, то будет сгенерировано исключение.
string[] people = { "Tom", "Bob", "Kate", "Tim", "Mike", "Sam" };
// первая строка, длина которой равна 5 символам
var firstWith5Chars = people.First(s => s.Length == 5); // ! исключение
Console.WriteLine(firstWith5Chars);
var first = new string[] {}.First(); // ! исключение
Console.WriteLine(first);
FirstOrdefault
Метод FirstOrDefault() также возвращает первый элемент и также может принимать условие, только если коллекция пуста или в коллекции не окажется элементов, которые соответствуют условию, то метод возвращает значение по умолчанию:
string[] people = { "Tom", "Bob", "Kate", "Tim", "Mike", "Sam" };
// первый элемент
var first = people.FirstOrDefault(); // Tom
Console.WriteLine(first);
// первая строка, длина которой равна 4 символам
var firstWith4Chars = people.FirstOrDefault(s => s.Length == 4); // Kate
Console.WriteLine(firstWith4Chars);
// первый элемент из пустой коллекции
var firstOrDefault = new string[] {}.FirstOrDefault();
Console.WriteLine(firstOrDefault); // null
Стоит учитывать, что для коллекций ссылочных типов FirstOrDefault возвращает значение типа T? (в примере выше - string?), то есть значение, которое может быть равно null, а значение по умолчанию - null. Для коллекций числовых типов возвращается непосредственно значение типа T, а значение по умолчанию - 0.
Но мы можем настроить значение по умолчанию, передав его в качестве одного из аргументов:
string[] people = { "Tom", "Bob", "Kate", "Tim", "Mike", "Sam" };
string? firstWith5Chars = people.FirstOrDefault(s => s.Length == 5, "Undefined");
Console.WriteLine(firstWith5Chars); // Undefined
// первый элемент из пустой коллекции строк
string? firstOrDefault = new string[] {}.FirstOrDefault("hello"); // hello - значение по умолчанию
Console.WriteLine(firstOrDefault); // hello
// первый элемент из пустой коллекции int
int firstNumber = new int[] {}.FirstOrDefault(100); // 100 - значение по умолчанию
Console.WriteLine(firstNumber); // 100
Last и LastOrDefault
Метод Last() аналогичен по работе методу First, только возвращает последний элемент. Если коллекция не содержит элемент, который соответствуют условию, или вообще пуста, то метод генерирует исключение.
string[] people = { "Tom", "Bob", "Kate", "Tim", "Mike", "Sam" };
string last = people.Last();
Console.WriteLine(last); // Sam
string lastWith4Chars = people.Last(s => s.Length == 4);
Console.WriteLine(lastWith4Chars); // Mike
Метод LastOrDefault() возвращает последний элемент или значение по умолчанию, если коллекция не содержит элемент, который соответствуют условию, или вообще пуста:
string[] people = { "Tom", "Bob", "Kate", "Tim", "Mike", "Sam" };
string? last = people.LastOrDefault();
Console.WriteLine(last); // Sam
string? lastWith4Chars = people.LastOrDefault(s => s.Length == 4);
Console.WriteLine(lastWith4Chars); // Mike
string? lastWith5Chars = people.LastOrDefault(s => s.Length == 5);
Console.WriteLine(lastWith5Chars); // null
string? lastWith5CharsOrDefault = people.LastOrDefault(s => s.Length == 5, "Undefined");
Console.WriteLine(lastWith5CharsOrDefault); // Undefined
// первый элемент из пустой коллекции строк
string? lastOrDefault = new string[] {}.LastOrDefault("hello");
Console.WriteLine(lastOrDefault); // hello