Respuestas:
¿Estás tratando de distinguirte por más de un campo? Si es así, solo use un tipo anónimo y el operador Distinct y debería estar bien:
var query = doc.Elements("whatever")
.Select(element => new {
id = (int) element.Attribute("id"),
category = (int) element.Attribute("cat") })
.Distinct();
Si está tratando de obtener un conjunto distinto de valores de un tipo "más grande", pero solo está mirando algún subconjunto de propiedades para el aspecto de distinción, probablemente desee DistinctBy
implementarlo en MoreLINQ en DistinctBy.cs
:
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;
}
}
}
(Si pasa null
como comparador, usará el comparador predeterminado para el tipo de clave).
Solo usa el Distinct()
con tu propio comparador.
Además de la respuesta de Jon Skeet, también puede usar el grupo por expresiones para obtener los grupos únicos junto con un conteo para cada iteración de grupos:
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() };
Para cualquiera que siga mirando; 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;
}
}
luego puede crear una extensión para linq Distinct que puede incluir lambda
public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, T, bool> lambda)
{
return list.Distinct(new LambdaComparer<T>(lambda));
}
Uso:
var availableItems = list.Distinct((p, p1) => p.Id== p1.Id);
Llego un poco tarde a la respuesta, pero es posible que desee hacer esto si desea todo el elemento, no solo los valores por los 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 completo que coincida con su grupo por selección, al igual que el segundo ejemplo de Jon Skeets usando DistinctBy, pero sin implementar el comparador IEqualityComparer. DistinctBy probablemente será más rápido, pero la solución anterior implicará menos código si el rendimiento no es un problema.
// 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));
}
Como estamos hablando de tener cada elemento exactamente una vez, un "conjunto" tiene más sentido para mí.
Ejemplo con clases e IEqualityComparer implementados:
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<Product>
{
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;
}
}
Ahora
List<Product> originalList = new List<Product> {new Product(1, "ad"), new Product(1, "ad")};
var setList = new HashSet<Product>(originalList, new ProductCompare()).ToList();
setList
tendrá elementos únicos
Pensé en esto mientras trataba con lo .Except()
que devuelve una diferencia establecida