2011-03-11 16 views
13

Actualmente tengo 2 consultas que están regresando listas de MyModel como esto:consulta LINQ con distintas y Unión

var q1 = .... 
     select new MyModel() 
     { 
      TheData1 = ... 
      TheData2 = ... 
      TheUniqueID = ... 
     } 

var q2 = .... 
     select new MyModel() 
     { 
      TheData1 = ... 
      TheData2 = ... 
      TheUniqueID = ... 
     } 

Si en la Q1 que tengo:

TheUniqueID = 2,3,6,9,11 

y en la Q2 que tienen:

TheUniqueID = 2,4,7,9,12 

¿Cómo escribir la consulta para que me sale una lista de MyModel donde

TheUniqueID = 2,3,4,6,7,9,11,12 

En otras palabras, cada TheUniqueID está presente solo una vez (es decir. 2 y 9 no repetidos).

Empecé a buscar en Union y distinto pero me pregunto si necesito 2 de declaraciones o no.

Cualquier sugerencia es bienvenida.

+0

Es esta LINQ a objetos o LINQ a SQL, etc? –

+0

es una consulta de linq a sql – frenchie

Respuesta

21

Creo que frenchie quiere una lista de MyModel en lugar de solo TheUniqueID.

Es necesario crear una clase MyModelTheUniqueIDComparer y aprobar una nueva instancia de la misma como un segundo argumento en Union:

class MyModelTheUniqueIDComparer : IEqualityComparer<MyModel> 
{ 
    public bool Equals(MyModel x, MyModel y) 
    { 
     return x.TheUniqueID == y.TheUniqueID; 
    } 

    // If Equals() returns true for a pair of objects 
    // then GetHashCode() must return the same value for these objects. 

    public int GetHashCode(MyModel myModel) 
    { 
     return myModel.TheUniqueID.GetHashCode(); 
    } 
} 

A continuación, puede llamar para obtener el resultado:

var result = q1.Union(q2, new MyModelTheUniqueIDComparer()); 

Ver http://msdn.microsoft.com/en-us/library/bb358407.aspx para más detalles.

Actualización:

Prueba esto:

public class A 
{ 
    public string TheData1 { get; set; } 
    public string TheData2 { get; set; } 
    public string UniqueID { get; set; } 
} 

public class AComparer : IEqualityComparer<A> 
{ 

    #region IEqualityComparer<A> Members 

    public bool Equals(A x, A y) 
    { 
     return x.UniqueID == y.UniqueID; 
    } 

    public int GetHashCode(A obj) 
    { 
     return obj.UniqueID.GetHashCode(); 
    } 

    #endregion 
} 

Y prueba con esto:

var listOfA = new List<A>(); 
var q1 = from a in listOfA 
       select new A() 
      { 
       TheData1 = "TestData", 
       TheData2 = "TestData", 
       UniqueID = a.UniqueID 
      }; 

var anotherListOfA = new List<A>(); 
var q2 = from a in anotherListOfA 
       select new A() 
       { 
        TheData1 = "TestData", 
        TheData2 = "TestData", 
        UniqueID = a.UniqueID 
       }; 

q1.Union(q2, new AComparer()); 

Asegúrate de que tienes using System.Linq;

+0

¡ah! ¡Sabía que no iba a ser algo simple! Me tomaré un descanso y volveré para probarlo un poco. Gracias por tu ayuda. – frenchie

+0

Obtengo una sobrecarga no compatible utilizada para el error de Unión del operador de consulta. Estoy tratando de transmitir pero no está funcionando: el resultado devuelto como Lista o el resultado de la devolución (Lista ) no funciona en la declaración de devolución. ¿Alguna idea de por qué? – frenchie

+0

try 'return q1.Union (q2, new MyModelTheUniqueIDComparer()). ToList();' –

3

Como se señaló si está combinando las listas con .Union() tendrá que definir la singularidad usando la sobrecarga que pasa un IEqualityComparer para su tipo.

var result = q1.Union(q2, myEqualityComparer); 

lo contrario, y más fácil que usted podría utilizar DistinctBy(x=> x.TheUniqueId) del proyecto MoreLinq:

var result = q1.Concat(q2).DistinctBy(c => c.TheUniqueID); 
+0

'MoreLinq' parece haberse convertido en un enlace inactivo. Pruebe esto en su lugar: https://code.google.com/p/morelinq/ –

6

Union crea un Enumerable con valores únicos de ambas colecciones. En otras palabras, no necesita Distinct.

edición: ejemplo de Unión here

Edit2: se olvidó de que no es la lista de UniqueIDs que está concatenando. Eliminé el código sugerido porque era incorrecto. Debería poder hacer un simple Union si implementa un IEqualityComparer, pero eso podría ser excesivo.

3

ans sola línea ineficiente wer sin IEqualityComparerer

Usando el código fuente de inspiración MoreLinq, esto le dará una lista única:

respuesta corta (el OrdenarPor no es necesario, pero si no se utiliza la respuesta sale como 2,3,6 , 9,11,4,7,12):

var concattedUniqueList = theUniqueIDList1.Concat(theUniqueIDList2) 
      .GroupBy(f=>f.UniqueID, f=>f).Select(g => g.First()).OrderBy(f=>f.UniqueID); 

la respuesta completa:

//INPUT 
//theUniqueIDList1 = 2,3,6,9,11 
//theUniqueIDList2 = 2,4,7,9,12 
//OUTPUT 
//2,3,4,6,7,9,11,12 
public class MyModel 
{ 
    public string TheData1 { get; set; } 
    public string TheData2 { get; set; } 
    public int UniqueID { get; set; } 
} 

public static void GroupByEx1() 
    { 
     // Create a list of Models. 
     List<MyModel> theUniqueIDList1 = 
      new List<MyModel>{ new MyModel { TheData1="Barley", UniqueID=2 }, 
            new MyModel { TheData1="Boots",  UniqueID=3 }, 
            new MyModel { TheData1="Whiskers", UniqueID=6 }, 
            new MyModel { TheData1="Daisy",  UniqueID=9 }, 
            new MyModel { TheData1="Preti",  UniqueID=11 } }; 
     List<MyModel> theUniqueIDList2 = 
      new List<MyModel>{ new MyModel { TheData1="Barley", UniqueID=2 }, 
            new MyModel { TheData1="Henry",  UniqueID=4 }, 
            new MyModel { TheData1="Walsh",  UniqueID=7 }, 
            new MyModel { TheData1="Daisy",  UniqueID=9 }, 
            new MyModel { TheData1="Ugly", UniqueID=12 } }; 

     var concattedUniqueList = theUniqueIDList1.Concat(theUniqueIDList2) 
      .OrderBy(f=>f.UniqueID).GroupBy(f=>f.UniqueID, f=>f).Select(g => g.First()); 

     foreach (var item in concattedUniqueList) 
     { 
      Console.WriteLine("UniqueId: {0}({1})", item.UniqueID, item.TheData1); 
     } 
    } 

void Main() 
{ 
    GroupByEx1();    
    //2,3,4,6,7,9,11,12 
} 

Nota: en comparación con el uso de un IEqualityComparer para la velocidad - 10000 veces para cada 698 ns para Concat 100 ns para IEqualityComparer

desarrollan en LinqPad

+1

En mi opinión, esta es la mejor respuesta. Es mucho mejor que hacer tu propio comparador. Para ser quisquilloso, haría '.GroupBy (f => f.UniqueID, f => f) .SelectMany (g => g.Take (1));' como una forma de no llamar habitualmente a '.First()' . Funciona aquí, pero en otras consultas, llamar a '.First()' puede provocar problemas. Pero ciertamente sigue siendo la mejor respuesta. – Enigmativity

+0

¿Qué sucede si dos listas son de tipos diferentes pero el parámetro UniqueID es el mismo en ambas listas? –