2013-02-14 6 views
5

Sono in procinto di creare un servizio per semplificare la selezione di un protocollo da parte di un utente da IANA - Protocol Registry.Come limitare in modo efficiente e quindi concatenare un risultato con un'espressione linq/lambda?

Come si potrebbe immaginare la ricerca nel registro per il termine http richiama un sacco di colpi. Dal momento che amt-soap-http verrà selezionato da un utente molto meno frequentemente rispetto al diretto http ho deciso che sarebbe una buona idea estrarre tutto ciò che inizia con http e quindi concatenarlo con i risultati rimanenti.

L'espressione lambda sotto è il risultato di tale processo di pensiero:

var records = this._ianaRegistryService.GetAllLike(term).ToList(); 
var results = records.Where(r => r.Name.StartsWith(term)) 
        .OrderBy(r => r.Name) 
        .Concat(records.Where(r => !r.Name.StartsWith(term)) 
            .OrderBy(r => r.Name)) 
        .Take(MaxResultSize); 

Purtroppo, mi sento come sto scorrendo i miei risultati più volte del necessario. Considerazioni sull'ottimizzazione prematura a parte c'è una combinazione di espressioni lambda che sarebbe più efficiente di quanto sopra?

+0

Per chi (come @Bobson e l'upvoter) che non conoscono la differenza tra '' Orderby' e GroupBy + SelectMany': Uno è 'n * log (n)' il funzionamento e l'altro 'n ' – I4V

+0

@ I4V Usando questa conoscenza c'è una risposta più efficiente della [risposta D Stanley fornita] (http://stackoverflow.com/a/14884435/61654)? – ahsteele

+0

ahsteele, ne avevo dato uno, ma l'ho rimosso dopo 3 downvotes (perché ho scritto * Non l'ho ancora testato *) – I4V

risposta

5

Potrebbe essere più efficace come un ordinamento in due fasi:

var results = records.OrderBy(r => r.Name.StartsWith(term) ? 1 : 2) 
        .ThenBy(r => r.Name) 
        .Take(MaxResultSize); 
+0

@ L.B c'è un'alternativa più efficiente a D Stanley e al mio codice? – ahsteele

+0

@DStanley Supponiamo 'n = 2 ** 20 (1M)', la tua soluzione sarebbe '1M * 20', ma l'ordinamento di due elementi 0.5M sarebbe' (0.5M * 19) * 2'. Inoltre, hai 'ThenBy' che è anche' (0.5M * 19) * 2' – I4V

+0

@ L.B e io con i miei miseri 6.800 punti. :) – ahsteele

3

Utilizzando commento per spiegare quello che sto cercando di fare è sempre difficile. Quindi posterò questa altra risposta. Supponiamo di voler ordinare una lista di interi casuali prima in base al suo essere pari o dispari poi in ordine numerico (simulando StartsWith con mod 2).

Ecco il caso di test: azione2 è la stessa di un'altra risposta.

Se si esegue questo codice, si vedrà che il mio suggerimento (action1) è due volte più veloce.

void Test() 
{ 
    Random rnd = new Random(); 
    List<int> records = new List<int>(); 
    for(int i=0;i<2000000;i++) 
    { 
     records.Add(rnd.Next()); 
    } 

    Action action1 =() => 
    { 
     var res1 = records.GroupBy(r => r % 2) 
        .OrderBy(x => x.Key) 
        .Select(x => x.OrderBy(y => y)) 
        .SelectMany(x => x) 
        .ToList(); 
    }; 

    Action action2 =() => 
    { 
     var res2 = records.OrderBy(x => x % 2).ThenBy(x => x).ToList(); 
    }; 


    //Avoid counting JIT 
    action1(); 
    action2(); 


    var sw = Stopwatch.StartNew(); 
    action1(); 
    long t1 = sw.ElapsedMilliseconds; 

    sw.Restart(); 
    action2(); 
    long t2 = sw.ElapsedMilliseconds; 

    Console.WriteLine(t1 + " " + t2); 
} 
+0

L'ordinamento dei gruppi è meno efficiente rispetto all'ordinamento dei singoli record quando esiste un solo record per gruppo. – Bobson

+0

@Bobson Provalo. ** Dico che è 2 volte più veloce ** – I4V

+0

Risultati ** quando c'è un solo record per gruppo **: '3302 615'. Ho preso il tuo codice, ho sostituito 'rnd.Next()' con 'i' e ho eliminato le chiamate'% 2'. Questa è un'approssimazione molto più vicina alla domanda dell'OP, in cui ogni valore nella sua lista era unico e il punto che stavo cercando di fare. Se si estrae anche l'ordinamento del secondo ordine da entrambi, i risultati sono '2656 568'. – Bobson

Problemi correlati