Question

J'ai une liste <> d'objets en C # et je besoin d'un moyen de retourner ces objets qui sont considérés comme des doublons dans la liste. Je ne ai pas besoin ResultSet Distinct, je besoin d'une liste de ces articles que je vais supprimer de mon dépôt.

Par souci de cet exemple, permet de dire que j'ai une liste de types « voitures » et je dois savoir lequel de ces voitures sont la même couleur que l'autre dans la liste. Voici les voitures dans la liste et leur propriété de couleur:

Car1.Color = Red;

Car2.Color = Blue;

Car3.Color = Green;

Car4.Color = Red;

Car5.Color = Red;

Pour cet exemple, j'ai besoin du résultat (IEnumerable <>, Liste <>, ou autre) pour contenir voiture4 et voiture5 parce que je veux les supprimer de mon référentiel ou db afin que j'ai une seule voiture par couleur dans mon dépôt . Toute aide serait appréciée.

Était-ce utile?

La solution

par inadvertance codé hier, quand je tentais d'écrire une « distincte par une projection ». J'inclus un! quand je ne devrais pas avoir, mais cette fois il est juste:

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

Vous auriez alors appelez avec:

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

Autres conseils

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

groupes les annonces par couleur, puis saute le premier résultat de chaque groupe, retournant le reste de chaque groupe aplati en une seule séquence.

Si vous avez des exigences particulières au sujet de laquelle vous souhaitez conserver, par exemple si la voiture a une propriété et vous Id voulez garder la voiture avec le plus bas <=>, vous pouvez ajouter un peu de commande là-bas, par exemple.

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;

Voici une autre solution que légèrement Linq que je pense, il est plus évident que vous essayez de le faire:

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

Il est juste groupant les voitures par la couleur, jetant tous les groupes qui ont plus d'un élément, puis mettre le reste dans le IEnumerable retourné.

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

Cela signifie essentiellement « retour des voitures où il y a une voiture dans la liste avec la même couleur et un indice plus faible ».

Je ne sais pas de la performance, cependant. Je soupçonne une approche avec un O (1) recherche de doublons (comme la méthode de dictionnaire / HashSet) peut être plus rapide pour les grands ensembles.

Créer une nouvelle et Dictionary<Color, Car> foundColors List<Car> carsToDelete

Ensuite, vous itérer sur votre liste originale des voitures comme ceci:

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

Ensuite, vous pouvez supprimer toutes les voitures qui se trouve dans foundColors.

Vous pouvez obtenir un coup de pouce mineur de performance en mettant votre logique « supprime l'enregistrement » dans la déclaration au lieu de if créer une nouvelle liste, mais la façon dont vous LIBELLEES la question suggère que vous aviez besoin de les rassembler dans une liste.

Sans elle codage en fait, que diriez-vous quelque chose d'algorithme comme ceci:

  • itérer à travers votre création d'un List<T> Dictionary<T, int>
  • itérer vos entrées int où la suppression est Dictionary> 1

Tout ce qui reste dans le doublons a <=>. La deuxième partie où vous supprimez réellement est facultative, bien sûr. Vous pouvez simplement parcourir le chercher et <=> la> 1 de prendre des mesures.

EDIT: OK, je cogné Ryan depuis qu'il a effectivement donné votre code. ;)

Ma réponse se inspire (dans cet ordre) des répondants adeptes. Joe Coehoorn, Greg Beech et Jon Skeet

J'ai décidé de donner un exemple complet, l'hypothèse étant (pour une efficacité réelle de mot) que vous avez une liste statique de couleurs de voiture. Je crois que le code suivant illustre une solution complète au problème d'une manière élégante, mais pas nécessairement hyper-efficace,.

#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

Je vais revenir par ici plus tard et comparer cette solution à trois de ses inspirations, pour contraster les styles. Il est plutôt intéressant.

public static IQueryable Doublons (cette source IEnumerable) où TSource: IComparable {

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

}

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top