» Решение одной задачи
10 Февраль 2012 – 4:21Расскажу о некотором моем решении, которое я применил для одной задачи. Решалась она при помощи C# (.NET 4.0). Хотелось сделать не просто «чтобы работало», а как-то в стиле C# что ли. Адекватного решения сходу на просторах сети найти не смог, пришлось самому мозговать. Итак, суть задачи:
Есть некий класс, у которого одно из полей — коллекция строк. Пусть это будет класс Person, а имя поля — NickNames. Далее имеется коллекция, объекты которой — Person. Назовем ее «persons». Требуется найти все элементы в этой коллекции, у которых в поле NickNames присутствуют 1 или более значений. Чтобы все стало понятнее, давайте рассмотрим пример и листинги кода.
Для начала опишем класс Person. Коллекция строк в данном случае — это поле NickNames. Выглядит все это следующим образом:
public class Person { public List<string> NickNames { get; set; } }
У нас есть некие персоны, у которых есть никнеймы. Создадим пару штук для примера:
List n1 = new List() { "Zer0", "Black", "Kn1ght" }; Person p1 = new Person() { NickNames = n1 }; List n2 = new List() { "Troll", "ZyHel", "Zer0" }; Person p2 = new Person() { NickNames = n2 }; List n3 = new List() { "Denchik", "Hunter", "GrordonD" }; Person p3 = new Person() { NickNames = n3 }; List n4 = new List() { "Hunter", "Moroz", "Zer0" }; Person p4 = new Person() { NickNames = n4 };
Теперь же создадим коллекцию, объектами которой и будут описанные выше «персоны»:
List persons = new List() { p1, p2, p3, p4 };
Как я писал выше, надо найти все персоны, у которых NickNames может быть, к примеру, «Zer0″ или «Zer0″ и «Hunter». Сделать это можно, пробежавшись по каждому элементу коллекции при помощи какого-нибудь foreach и с поиском совпадений нужных никнеймов с никнеймами текущей персоны, опять же при помощи какого-нибудь цикла. Подобное решение мне не понравилось. Мало того, что куча кода, так еще и несколько циклов. Ну и ко всему этому — мое желание написать это при помощи LINQ, изучение которого я только начал на момент решения задачи. Следовательно передо мною стояла задача поиска подмассива в массиве. Один хороший человек напомнил мне о такой вещи, как пересечение множеств. Зачем оно мне — будет описано ниже. А сейчас открываем любимый гугл, ищем что-нибудь про пересечение множеств на C# и находим LINQ метод Enumerable.Intersect. То, что нужно! Изучаем, что этот метод может и как его применять и пробуем применить его на практике в нашей задаче:
List nicksForSearch1 = new List() { "Zer0" }; List nicksForSearch2 = new List() { "Zer0", "Hunter" }; IEnumerable result = persons.Where(p => p.NickNames.Intersect(nicksForSearch2, StringComparer.CurrentCultureIgnoreCase).Count() == nicksForSearch2.Count);
Рассмотрим, что тут происходит: как я выше говорил, нам требуется найти все персоны с определенными никнеймами. Мы будем искать то, что указанно в nicksForSearch2. Для поиска заюзаем LINQ запросы Where и Intersect. Если попробовать перевести данный участок кода на русский язык, то получится примерно так:
«Получить из всех persons те персоны (p), у которых никнеймы (p.NickNames) содержат (Intersect) все перечисленные в nicksForSearch2 строки (Count() == nicksForSearch2.Count) независимо от регистра (CurrentCultureIgnoreCase)».
Метод Intersect — это пересечение множеств, а наши коллекции и есть по сути множества. При использовании данного метода нам возвращается коллекция совпавших элементов. Если ее размер равняется размеру всех запрашиваемых элементов (в нашем случае это nicksForSearch2) — значит все элементы nicksForSearch2 присутствуют у объекта XXX. Следовательно мы нашли нашего пассажира. Вот и вся хитрость.
Данное решение, возможно, не идеально, но вполне может кому-нибудь пригодится. Не могу ничего сказать насчет его производительности, данный вопрос меня не особо заботил — кому надо, проведет бенчмарки сам. А приведенный мною пример лишь помог получше узнать, что вообще можно делать при помощи LINQ. Оказывается — много чего
Удачи Вам и легких решений любых задач.