ha un modo per "memorizzare" i risultati della query precedente durante l'interrogazione di?LINQ con query "Memoria"
Si consideri il seguente caso:
public class Foo {
public int Id { get; set; }
public ICollection<Bar> Bars { get; set; }
}
public class Bar {
public int Id { get; set; }
}
Ora, se due o più Foo
hanno lo stesso insieme di Bar
(non importa quale sia l'ordine è), essi sono considerati come simileFoo
.
Esempio:
foo1.Bars = new List<Bar>() { bar1, bar2 };
foo2.Bars = new List<Bar>() { bar2, bar1 };
foo3.Bars = new List<Bar>() { bar3, bar1, bar2 };
Nel caso precedente, foo1
è simile a foo2
ma entrambi foo1
e foo2
sono nonsimile a foo3
Dato che abbiamo un risultato query
consistente IEnumerable
o IOrderedEnumerable
di Foo
. Da query
, dobbiamo trovare il primo N
foo
che sono nonsimile.
Questa attività sembra richiedere una memoria della raccolta di bars
che è stata scelta in precedenza.
Con parzialeLINQ
potremmo fare in questo modo:
private bool areBarsSimilar(ICollection<Bar> bars1, ICollection<Bar> bars2) {
return bars1.Count == bars2.Count && //have the same amount of bars
!bars1.Select(x => x.Id)
.Except(bars2.Select(y => y.Id))
.Any(); //and when excepted does not return any element mean similar bar
}
public void somewhereWithQueryResult(){
.
.
List<Foo> topNFoos = new List<Foo>(); //this serves as a memory for the previous query
int N = 50; //can be any number
foreach (var q in query) { //query is IOrderedEnumerable or IEnumerable
if (topNFoos.Count == 0 || !topNFoos.Any(foo => areBarsSimilar(foo.Bars, q.Bars)))
topNFoos.Add(q);
if (topNFoos.Count >= N) //We have had enough Foo
break;
}
}
Il topNFoos
List
servirà come memoria della query precedente e possiamo saltare la Foo q
nella foreach
ciclo che già hanno identica Bars
con Any
dello Foo
nello topNFoos
.
La mia domanda è: esiste un modo per farlo in LINQ
(completamenteLINQ
)?
var topNFoos = from q in query
//put something
select q;
Se la "memoria" richiesto è da un particolare elemento di query q
o una variabile al di fuori della query, allora potremmo usare let
variabile per memorizzare nella cache è:
int index = 0;
var topNFoos = from q in query
let qc = index++ + q.Id //depends on q or variable outside like index, then it is OK
select q;
Ma se deve venire dal interrogazione precedente della query stessa quindi le cose cominciano a diventare più fastidiose.
C'è un modo per farlo?
Edit:
(Io attualmente sono creating a test case (link GitHub) per le risposte.Ancora capire come posso testare tutte le risposte equamente)
(La maggior parte delle risposte di seguito hanno lo scopo di risolvere il mio particolare domanda e sono di per sé buono (Rob, Spender di, e le risposte di David B che utilizzano IEqualityComparer
sono particolarmente impressionante). Tuttavia, se c'è qualcuno che può dare risposta alla mia domanda più generale "non LINQ ha un modo per 'memorizzare' le sue precedenti risultati delle query durante l'interrogazione", vorrei anche essere felice)
(parte dalla significativa differenza di prestazioni per il caso particolare che ho presentato sopra quando si utilizza LINQ completo/parziale, una risposta che mira a rispondere alla mia domanda generale sulla memoria LINQ è di Ivan Stoev. Un altro con una buona combinazione è Rob. Per essere più chiaro, cerco una soluzione generale ed efficiente, se ce n'è una, usando LINQ)
Il tuo caso "Senza LINQ" sembra utilizzare principalmente LINQ. – spender
Questo è ciò 'Ora, se due o più Foo hanno la stessa collezione di barre (indipendentemente dall'ordine), sono considerate come Foo simili. Considerate valide per l'intera applicazione o solo in questo caso? – Rob
@spender hai ragione, quello che intendo in realtà è * parziale * LINQ, dovrei aggiornarlo ... – Ian