remove generic example c# linq lambda iequalitycomparer

generic - C#3.0: necesita devolver duplicados de una lista<>



remove duplicates list c# linq (8)

Crear un nuevo Dictionary<Color, Car> foundColors y una List<Car> carsToDelete

Luego iteras a través de tu lista original de autos de la siguiente manera:

foreach(Car c in listOfCars) { if (foundColors.containsKey(c.Color)) { carsToDelete.Add(c); } else { foundColors.Add(c.Color, c); } }

Luego puede eliminar cada automóvil que esté en foundColors.

Podría obtener un impulso de rendimiento menor al poner su lógica de "borrar registro" en la sentencia if lugar de crear una nueva lista, pero la forma en que redactó la pregunta sugería que debía recopilarlos en una lista.

Tengo una lista <> de objetos en C # y necesito una forma de devolver esos objetos que se consideran duplicados dentro de la lista. No necesito el conjunto de resultados Distinct, necesito una lista de los elementos que eliminaré de mi repositorio.

Por el bien de este ejemplo, digamos que tengo una lista de tipos de "Automóvil" y necesito saber cuáles de estos automóviles son del mismo color que otro de la lista. Aquí están los autos en la lista y su propiedad de color:

Car1.Color = Red; Car2.Color = Blue; Car3.Color = Green; Car4.Color = Red; Car5.Color = Red;

Para este ejemplo, necesito el resultado (IEnumerable <>, List <>, o lo que sea) para contener Car4 y Car5 porque quiero eliminarlos de mi repositorio o db para que solo tenga un carro por color en mi repositorio. Cualquier ayuda sería apreciada.


Inadvertidamente codifiqué esto ayer, cuando estaba tratando de escribir un "distinto por una proyección". ¡Incluí un! cuando no debería, pero esta vez es lo correcto:

public static IEnumerable<TSource> DuplicatesBy<TSource, TKey> (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) { HashSet<TKey> seenKeys = new HashSet<TKey>(); foreach (TSource element in source) { // Yield it if the key hasn''t actually been added - i.e. it // was already in the set if (!seenKeys.Add(keySelector(element))) { yield return element; } } }

Luego lo llamarías con:

var duplicates = cars.DuplicatesBy(car => car.Color);


Sin realmente codificarlo, ¿qué tal un algoritmo como este?

  • iterar a través de su List<T> creando un Dictionary<T, int>
  • iterar a través de su Dictionary<T, int> borrando entradas donde el int es> 1

Todo lo que queda en el Dictionary tiene duplicados. La segunda parte donde realmente se elimina es opcional, por supuesto. Puede simplemente iterar a través del Dictionary y buscar los> 1 para tomar medidas.

EDITAR: OK, subí el de Ryan porque él realmente te dio el código. ;)


var duplicates = from car in cars group car by car.Color into grouped from car in grouped.Skip(1) select car;

Esto agrupa los autos por color y luego omite el primer resultado de cada grupo, devolviendo el resto de cada grupo en una secuencia única.

Si tiene requisitos específicos sobre cuál quiere conservar, por ejemplo, si el automóvil tiene una propiedad Id y desea conservar el automóvil con el Id más bajo, entonces podría agregar allí algún orden, por ejemplo:

var duplicates = from car in cars group car by car.Color into grouped from car in grouped.OrderBy(c => c.Id).Skip(1) select car;


IEnumerable<Car> GetDuplicateColors(List<Car> cars) { return cars.Where(c => cars.Any(c2 => c2.Color == c.Color && cars.IndexOf(c2) < cars.IndexOf(c) ) ); }

Básicamente significa "autos devueltos donde hay un auto en la lista con el mismo color y un índice más pequeño".

No estoy seguro del rendimiento, sin embargo. Sospecho que un enfoque con una búsqueda O (1) para duplicados (como el método de diccionario / hashset) puede ser más rápido para conjuntos grandes.


Aquí hay una solución Linq ligeramente diferente que creo que hace más obvio lo que estás tratando de hacer:

var s = from car in cars group car by car.Color into g where g.Count() == 1 select g.First();

Es solo agrupar coches por color, tirando a todos los grupos que tienen más de un elemento y luego colocando el resto en el IEnumerable devuelto.


Mi respuesta toma inspiración (en este orden) de los seguidores que respondieron: Joe Coehoorn, Greg Beech y Jon Skeet.

Decidí proporcionar un ejemplo completo, asumiendo (para la eficacia de la palabra real) que tiene una lista estática de los colores del automóvil. Creo que el siguiente código ilustra una solución completa al problema de una manera elegante, aunque no necesariamente hiper eficiente.

#region SearchForNonDistinctMembersInAGenericListSample public static string[] carColors = new[]{"Red", "Blue", "Green"}; public static string[] carStyles = new[]{"Compact", "Sedan", "SUV", "Mini-Van", "Jeep"}; public class Car { public Car(){} public string Color { get; set; } public string Style { get; set; } } public static List<Car> SearchForNonDistinctMembersInAList() { // pass in cars normally, but declare here for brevity var cars = new List<Car>(5) { new Car(){Color=carColors[0], Style=carStyles[0]}, new Car(){Color=carColors[1],Style=carStyles[1]}, new Car(){Color=carColors[0],Style=carStyles[2]}, new Car(){Color=carColors[2],Style=carStyles[3]}, new Car(){Color=carColors[0],Style=carStyles[4]}}; List<Car> carDupes = new List<Car>(); for (int i = 0; i < carColors.Length; i++) { Func<Car,bool> dupeMatcher = c => c.Color == carColors[i]; int count = cars.Count<Car>(dupeMatcher); if (count > 1) // we have duplicates { foreach (Car dupe in cars.Where<Car>(dupeMatcher).Skip<Car>(1)) { carDupes.Add(dupe); } } } return carDupes; } #endregion

Voy a volver más tarde y comparar esta solución con sus tres inspiraciones, solo para contrastar los estilos. Es bastante interesante.


duplicados IQueryable estáticos públicos (esta fuente IEnumerable) donde TSource: IComparable {

if (source == null) throw new ArgumentNullException("source"); return source.Where(x => source.Count(y=>y.Equals(x)) > 1).AsQueryable<TSource>();

}