Distinto por propiedad de clase con LINQ


Tengo una colección:

List<Car> cars = new List<Car>();

Los coches se identifican de forma única por su propiedad CarCode.

Tengo tres autos en la colección, y dos con CarCasas idénticas.

¿Cómo puedo usar LINQ para convertir esta colección a Coches con CarCasas únicas?

Author: wonea, 2010-03-29

9 answers

Puede usar agrupación, y obtener el primer automóvil de cada grupo:

List<Car> distinct =
  cars
  .GroupBy(car => car.CarCode)
  .Select(g => g.First())
  .ToList();
 218
Author: Guffa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-29 12:44:02

Use MoreLINQ , que tiene un método DistinctBy:)

IEnumerable<Car> distinctCars = cars.DistinctBy(car => car.CarCode);

(Esto es solo para Objetos LINQ to, eso sí.)

 102
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-20 21:02:12

El mismo enfoque que Guffa pero como un método de extensión:

public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
{
    return items.GroupBy(property).Select(x => x.First());
}

Utilizado como:

var uniqueCars = cars.DistinctBy(x => x.CarCode);
 33
Author: Sheldor the conqueror,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-27 14:14:16

Puede implementar un IEqualityComparer y usarlo en su extensión Distinta.

class CarEqualityComparer : IEqualityComparer<Car>
{
    #region IEqualityComparer<Car> Members

    public bool Equals(Car x, Car y)
    {
        return x.CarCode.Equals(y.CarCode);
    }

    public int GetHashCode(Car obj)
    {
        return obj.CarCode.GetHashCode();
    }

    #endregion
}

Y luego

var uniqueCars = cars.Distinct(new CarEqualityComparer());
 26
Author: Anthony Pegram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-29 12:52:51

Otro método de extensión para Linq-to-Objects, sin usar GroupBy:

    /// <summary>
    /// Returns the set of items, made distinct by the selected value.
    /// </summary>
    /// <typeparam name="TSource">The type of the source.</typeparam>
    /// <typeparam name="TResult">The type of the result.</typeparam>
    /// <param name="source">The source collection.</param>
    /// <param name="selector">A function that selects a value to determine unique results.</param>
    /// <returns>IEnumerable&lt;TSource&gt;.</returns>
    public static IEnumerable<TSource> Distinct<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
        HashSet<TResult> set = new HashSet<TResult>();

        foreach(var item in source)
        {
            var selectedValue = selector(item);

            if (set.Add(selectedValue))
                yield return item;
        }
    }
 5
Author: Luke Puplett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-18 10:59:46

Creo que la mejor opción en términos de rendimiento (o en cualquier término) es distinguirse usando la interfaz IEqualityComparer.

Aunque implementar cada vez un nuevo comparador para cada clase es engorroso y produce código repetitivo.

Así que aquí hay un método de extensión que produce un nuevo IEqualityComparer sobre la marcha para cualquier clase que use reflexión.

Uso:

var filtered = taskList.DistinctBy(t => t.TaskExternalId).ToArray();

Código del método de extensión

public static class LinqExtensions
{
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
    {
        GeneralPropertyComparer<T, TKey> comparer = new GeneralPropertyComparer<T,TKey>(property);
        return items.Distinct(comparer);
    }   
}
public class GeneralPropertyComparer<T,TKey> : IEqualityComparer<T>
{
    private Func<T, TKey> expr { get; set; }
    public GeneralPropertyComparer (Func<T, TKey> expr)
    {
        this.expr = expr;
    }
    public bool Equals(T left, T right)
    {
        var leftProp = expr.Invoke(left);
        var rightProp = expr.Invoke(right);
        if (leftProp == null && rightProp == null)
            return true;
        else if (leftProp == null ^ rightProp == null)
            return false;
        else
            return leftProp.Equals(rightProp);
    }
    public int GetHashCode(T obj)
    {
        var prop = expr.Invoke(obj);
        return (prop==null)? 0:prop.GetHashCode();
    }
}
 4
Author: Anestis Kivranoglou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-09 13:33:32

No se puede usar efectivamente Distinct en una colección de objetos (sin trabajo adicional). Voy a explicar por qué.

La documentación dice :

Utiliza el comparador de igualdad predeterminado, Default, para comparar valores.

Para objetos eso significa que usa el método de ecuación por defecto para comparar objetos ( fuente). Eso está en su código hash. Y dado que sus objetos no implementan los métodos GetHashCode() y Equals, verificará la referencia del objeto, que no son distintos.

 2
Author: Patrick Hofman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-22 11:43:22

Otra forma de lograr lo mismo...

List<Car> distinticBy = cars
    .Select(car => car.CarCode)
    .Distinct()
    .Select(code => cars.First(car => car.CarCode == code))
    .ToList();

Es posible crear un método de extensión para hacer esto de una manera más genérica. Sería interesante si alguien pudiera evaluar el desempeño de este' DistinctBy ' contra el enfoque de GroupBy.

 1
Author: JwJosefy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-27 18:09:10

Puedes consultar mi biblioteca PowerfulExtensions. Actualmente está en una etapa muy joven, pero ya puede usar métodos como Distinct, Union, Intersect, Excepto en cualquier número de propiedades;

Así es como lo usas:

using PowerfulExtensions.Linq;
...
var distinct = myArray.Distinct(x => x.A, x => x.B);
 1
Author: gisek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-15 20:21:58