linq - как вы делаете запрос для элементов в одном источнике запроса, которые не находятся в другом? - PullRequest
4 голосов
/ 09 сентября 2008

Если у меня есть 2 источника запросов, как найти те, которые находятся в одном, а не в другом?

пример объединения для поиска предметов в обоих:

var results = from item1 in qs1.Items
   join item2 in qs2 on item1.field1 equals item2.field2
   select item1;

Так что же будет с кодом linq для возврата элементов в qs1, которых нет в qs2?

Ответы [ 5 ]

4 голосов
/ 09 сентября 2008

используйте метод расширения Except.

var items1 = new List<string> { "Apple","Orange","Banana" };
var items2 = new List<string> { "Grapes","Apple","Kiwi" };

var excluded = items1.Except(items2);
4 голосов
/ 09 сентября 2008

С Марко Руссо

NorthwindDataContext dc = new NorthwindDataContext();
dc.Log = Console.Out;
var query =
    from c in dc.Customers
    where !(from o in dc.Orders
            select o.CustomerID)
           .Contains(c.CustomerID)
    select c;
foreach (var c in query) Console.WriteLine( c );
3 голосов
/ 05 мая 2009

Ответ Даррена Коппа :

var excluded = items1.Except(items2);

- лучшее решение с точки зрения производительности.

(NB: Это верно, по крайней мере, для обычного LINQ, возможно, LINQ to SQL меняет положение вещей согласно посту Марко Руссо . Однако я думаю, что в «худшем случае» Даррен Метод Коппа будет возвращать, по крайней мере, скорость метода Руссо даже в среде LINQ to SQL).

В качестве быстрого примера попробуйте это в LINQPad :

void Main()
{
   Random rand = new Random();
   int n = 100000;
   var randomSeq = Enumerable.Repeat(0, n).Select(i => rand.Next());
   var randomFilter = Enumerable.Repeat(0, n).Select(i => rand.Next());

   /* Method 1: Bramha Ghosh's/Marco Russo's method */
   (from el1 in randomSeq where !(from el2 in randomFilter select el2).Contains(el1) select el1).Dump("Result");

   /* Method 2: Darren Kopp's method */
   randomSeq.Except(randomFilter).Dump("Result");
}

Попробуйте прокомментировать один из двух методов одновременно и попробуйте производительность для разных значений n.

Мой опыт (на моем ноутбуке Core 2 Duo), кажется, подсказывает:

n = 100. Method 1 takes about 0.05 seconds, Method 2 takes about 0.05 seconds
n = 1,000. Method 1 takes about 0.6 seconds, Method 2 takes about 0.4 seconds
n = 10,000. Method 1 takes about 2.5 seconds, Method 2 takes about 0.425 seconds
n = 100,000. Method 1 takes about 20 seconds, Method 2 takes about 0.45 seconds
n = 1,000,000. Method 1 takes about 3 minutes 25 seconds, Method 2 takes about 1.3 seconds

Метод 2 (ответ Даррена Коппа) явно быстрее.

Снижение скорости для метода 2 для больших n, скорее всего, связано с созданием случайных данных (не стесняйтесь добавлять разность в DateTime, чтобы подтвердить это), тогда как метод 1 явно имеет проблемы алгоритмической сложности (и просто глядя на вас можно видеть, что это по крайней мере O (N ^ 2), поскольку для каждого числа в первом наборе он сравнивается со всем вторым сбором).

Вывод: Используйте ответ Даррена Коппа о методе LINQ 'Except'

1 голос
/ 09 сентября 2008

Еще один совершенно другой способ взглянуть на это - передать лямбда-выражение (условие для заполнения второй коллекции) в качестве предиката для первой коллекции.

Я знаю, что это не точный ответ на вопрос. Я думаю, что другие пользователи уже дали правильный ответ.

0 голосов
/ 09 сентября 2008

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

List<string> items1 = new List<string>();
items1.Add("cake");
items1.Add("cookie");
items1.Add("pizza");

List<string> items2 = new List<string>();
items2.Add("pasta");
items2.Add("pizza");

var results = from item in items1
          where items2.Contains(item)
          select item;

foreach (var item in results)
    Console.WriteLine(item); //Prints 'pizza'
...