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.

¿Fue útil?

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 un Dictionary<T, int>
  • iterar a través de su Dictionary<T, int> eliminación de entradas en las que la int 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>();   

}

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top