2009-06-15 29 views
114

tengo el siguiente conjunto de elementos de un XML:LINQ: valores distintos

id   category 

5   1 
5   3 
5   4 
5   3 
5   3 

Necesito una lista distinta de estos elementos:

5   1 
5   3 
5   4 

Cómo puedo distinta de la categoría y el ID también en LINQ?

Respuesta

185

¿Estás tratando de ser distintos en más de un campo? Si es así, sólo tiene que utilizar un tipo anónimo y el operador distinto y debe estar bien:

var query = doc.Elements("whatever") 
       .Select(element => new { 
          id = (int) element.Attribute("id"), 
          category = (int) element.Attribute("cat") }) 
       .Distinct(); 

Si usted está tratando de conseguir un conjunto distinto de valores de un tipo "más grande", pero sólo mirando a algún subconjunto de propiedades para el aspecto distinción, es probable que desee DistinctBy como se aplica en MoreLINQ en DistinctBy.cs: (. Si se pasa en null como el comparador, se utilizará el comparador predeterminado para el tipo de clave)

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector, 
    IEqualityComparer<TKey> comparer) 
{ 
    HashSet<TKey> knownKeys = new HashSet<TKey>(comparer); 
    foreach (TSource element in source) 
    { 
     if (knownKeys.Add(keySelector(element))) 
     { 
      yield return element; 
     } 
    } 
} 

+0

Oh tan por "tipo más grande" que puede significar que Todavía quiero todas las propiedades en el resultado, aunque solo quiero comparar algunas propiedades para determinar la distinción. –

+0

@TheRedPea: Sí, exactamente. –

22

Además de la respuesta de Jon Skeet, también se puede utilizar el grupo de expresiones para obtener los grupos únicos a lo largo de w/un recuento de cada grupo de iteraciones:

var query = from e in doc.Elements("whatever") 
      group e by new { id = e.Key, val = e.Value } into g 
      select new { id = g.Key.id, val = g.Key.val, count = g.Count() }; 
3
// First Get DataTable as dt 
// DataRowComparer Compare columns numbers in each row & data in each row 

IEnumerable<DataRow> Distinct = dt.AsEnumerable().Distinct(DataRowComparer.Default); 

foreach (DataRow row in Distinct) 
{ 
    Console.WriteLine("{0,-15} {1,-15}", 
     row.Field<int>(0), 
     row.Field<string>(1)); 
} 
7

I Llego un poco tarde a la respuesta, pero es posible que desee hacerlo si desea el elemento completo, no solo los valores que desea agrupar:

var query = doc.Elements("whatever") 
       .GroupBy(element => new { 
          id = (int) element.Attribute("id"), 
          category = (int) element.Attribute("cat") }) 
       .Select(e => e.First()); 

esto le dará el primer elemento de conjunto que coincidan con su grupo por la selección, al igual que Jon Skeets segundo ejemplo usando DistinctBy, pero sin implementar IEqualityComparer comparador. DistinctBy probablemente sea más rápido, pero la solución anterior implicará menos código si el rendimiento no es un problema.

11

Para cualquiera que todavía parece; Aquí hay otra forma de implementar un comparador lambda personalizado.

public class LambdaComparer<T> : IEqualityComparer<T> 
    { 
     private readonly Func<T, T, bool> _expression; 

     public LambdaComparer(Func<T, T, bool> lambda) 
     { 
      _expression = lambda; 
     } 

     public bool Equals(T x, T y) 
     { 
      return _expression(x, y); 
     } 

     public int GetHashCode(T obj) 
     { 
      /* 
      If you just return 0 for the hash the Equals comparer will kick in. 
      The underlying evaluation checks the hash and then short circuits the evaluation if it is false. 
      Otherwise, it checks the Equals. If you force the hash to be true (by assuming 0 for both objects), 
      you will always fall through to the Equals check which is what we are always going for. 
      */ 
      return 0; 
     } 
    } 

a continuación, puede crear una extensión para el linq distinto que puede tener en

public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, T, bool> lambda) 
     { 
      return list.Distinct(new LambdaComparer<T>(lambda)); 
     } 

uso de lambda:

var availableItems = list.Distinct((p, p1) => p.Id== p1.Id); 
+0

En cuanto a la fuente de referencia, distinta de hash utiliza un set para almacenar los elementos que ya se ha producido. Siempre devolviendo el mismo código hash significa que cada elemento devuelto previamente se examina cada vez. Un código hash más robusto aceleraría las cosas porque solo se compararía con los elementos en el mismo cubo hash.Cero es un valor predeterminado razonable, pero podría valer la pena apoyar una segunda lambda para el código hash. – Darryl

+0

¡Buen punto! Voy a intentar editar cuando tenga tiempo, si estás trabajando en este dominio en este momento, siéntete libre de editarlo –

Cuestiones relacionadas