2010-07-07 21 views
10

Estoy tratando de encontrar una intersección con LINQ.LINQ intersect, multiple lists, some empty

muestra:

List<int> int1 = new List<int>() { 1,2 }; 
List<int> int2 = new List<int>(); 
List<int> int3 = new List<int>() { 1 }; 
List<int> int4 = new List<int>() { 1, 2 }; 
List<int> int5 = new List<int>() { 1 }; 

desea devolver: 1, tal como existe en todas las listas .. Si me quedo:

var intResult= int1 
      .Intersect(int2) 
      .Intersect(int3) 
      .Intersect(int4) 
      .Intersect(int5).ToList(); 

no devuelve nada como 1, obviamente, no está en la lista INT2 . ¿Cómo hago para que esto funcione independientemente de si una lista está vacía o no?

Utilice el ejemplo anterior o:

List<int> int1 = new List<int>() { 1,2 }; 
List<int> int2 = new List<int>(); 
List<int> int3 = new List<int>(); 
List<int> int4 = new List<int>(); 
List<int> int5 = new List<int>(); 

¿Cómo devuelvo 1 & 2 en este caso .. No sé de antemano si las listas están pobladas ...

+4

Considere la intersección de '{1}, {2}, {3}'. Después de intersecar los dos primeros, estás tratando de encontrar la intersección de '{}, {3}'. Y nos está pidiendo que propongamos algo que diera '{}' en este caso, pero '{3}' si realizáramos la misma operación en '{3}, {}'. Si no desea que se tengan en cuenta las listas vacías, elimínelas de consideración antes de intentar calcular la intersección. –

+0

@Anon, observación interesante. –

+0

¿Cómo manejaría eso? if (int2.Count> 0) var set = int1.Intersect (int2); if (int3.Count> 0) var set2 = set.Intersect (set); if (int2.Count> 0) var set3 = int2.Intersect (int2); ..... Parece un poco hokey ... –

Respuesta

2

Usted podría escribir un método de extensión para definir ese comportamiento. Algo así como

static class MyExtensions 
{ 
    public static IEnumerable<T> IntersectAllIfEmpty<T>(this IEnumerable<T> list, IEnumerable<T> other) 
    { 
     if (other.Any()) 
      return list.Intersect(other); 
     else 
      return list; 
    } 
} 

Así que el código de abajo imprimiría 1.

List<int> list1 = new List<int>() { 1, 2 }; 
List<int> list2 = new List<int>(); 
List<int> list3 = new List<int>() { 1 }; 

foreach (int i in list1.IntersectAllIfEmpty(list2).IntersectAllIfEmpty(list3)) 
    Console.WriteLine(i); 

Actualización:

Anon nos lleva a un buen punto en los comentarios a la pregunta. La función anterior dará como resultado un conjunto vacío si list está vacío, lo que debería ser deseable. Esto significa que si la primera lista en la cadena de método o el conjunto de resultados de cualquier intersección está vacío, el resultado final estará vacío.

Para permitir una primera lista vacía pero no para conjuntos de resultados vacíos, podría tomar un enfoque diferente. Este es un método que no es un método de extensión, sino que toma una matriz de params de IEnumerables y primero filtra los conjuntos vacíos y luego intenta interceptar el resto.

public static IEnumerable<T> IntersectAllIfEmpty<T>(params IEnumerable<T>[] lists) 
{ 
    IEnumerable<T> results = null; 

    lists = lists.Where(l => l.Any()).ToArray(); 

    if (lists.Length > 0) 
    { 
     results = lists[0]; 

     for (int i = 1; i < lists.Length; i++) 
      results = results.Intersect(lists[i]); 
    } 
    else 
    { 
     results = new T[0]; 
    } 

    return results; 
} 

Se podría utilizar de esta manera

List<int> list0 = new List<int>(); 
List<int> list1 = new List<int>() { 1, 2 }; 
List<int> list2 = new List<int>() { 1 }; 
List<int> list3 = new List<int>() { 1,2,3 }; 

foreach (int i in IntersectAllIfEmpty(list0, list1, list2, list3)) 
{ 
    Console.WriteLine(i); 
} 
+0

¿dónde declararía el método de extensión? Me da: El método de extensión solo se puede desclasar en clase estática no genérica, no anidada simplemente tratando de hacer esto en un método ... –

+0

Lo pondría en una clase estática. He agregado una definición de clase a mi respuesta. –

+0

que funciona muy bien! Gracias Anthony ... –

13

Si lo necesita en un solo paso, la solución más simple es filtrar las listas vacías:

public static IEnumerable<T> IntersectNonEmpty<T>(this IEnumerable<IEnumerable<T>> lists) 
{ 
    var nonEmptyLists = lists.Where(l => l.Any()); 
    return nonEmptyLists.Aggregate((l1, l2) => l1.Intersect(l2)); 
} 

A continuación, puede utilizar en una colección de listas u otra IEnumerable s:

IEnumerable<int>[] lists = new[] { l1, l2, l3, l4, l5 }; 
var intersect = lists.IntersectNonEmpty(); 

Usted puede preferir un método estático normal:

public static IEnumerable<T> IntersectNonEmpty<T>(params IEnumerable<T>[] lists) 
{ 
    return lists.IntersectNonEmpty(); 
} 

var intersect = ListsExtensionMethods.IntersectNonEmpty(l1, l2, l3, l4, l5); 
+0

hmmm, buenas cosas, gracias chicos! –

+1

Solo una pequeña advertencia: gracias al Agregado(), este método de extensión arrojará una InvalidOperationException con "la secuencia no contiene elementos" si todas las listas están vacías. – Gavin