Domanda

Ho un List <> di oggetti in C # e ho bisogno di un modo per restituire quegli oggetti che sono considerati i duplicati all'interno della lista. Non ho bisogno di risultati Distinto, ho bisogno di una lista di quegli elementi che sarò Eliminazione dal mio repository.

Per il bene di questo esempio, consente di dire che ho un elenco dei tipi di "macchina" e ho bisogno di sapere quale di queste vetture sono dello stesso colore come un altro nella lista. Qui ci sono le vetture nella lista e le loro proprietà di colore:

Car1.Color = Red;

Car2.Color = Blue;

Car3.Color = Green;

Car4.Color = Red;

Car5.Color = Red;

Per questo esempio ho bisogno del risultato (IEnumerable <>, List <>, o qualsiasi altra cosa) per contenere car4 e Car5 perché voglio eliminare questi dal mio repository o db in modo da avere solo una macchina per colore nel mio repository . Qualsiasi aiuto sarebbe apprezzato.

È stato utile?

Soluzione

I inavvertitamente codificato ieri, quando cercavo di scrivere un "distinto da un risalto". Ho incluso un! quando non avrei dovuto, ma questa volta è solo a destra:

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;
        }
    }
}

Si potrebbe quindi chiamare con:

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

Altri suggerimenti

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

Questo raggruppa le automobili di colore e poi salta il primo risultato da ciascun gruppo, restituendo il resto da ciascun gruppo appiattito in una singola sequenza.

Se hai esigenze particolari su quale si desidera conservare, ad esempio, Se l'automobile è dotata di Id proprietà e si desidera mantenere l'auto con il <=> più basso, allora si potrebbe aggiungere un po 'ordinazione in là, per esempio.

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;

Ecco una soluzione Linq leggermente diversa che credo rende più evidente quello che stai cercando di fare:

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

E 'solo il raggruppamento vetture in base al colore, tirare fuori tutti i gruppi che hanno più di un elemento, e poi mettere il resto in IEnumerable restituito.

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

E 'fondamentalmente significa "ritorno vetture dove c'è alcuna macchina nella lista con lo stesso colore e un indice più piccolo".

Non sono sicuro delle prestazioni, però. Ho il sospetto che un approccio con un O (1) ricerca per i duplicati (come il dizionario / metodo di hashset) può essere più veloce per grandi insiemi.

Crea un nuovo Dictionary<Color, Car> foundColors e List<Car> carsToDelete

Poi eseguire iterazioni attraverso la vostra lista originale delle vetture in questo modo:

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

Quindi è possibile eliminare ogni macchina che è in foundColors.

Si potrebbe ottenere un incremento delle prestazioni minore mettendo la vostra logica "delete record" nel if dichiarazione invece di creare un nuovo elenco, ma il modo in cui si recita la domanda suggerito che avevi bisogno di raccoglierle in un elenco.

Senza in realtà codifica esso, come su di un algoritmo di qualcosa di simile a questo:

  • iterare attraverso il vostro List<T> la creazione di un Dictionary<T, int>
  • iterare attraverso le voci int eliminazione dove il Dictionary è> 1

Tutto ciò che a sinistra in <=> ha duplicati. La seconda parte in cui è effettivamente eliminato è facoltativo, naturalmente. Si può solo scorrere l'<=> e cercare il> 1 di agire.

EDIT: OK, ho sbattuto fino Ryan da quando in realtà ti ha dato il codice. ;)

La mia risposta si ispira (in questo ordine) da parte dei seguaci intervistati:. Joe Coehoorn, Greg Faggio e Jon Skeet

Ho deciso di fornire un esempio completo, con il presupposto di essere (per reale efficacia di parola) che si dispone di un elenco statico di colori auto. Credo che il codice seguente mostra una soluzione completa al problema in un elegante, anche se non necessariamente iper-efficiente, modo.

#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

ho intenzione di tornare di qui in seguito e confrontarlo con questa soluzione a tutte e tre le sue ispirazioni, solo per contrastare gli stili. E 'piuttosto interessante.

public static IQueryable duplicati (questa fonte IEnumerable) dove TSource: IComparable {

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

}

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top