2012-08-13 15 views
25

Dato un insieme di elementi, come posso dividere la raccolta in 2 sotto-raccolte in base a un predicato?LINQ supporta nativamente la divisione di una raccolta in due?

Si potrebbe fare 2 Dove le ricerche, ma poi il tempo di esecuzione è di 2 * N (che, pur O (n), prende il doppio del tempo e non è, ovviamente, preferito)

IEnumerable<int> even = nums.Where(i => IsEven(i)); 
IEnumerable<int> odd = nums.Where(i => !IsEven(i)); 

Si potrebbe fare un singolo passaggio lineare (rifattorizzato in un metodo di estensione qui), ma questo significa che devi trascinare questo codice dappertutto, e più codice personalizzato rende le cose meno manutenibili.

public static void SplitOnPred<T>(
     this IEnumerable<T> collection, 
     Func<T, bool> pred, 
     out IEnumerable<T> trueSet, 
     out IEnumerable<T> falseSet 
    ) { 
     List<T> trueSetList = new List<T>(); 
     List<T> falseSetList = new List<T>(); 
     foreach(T item in collection) { 
      if(pred(item)) { 
       trueSetList.Add(item); 
      } else { 
       falseSetList.Add(item); 
      } 
     } 
     trueSet = trueSetList; 
     falseSet = falseSetList; 
} 

Domanda: Vuol LINQ hanno alcun supporto nativo per suddividere una collezione in 1 pass lineare?

+0

Perché è necessario un percorso lineare? –

+3

@SaeedAmiri non è proprio un requisito, e 2 passaggi lineari è abbastanza buono_ nella maggior parte dei casi, ma non sono mai veramente soddisfatto delle prestazioni _abbastanza_bene: P – James

risposta

25

LINQ dispone di un supporto nativo per suddividere una raccolta in 1 passaggio lineare?

Non esistono metodi incorporati che suddividano una raccolta in due versioni in base a un predicato. Dovresti utilizzare il tuo metodo, simile a quello che hai pubblicato.

Il metodo integrato più vicino sarebbe GroupBy (o ToLookup). Si potrebbe gruppo da pari o dispari:

var groups = nums.GroupBy(i => IsEven(i)); 

Questo si dividerà in due "gruppi" in base al fatto i numeri sono pari o dispari.

5

Beh, se la logica è esclusivo, in tuo caso, potete fare come

var list = new List<int> {1,2,3,4,5,6,7,8,9,10};  
var result = list.GroupBy(x=> x%2==0); 

e in result

foreach(var r in result) 
{ 
    if(r.Key) 
    //EVEN 
    else 
    //ODD 
} 
8

risposta di Reed Copsey cita ToLookup, e che sembra attraente.

var lookup = nums.ToLookup(IsEven); 

dove IsEven è un metodo statico con il tipo di firma e rendimento atteso. Poi

IEnumerable<int> even = lookup[true]; 
IEnumerable<int> odd = lookup[false]; 
1

Se si desidera supportare esecuzione differita, utilizzare una funzione o un'estensione simile a questo:

IEnumerable<T> Split<T>(this IEnumerable<T> source, out IEnumerable<T> odd) 
{ 
    IList<T> oddCollector = new List<T>(); 
    Bool odd = true; 
    foreach(T item in source) 
    { 
     if(odd) 
     { 
      oddCollector.Add(item); 
     } 
     else 
     { 
      yield return item; 
     } 
     odd = !odd; 
    } 
} 

Le mie scuse per qualsiasi piccolo errore del compilatore, ho fatto questo dalla parte superiore della mia testa. Invece di pari/dispari, puoi aggiungere un predicato.

+0

ATTENZIONE Questo supporta solo l'esecuzione differita se leggi anche prima, o leggi i due enumerabili in ordine alternato. Se leggi prima dispari non otterrai tutti i risultati. – csauve

Problemi correlati