LINQ: valori distinti

Ho il seguente set di elementi da un XML:

id category 5 1 5 3 5 4 5 3 5 3 

Ho bisogno di un elenco distinto di questi elementi:

 5 1 5 3 5 4 

Come posso distinguere per ID categoria e anche in LINQ?

Stai cercando di essere distinto da più di un campo? Se è così, basta usare un tipo anonimo e l’operatore Distinct e dovrebbe essere a posto:

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

Se stai cercando di ottenere un insieme distinto di valori di un tipo “più grande”, ma solo guardando un sottoinsieme di proprietà per l’aspetto distinzione, probabilmente DistinctBy come implementato in MoreLINQ in DistinctBy.cs :

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

(Se si passa in null come comparatore, verrà utilizzato il confronto predefinito per il tipo di chiave.)

Usa semplicemente Distinct() con il tuo comparatore.

http://msdn.microsoft.com/en-us/library/bb338049.aspx

Oltre alla risposta di Jon Skeet, puoi anche utilizzare il gruppo per espressioni per ottenere i gruppi univoci lungo w / un conteggio per ciascuna iterazione di gruppi:

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

Per chiunque stia ancora guardando; ecco un altro modo di implementare un comparatore Lambda personalizzato.

 public class LambdaComparer : IEqualityComparer { private readonly Func _expression; public LambdaComparer(Func 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; } } 

puoi quindi creare un’estensione per linq Distinct che può contenere lambda’s

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

Uso:

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

Sono un po ‘in ritardo sulla risposta, ma potresti volerlo fare se vuoi l’intero elemento, non solo i valori che desideri raggruppare:

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

Questo ti darà il primo elemento intero corrispondente al tuo gruppo per selezione, proprio come il secondo esempio di Jon Skeets usando DistinctBy, ma senza implementare il comparatore IEqualityComparer. DistinctBy sarà molto più veloce, ma la soluzione sopra implicherà meno codice se le prestazioni non sono un problema.

 // First Get DataTable as dt // DataRowComparer Compare columns numbers in each row & data in each row IEnumerable Distinct = dt.AsEnumerable().Distinct(DataRowComparer.Default); foreach (DataRow row in Distinct) { Console.WriteLine("{0,-15} {1,-15}", row.Field(0), row.Field(1)); } 

Dato che stiamo parlando di avere ogni elemento esattamente una volta, un “set” ha più senso per me.

Esempio con classi e IEqualityComparer implementato:

  public class Product { public int Id { get; set; } public string Name { get; set; } public Product(int x, string y) { Id = x; Name = y; } } public class ProductCompare : IEqualityComparer { public bool Equals(Product x, Product y) { //Check whether the compared objects reference the same data. if (Object.ReferenceEquals(x, y)) return true; //Check whether any of the compared objects is null. if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) return false; //Check whether the products' properties are equal. return x.Id == y.Id && x.Name == y.Name; } public int GetHashCode(Product product) { //Check whether the object is null if (Object.ReferenceEquals(product, null)) return 0; //Get hash code for the Name field if it is not null. int hashProductName = product.Name == null ? 0 : product.Name.GetHashCode(); //Get hash code for the Code field. int hashProductCode = product.Id.GetHashCode(); //Calculate the hash code for the product. return hashProductName ^ hashProductCode; } } 

Adesso

 List originalList = new List {new Product(1, "ad"), new Product(1, "ad")}; var setList = new HashSet(originalList, new ProductCompare()).ToList(); 

setList avrà elementi unici

Ho pensato a questo mentre ho a che fare con .Except() che restituisce una differenza di set