C# 3.0:Necesidad de volver duplicados de una Lista<>
-
20-08-2019 - |
Pregunta
Tengo una Lista<> de los objetos en C# y necesito una manera de devolver los objetos que se consideran duplicados dentro de la lista.No necesito las Distintas conjunto de resultados, necesito una lista de los puntos que voy a eliminar de mi repositorio.
Por el bien de este ejemplo, digamos que tengo una lista de "Coche" tipos y necesito saber cual de estos coches son del mismo color como otro en la lista.Aquí están los coches en la lista y el color de su propiedad:
Car1.Color = Red;
Car2.Color = Blue;
Car3.Color = Green;
Car4.Color = Red;
Car5.Color = Red;
Para este ejemplo yo necesito el resultado (IEnumerable<>, Lista de<>, o lo que sea) para contener Car4 y Car5 porque quiero eliminar estos desde mi repositorio o db, de modo que sólo tengo un coche por el color en mi repositorio.Cualquier ayuda se agradece.
Solución
Me inadvertidamente codificado esto ayer, cuando estaba tratando de escribir una "clara por una proyección". Incluí una! cuando no debería haberlo hecho, pero esta vez es la correcta:
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;
}
}
}
Se podría entonces llama con:
var duplicates = cars.DuplicatesBy(car => car.Color);
Otros consejos
var duplicates = from car in cars
group car by car.Color into grouped
from car in grouped.Skip(1)
select car;
Este grupos de los coches por color y luego se salta el primer resultado de cada grupo, volviendo el resto de cada grupo aplanado en una sola secuencia.
Si usted tiene requisitos particulares sobre las cuales uno desea mantener, por ejemplo, si el coche tiene un Id
propiedad y desea mantener el coche con el <=> más bajo, entonces se podría añadir un poco de orden en ese país, 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;
Aquí hay una solución ligeramente diferente LINQ que creo que hace que sea más evidente 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 sólo agrupación coches por el color, lanzando todos los grupos que tienen más de un elemento, y luego poner el resto en el IEnumerable devuelto.
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 "volver coches donde hay ningún coche en la lista con el mismo color y un índice más pequeño".
No está seguro de los resultados, sin embargo. Sospecho que un enfoque con un O (1) las operaciones de búsqueda de duplicados (como el diccionario de métodos / hashset) puede ser más rápido para conjuntos grandes.
Crear una nueva Dictionary<Color, Car> foundColors
y un List<Car> carsToDelete
A continuación, se itera a través de la lista original de los coches así:
foreach(Car c in listOfCars)
{
if (foundColors.containsKey(c.Color))
{
carsToDelete.Add(c);
}
else
{
foundColors.Add(c.Color, c);
}
}
A continuación, puede eliminar todos los autos en foundColors.
Usted podría conseguir un menor aumento de rendimiento por poner su "eliminar registro" de la lógica en la if
declaración en lugar de crear una nueva lista, pero la forma de redactar la pregunta sugiere que usted necesita para recoger en una Lista.
Sin llegar a la codificación de él, acerca de cómo un algoritmo algo como esto:
- iterar a través de su
List<T>
la creación de unDictionary<T, int>
- iterar a través de su
Dictionary<T, int>
eliminación de entradas en las que laint
es >1
Cualquier cosa a la izquierda en la Dictionary
ha duplicados.La segunda parte donde realmente eliminar es opcional, por supuesto.Usted sólo puede iterar a través de la Dictionary
y buscar la >1 para tomar medidas.
EDITAR:OK, he topado de Ryan, ya que él en realidad le dio el código.;)
Mi respuesta se inspira (en este orden) de los encuestados seguidores:. Joe Coehoorn, Greg Haya y Jon Skeet
decidí dar un ejemplo completo, con el supuesto de estar (de verdad la eficiencia palabra) que tiene una lista estática de colores de los coches. Creo que el siguiente código ilustra una solución completa al problema en un elegante, aunque no necesariamente hiper-eficiente, de manera.
#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 por aquí más tarde y comparar esta solución a los tres de sus inspiraciones, sólo para contrastar los estilos. Es bastante interesante.
public static IQueryable Duplicados (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>();
}