linq: come si esegue una query per elementi in una fonte di query che non si trovano in un'altra?

StackOverflow https://stackoverflow.com/questions/50702

  •  09-06-2019
  •  | 
  •  

Domanda

Se ho 2 origini query, come posso trovare quelle presenti in una che non sono presenti nell'altra?

esempio di unione per trovare elementi in entrambi:

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

Quindi quale sarebbe il codice linq per restituire gli elementi in qs1 che non sono in qs2?

È stato utile?

Soluzione

Di Darren Kopp risposta:

var excluded = items1.Except(items2);

è la soluzione migliore dal punto di vista delle prestazioni.

(NB:Questo è vero almeno per LINQ regolare, forse LINQ to SQL cambia le cose come da Il post sul blog di Marco Russo.Tuttavia, immagino che nel "caso peggiore" il metodo di Darren Kopp restituirà almeno la velocità del metodo di Russo anche in un ambiente LINQ to SQL).

Come esempio veloce, prova questo 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");
}

Prova a commentare uno dei due metodi alla volta e prova le prestazioni per diversi valori di n.

La mia esperienza (sul mio laptop Core 2 Duo) sembra suggerire:

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

Il metodo 2 (risposta di Darren Kopp) è chiaramente più veloce.

La diminuzione della velocità per il Metodo 2 per n maggiori è molto probabilmente dovuta alla creazione di dati casuali (sentitevi liberi di inserire un diff DateTime per confermarlo) mentre il Metodo 1 ha chiaramente problemi di complessità algoritmica (e semplicemente guardando potete vederlo è almeno O(N^2) poiché ogni numero della prima raccolta viene confrontato con l'intera seconda raccolta).

Conclusione: Utilizzare la risposta di Darren Kopp del metodo "Except" di LINQ

Altri suggerimenti

Da Marco Russo

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 );

utilizzare il metodo di estensione Eccetto.

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

var excluded = items1.Except(items2);

Un altro modo completamente diverso di vedere la cosa sarebbe passare un'espressione lambda (condizione per popolare la seconda raccolta) come predicato della prima raccolta.

So che questa non è la risposta esatta alla domanda.Penso che altri utenti abbiano già dato la risposta corretta.

Ecco una versione più semplice della stessa cosa, non è necessario nidificare la query:

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'
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top