Frage

Ich habe eine Liste <> von Objekten in C # und ich brauche eine Möglichkeit, diese Objekte zurückzugeben, die Duplikate in der Liste berücksichtigt werden. Ich habe nicht die Distinct resultset brauche, ich brauche eine Liste der Gegenstände, die ich aus meinem Repository werden gelöscht werden.

Aus Gründen der diesem Beispiel können sagen, dass ich eine Liste von „Auto“ -Typen haben und ich muss wissen, welche dieser Autos sind die gleiche Farbe wie der andere in der Liste. Hier sind die Autos in der Liste und ihre Farbe Eigenschaft:

Car1.Color = Red;

Car2.Color = Blue;

Car3.Color = Green;

Car4.Color = Red;

Car5.Color = Red;

Für dieses Beispiel muss ich das Ergebnis (IEnumerable <>, List <>, oder was auch immer) car4 und car5 enthalten, weil ich diese aus meinem Repository oder db löschen möchten, so dass ich nur pro Farbe in meinem Repository ein Auto haben . Jede mögliche Hilfe würde geschätzt.

War es hilfreich?

Lösung

ich dies gestern versehentlich codiert, wenn ich eine „distinct durch einen Vorsprung“ zu schreiben versuchte. I enthalten ein! wenn ich nicht haben sollte, aber dieses Mal ist es genau richtig:

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

Sie würden dann rufen Sie es mit:

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

Andere Tipps

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

Diese Gruppen die Wagen durch Farbe und dann überspringt das erste Ergebnis aus jeder Gruppe, wobei der Rest der Rückkehr von jeder Gruppe zu einer einzigen Sequenz abgeflacht.

Wenn Sie besondere Anforderungen, über die man Sie behalten möchten, zum Beispiel wenn das Auto eine Id Eigenschaft hat und Sie wollen mit dem niedrigsten Id das Auto zu halten, dann könnte man eine gewisse Ordnung in dort hinzufügen, z.

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;

Hier ist eine etwas andere Linq-Lösung, die ich denke, macht es offensichtlich, was Sie zu tun versuchen:

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

Es ist die Gruppierung nur Autos durch Farbe, alle Gruppen wirft heraus, dass mehr als ein Element hat, und dann den Rest in den zurück IEnumerable setzen.

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

Im Grunde bedeutet es „Autos zurück, wo jedes Auto gibt es in der Liste mit der gleichen Farbe und ein kleinerer Index“.

Nicht der Leistung sicher, though. Ich vermute, dass ein Ansatz mit einem O (1) Lookup nach Duplikaten (wie das Wörterbuch / Hashset Methode) für große Mengen schneller sein.

Erstellen Sie eine neue Dictionary<Color, Car> foundColors und ein List<Car> carsToDelete

Dann iterieren Sie durch Ihre ursprüngliche Liste von Autos wie so:

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

Dann können Sie jedes Auto löschen, die in foundColors ist.

Sie könnten eine kleine Leistungssteigerung erhalten, indem Sie Ihre „Datensatz löschen“ Logik in der if Anweisung statt der Schaffung einer neuen Liste setzen, aber die Art und Weise Sie die Frage formuliert vorgeschlagen, dass Sie sie in einer Liste zu sammeln benötigt.

Ohne sie tatsächlich Codierung, wie etwa einen Algorithmus etwas wie folgt aus:

  • durchlaufen Ihre List<T> eine Dictionary<T, int> Erstellen
  • durchlaufen Ihre Dictionary<T, int> Löschen von Einträgen in dem die int> 1

Alles, was in der Dictionary übrig bleiben Duplikate. Der zweite Teil, wo man tatsächlich löschen ist optional, natürlich. Sie können nur durch die Dictionary laufen und suchen Sie nach der> 1 ist, Maßnahmen zu ergreifen.

EDIT: OK, ich gestoßen Ryans da er tatsächlich von Code gab. ;)

Meine Antwort ist inspiriert (in dieser Reihenfolge) von den Anhängern Befragten. Joe Coehoorn, Greg Beech und Jon Skeet

Ich beschloss, ein vollständiges Beispiel zu schaffen, mit der Annahme zu sein (für echte Wort Effizienz), die eine statische Liste mit Auto Farben haben. Ich glaube, dass der folgende Code eine komplette Lösung für das Problem in einem eleganten zeigt, wenn auch nicht unbedingt hypereffiziente Art und Weise.

#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

Ich werde später durch hier wieder kommen und diese Lösung für alle drei seine Inspirationen vergleichen, um nur die Stile kontrastiert. Es ist ziemlich interessant.

public static IQueryable Dubletten (diese IEnumerable Quelle), wo TSource: IComparable {

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

}

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top