Пересечение нескольких списков с IEnumerable. Пересекитесь ()

Имена классов должны всегда быть описательными и очевидными. Если у Вас есть несколько доменов ответственности за Ваши классы тогда, они должны, вероятно, быть пересмотрены.

Аналогично для Вас пакеты. Они должны быть сгруппированы доменом ответственности. Каждый домен имеет свои собственные исключения.

Обычно не потеют он, пока Вы не добираетесь до точки, где это становится подавляющим и чрезмерно увеличенным в размерах. Тогда сядьте и не кодируйте, просто осуществляйте рефакторинг классы, компилируя регулярно, чтобы удостовериться, что все работает. Тогда продолжите, как Вы сделали прежде.

76
задан Oskar 5 November 2009 в 08:52
поделиться

4 ответа

Как насчет:

var intersection = listOfLists
    .Skip(1)
    .Aggregate(
        new HashSet<T>(listOfLists.First()),
        (h, e) => { h.IntersectWith(e); return h; }
    );

Таким образом, он оптимизируется за счет использования одного и того же HashSet повсюду и по-прежнему в одном операторе. Просто убедитесь, что listOfLists всегда содержит хотя бы один список.

62
ответ дан 24 November 2019 в 11:15
поделиться

Вы действительно можете использовать Пересечение дважды. Однако я считаю, что это будет более эффективно:

HashSet<int> hashSet = new HashSet<int>(list1);
hashSet.IntersectWith(list2);
hashSet.IntersectWith(list3);
List<int> intersection = hashSet.ToList();

Конечно, это не проблема с маленькими наборами, но если у вас много больших наборов, это может быть значительным.

Обычно Enumerable.Intersect требует для создания набора для каждого вызова - если вы знаете, что собираетесь выполнять больше операций с наборами, вы также можете оставить этот набор.

Как всегда, внимательно следите за производительностью и удобочитаемостью - цепочка методов вызов Intersect дважды очень привлекателен.

РЕДАКТИРОВАТЬ: Для обновленного вопроса:

public List<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists)
{
    HashSet<T> hashSet = null;
    foreach (var list in lists)
    {
        if (hashSet == null)
        {
            hashSet = new HashSet<T>(list);
        }
        else
        {
            hashSet.IntersectWith(list);
        }
    }
    return hashSet == null ? new List<T>() : hashSet.ToList();
}

Или, если вы знаете, что он не будет пустым, и что Skip будет относительно дешевым:

public List<T> IntersectAll<T>(IEnumerable<IEnumerable<T>> lists)
{
    HashSet<T> hashSet = new HashSet<T>(lists.First());
    foreach (var list in lists.Skip(1))
    {
        hashSet.IntersectWith(list);
    }
    return hashSet.ToList();
}
57
ответ дан 24 November 2019 в 11:15
поделиться

Вы могли бы сделать следующее

var result = list1.Intersect(list2).Intersect(list3).ToList();
4
ответ дан 24 November 2019 в 11:15
поделиться

Попробуйте, это работает, но мне бы очень хотелось избавиться от .ToList () в совокупности.

var list1 = new List<int>() { 1, 2, 3 };
var list2 = new List<int>() { 2, 3, 4 };
var list3 = new List<int>() { 3, 4, 5 };
var listOfLists = new List<List<int>>() { list1, list2, list3 };
var intersection = listOfLists.Aggregate((previousList, nextList) => previousList.Intersect(nextList).ToList());

Обновление:

После комментария от @pomber, это можно избавиться от ToList () внутри вызова Aggregate и переместить его за пределы, чтобы выполнить его только один раз. Я не тестировал на производительность, будет ли предыдущий код быстрее нового. Необходимо указать параметр универсального типа метода Aggregate в последней строке, как показано ниже:

var intersection = listOfLists.Aggregate<IEnumerable<int>>(
   (previousList, nextList) => previousList.Intersect(nextList)
   ).ToList();
25
ответ дан 24 November 2019 в 11:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: