» Решение одной задачи

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. Оказывается — много чего :) Удачи Вам и легких решений любых задач.

Оставить коммент

:mrgreen: :neutral: :twisted: :shock: :smile: :???: :cool: :grin: :oops: :razz: :roll: :wink: :cry: :lol: :sad: