Eliminar duplicados en la lista usando linq

314

Tengo una clase Itemscon properties (Id, Name, Code, Price).

La lista de Itemsse rellena con elementos duplicados.

Por ej .:

1         Item1       IT00001        $100
2         Item2       IT00002        $200
3         Item3       IT00003        $150
1         Item1       IT00001        $100
3         Item3       IT00003        $150

¿Cómo eliminar los duplicados en la lista usando linq?

Prasad
fuente
Tengo otra clase como propiedad en la Clase de Artículos también
Prasad
También puedes hacer var set = new HashSet<int>(); var uniques = items.Where(x => set.Add(x.Id));. Debería ser criminal hacerlo ...
nawfal

Respuestas:

394
var distinctItems = items.Distinct();

Para hacer coincidir solo algunas de las propiedades, cree un comparador de igualdad personalizado, por ejemplo:

class DistinctItemComparer : IEqualityComparer<Item> {

    public bool Equals(Item x, Item y) {
        return x.Id == y.Id &&
            x.Name == y.Name &&
            x.Code == y.Code &&
            x.Price == y.Price;
    }

    public int GetHashCode(Item obj) {
        return obj.Id.GetHashCode() ^
            obj.Name.GetHashCode() ^
            obj.Code.GetHashCode() ^
            obj.Price.GetHashCode();
    }
}

Entonces úsalo así:

var distinctItems = items.Distinct(new DistinctItemComparer());
Christian Hayter
fuente
Hola Christian, ¿Cuál será el cambio en el código si tengo una Lista <my_Custom_Class> y List <string>. Mi clase personalizada tiene varios elementos en los que uno es el número DCN y la lista <cadena> solo tiene un número DCN. Entonces necesito verificar que la Lista <Custom_Class> contiene cualquier dcn de List <string>. Por ejemplo, suponga List1 = List <Custom_Class> y List2 = List <String>. Si List1 tiene 2000 elementos y list2 tiene 40000 elementos en los que 600 elementos de List1 existen en List2. Entonces, en este caso, necesito 1400 como mi lista de salida como list1. Entonces, ¿cuál sería la expresión? Gracias de antemano
También hay un caso más aquí, ya que List1 contiene varios elementos, los valores de otros elementos pueden ser diferentes, pero el DCN debe ser el mismo. Entonces, en mi caso, Distinct no pudo dar la salida deseada.
2
Encuentro que las clases de comparación son extremadamente útiles. Pueden expresar una lógica distinta de las simples comparaciones de nombres de propiedades. Escribí uno nuevo el mes pasado, para hacer algo que GroupByno podía.
Christian Hayter
Funciona bien y me hizo aprender algo nuevo e investigar al XoRoperador ^en C #. Lo había usado en VB.NET a través de, Xorpero tuve que hacer una doble toma de su código para ver qué era al principio.
atconway
Este es el error que obtengo cuando intento usar Distinct Comparer: "LINQ to Entities no reconoce el método 'System.Linq.IQueryable 1[DataAccess.HR.Dao.CCS_LOCATION_TBL] Distinct[CCS_LOCATION_TBL](System.Linq.IQueryable1 [DataAccess.HR.Dao.CCS_LOCATION_TBL], System.Collections.Generic.IEqualityComparer`1 [ DataAccess.HR.Dao.CCS_LOCATION_TBL]) ', y este método no se puede traducir a una expresión de tienda.
user8128167
601
var distinctItems = items.GroupBy(x => x.Id).Select(y => y.First());
Freddy
fuente
28
Gracias. Estaba buscando evitar escribir una clase comparadora, así que me alegro de que esto funcione :)
Jen
8
+1 Esta solución incluso permite un desempate: ¡elimine los duplicados con criterios!
Adriano Carneiro
44
Pero un poco por encima!
Amirhossein Mehrvarzi
1
Pero, como Victor Juri sugirió a continuación: use FirstorDefault. no puedo creer, esa solución puede ser tan simple (sin un comparador de igualdad personalizado)
CyberHawk
66
Puede agrupar con múltiples propiedades: List <XYZ> MyUniqueList = MyList.GroupBy (x => new {x.Column1, x.Column2}). Seleccione (g => g.First ()). ToList ();
Sumit Joshi
41

Si hay algo que está desechando su consulta Distinct, es posible que desee mirar MoreLinq y usar el operador DistinctBy y seleccionar objetos distintos por id.

var distinct = items.DistinctBy( i => i.Id );
tvanfosson
fuente
1
No existe un método DistinctBy () con Linq.
Fereydoon Barikzehy
77
@FereydoonBarikzehy Pero no está hablando de Linq puro. En la publicación es linq para el proyecto MoreLinq ...
Ademar
30

Así es como pude agruparme con Linq. Espero eso ayude.

var query = collection.GroupBy(x => x.title).Select(y => y.FirstOrDefault());
Victor Juri
fuente
3
@nawfal, estaba sugiriendo FirstOrDefault () en lugar de First ()
sobelito
23
Si estoy en lo cierto, el uso de FirstOrDefaultaquí no ofrece ningún beneficio si Selectsigue inmediatamente GroupBy, ya que no hay posibilidad de que haya un grupo vacío (los grupos se derivaron del contenido de la colección)
Roy Tinker
17

Use, Distinct()pero tenga en cuenta que usa el comparador de igualdad predeterminado para comparar valores, por lo que si desea algo más allá de eso, debe implementar su propio comparador.

Consulte http://msdn.microsoft.com/en-us/library/bb348436.aspx para ver un ejemplo.

Brian Rasmussen
fuente
Debo notar que el comparador predeterminado funciona si los tipos de miembros de la colección son uno de los tipos de valor. Pero qué comparador de igualdad predeterminado selecciona por csc para los tipos de referencia. Los tipos de referencia deben tener comparador (es) propio (s).
Nuri YILMAZ
16

Aquí tiene tres opciones para eliminar elementos duplicados de su lista:

  1. Use un comparador de igualdad personalizado y luego úseloDistinct(new DistinctItemComparer()) como mencionó @Christian Hayter .
  2. Utilizar GroupBy , pero tenga en cuenta GroupByque debe Agrupar por todas las columnas porque si solo agrupa por Idél no siempre elimina los elementos duplicados. Por ejemplo, considere el siguiente ejemplo:

    List<Item> a = new List<Item>
    {
        new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
        new Item {Id = 2, Name = "Item2", Code = "IT00002", Price = 200},
        new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
        new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
        new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
        new Item {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}
    };
    var distinctItems = a.GroupBy(x => x.Id).Select(y => y.First());

    El resultado para esta agrupación será:

    {Id = 1, Name = "Item1", Code = "IT00001", Price = 100}
    {Id = 2, Name = "Item2", Code = "IT00002", Price = 200}
    {Id = 3, Name = "Item3", Code = "IT00003", Price = 150}

    Lo cual es incorrecto porque se considera {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}duplicado. Entonces la consulta correcta sería:

    var distinctItems = a.GroupBy(c => new { c.Id , c.Name , c.Code , c.Price})
                         .Select(c => c.First()).ToList();

    3. Anular EqualyGetHashCode en la clase de elemento:

    public class Item
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Code { get; set; }
        public int Price { get; set; }
    
        public override bool Equals(object obj)
        {
            if (!(obj is Item))
                return false;
            Item p = (Item)obj;
            return (p.Id == Id && p.Name == Name && p.Code == Code && p.Price == Price);
        }
        public override int GetHashCode()
        {
            return String.Format("{0}|{1}|{2}|{3}", Id, Name, Code, Price).GetHashCode();
        }
    }

    Entonces puedes usarlo así:

    var distinctItems = a.Distinct();
Salah Akbari
fuente
12

Un método de extensión universal:

public static class EnumerableExtensions
{
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> enumerable, Func<T, TKey> keySelector)
    {
        return enumerable.GroupBy(keySelector).Select(grp => grp.First());
    }
}

Ejemplo de uso:

var lstDst = lst.DistinctBy(item => item.Key);
TOL
fuente
Enfoque muy limpio
Steven Ryssaert
5

Prueba este método de extensión. Espero que esto pueda ayudar.

public static class DistinctHelper
{
    public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
    {
        var identifiedKeys = new HashSet<TKey>();
        return source.Where(element => identifiedKeys.Add(keySelector(element)));
    }
}

Uso:

var outputList = sourceList.DistinctBy(x => x.TargetProperty);
Kent Aguilar
fuente
3
List<Employee> employees = new List<Employee>()
{
    new Employee{Id =1,Name="AAAAA"}
    , new Employee{Id =2,Name="BBBBB"}
    , new Employee{Id =3,Name="AAAAA"}
    , new Employee{Id =4,Name="CCCCC"}
    , new Employee{Id =5,Name="AAAAA"}
};

List<Employee> duplicateEmployees = employees.Except(employees.GroupBy(i => i.Name)
                                             .Select(ss => ss.FirstOrDefault()))
                                            .ToList();
Arun Kumar
fuente
0

Otra solución alternativa, no hermosa compra viable.

Tengo un archivo XML con un elemento llamado "MEMDES" con dos atributos como "GRADE" y "SPD" para registrar la información del módulo RAM. Hay muchos artículos duplicados en SPD.

Así que aquí está el código que uso para eliminar los elementos duplicados:

        IEnumerable<XElement> MList =
            from RAMList in PREF.Descendants("MEMDES")
            where (string)RAMList.Attribute("GRADE") == "DDR4"
            select RAMList;

        List<string> sellist = new List<string>();

        foreach (var MEMList in MList)
        {
            sellist.Add((string)MEMList.Attribute("SPD").Value);
        }

        foreach (string slist in sellist.Distinct())
        {
            comboBox1.Items.Add(slist);
        }
Rex Hsu
fuente
-1

Cuando no desee escribir IEqualityComparer, puede intentar algo como lo siguiente.

 class Program
{

    private static void Main(string[] args)
    {

        var items = new List<Item>();
        items.Add(new Item {Id = 1, Name = "Item1"});
        items.Add(new Item {Id = 2, Name = "Item2"});
        items.Add(new Item {Id = 3, Name = "Item3"});

        //Duplicate item
        items.Add(new Item {Id = 4, Name = "Item4"});
        //Duplicate item
        items.Add(new Item {Id = 2, Name = "Item2"});

        items.Add(new Item {Id = 3, Name = "Item3"});

        var res = items.Select(i => new {i.Id, i.Name})
            .Distinct().Select(x => new Item {Id = x.Id, Name = x.Name}).ToList();

        // now res contains distinct records
    }



}


public class Item
{
    public int Id { get; set; }

    public string Name { get; set; }
}
Kundan Bhati
fuente