2012-08-13 10 views
25

Dada una colección de elementos, ¿cómo divido la colección en 2 subcolecciones basadas en un predicado?¿LINQ admite de forma nativa dividir una colección en dos?

Usted podría hacer 2 Donde búsquedas, pero entonces el tiempo de ejecución es de 2 * N (que, sin dejar de O (n), toma el doble de tiempo y, obviamente, no es preferido)

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

Usted podría hacer un solo pase lineal usted mismo (refactorizado en un método de extensión aquí), pero esto significa que tiene que arrastrar este código por todas partes, y más código personalizado hace que las cosas sean menos fáciles de mantener.

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

Pregunta: ¿Tiene LINQ tienen ningún soporte nativo para dividir una colección de 1 pase lineal?

+0

¿Por qué necesita una ruta lineal? –

+3

@SaeedAmiri no es realmente un requisito, y 2 pasos lineales son lo suficientemente buenos en la mayoría de los casos, pero nunca estoy realmente contento con el desempeño lo suficientemente bueno: P – James

Respuesta

25

¿LINQ tiene algún soporte nativo para dividir una colección en 1 pase lineal?

No hay métodos integrados que dividan una colección en dos versiones basadas en un predicado. Debería usar su propio método, similar al que publicó.

El método incorporado más cercano sería GroupBy (o ToLookup). Puede agrupar por pares o impares:

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

Esto dividirá en dos "grupos" en función de si los números son pares o impares.

5

Bueno, si la lógica es esclusive, en su caso, usted puede hacer como

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

y en result

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

respuesta de Reed Copsey menciona ToLookup, y que parece atractivo.

var lookup = nums.ToLookup(IsEven); 

donde IsEven es un método estático con la firma y el retorno tipo esperado. Entonces

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

Si quieres apoyar ejecución diferida, utilizar una función o extensión de la siguiente manera:

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

Mis disculpas por cualquier error del compilador pequeña, lo hice desde la parte superior de la cabeza. En lugar de par/impar, puede agregar un predicado.

+0

ADVERTENCIA Esto solo admite ejecución diferida si lee primero, o lee los dos enumerables en orden alterno. Si lees impar primero, no obtendrás todos los resultados. – csauve

Cuestiones relacionadas