text: GroupBy - gruppieren von mehreren Elementen mit Kriterien - Datenbanken (inkl. ADO.NET) - C#- & .NET Programmierung - Entwickler-Ecke
  GroupBy - gruppieren von mehreren Elementen mit Kriterien
  Verfasst: Mi 02.01.19 03:22
  ich möchte mehrere Dateien nach mehreren Kriterien gruppieren lassen.
  Grundlage: Eine Liste von FileInfo Items.
  Der Benutzer kann aus mehreren Kriterien zum gruppieren wählen.
  Z.b. Name + Dateigröße + Erstelldatum (usw).
  Geschachtelte Gruppierung (nach Dateigröße, danach nach Name, danach nach Erstelldatum)
  // Dateigröße gruppieren
  _data = _data.GroupBy(t => t.File.Length).Where(t => t.Count() > 1).SelectMany(grp => grp.ToList()).ToList();
  // Erstelldatum gruppieren
  _data = _data.GroupBy(t => t.File.LastWriteTime).Where(t => t.Count() > 1).SelectMany(grp => grp.ToList()).ToList();
  Einmalige Gruppierung (nach Dateigröße + Dateiname)
  Wie kann ich jetzt gruppieren, damit eine "oder" Verbindung entsteht?
  Es sollen Dateien gruppiert werden, die entweder vom Namen oder aber von der Dateigröße übereinstimmen.
  Eine Gruppierung mittels anonymer Typen habe ich schon probiert, funktioniert nicht.
  Zuletzt bearbeitet von Chiyoko am Fr 04.01.19 21:31, insgesamt 1-mal bearbeitet
  Verfasst: Mi 02.01.19 09:53
  Dazu muß entweder deine Klasse die Equals-Methode überschrieben (s. Linq and grouping with custom object) oder aber du gibst dem GroupBy noch einen weiteren IEqualityComparer<TKey>-Parameter mit (GroupBy(IEnumerable<TSource>, Func<TSource,TKey>, IEqualityComparer<TKey>) -> Grouping elements in LINQ .NET using GroupBy and an EqualityComparer).
  Verfasst: Mi 02.01.19 15:09
  Entweder so, oder Du baust eine Methode, die sozusagen die "Basis-Query" zurück gibt:
  IEnumerable<IGrouping<object, Data>> GetGroupedQuery()
  if (GroupByFileLength)
  return _data.GroupBy(t => t.File.Length);
  if (GroupByFileLastWriteTime)
  return _data.GroupBy(t => t.File.LastWriteTime);
  _data = GetGroupedQuery().Where(t => t.Count() > 1).SelectMany(grp => grp.ToList()).ToList();
  Ist - je nachdem, wie man es betrachtet - nicht unbedingt die schönste Lösung, aber sie ist schnell und einfach gemacht.
  Wenn es mehrere, komplexere Möglichkeiten gibt, vielleicht sogar zusammengesetzte Kriterien, würde ich aber zu dem raten, was Th69 vorgeschlagen hat, damit bist Du weit flexibler.
  Verfasst: Mi 02.01.19 16:23
  Danke euch, das löst mein Problem.
  Verfasst: Fr 04.01.19 02:37
  Ich habe beide Varianten erfolgreich getestet.
  Allerdings gibt es noch ein Problem.
  GetHasCode wird ausgeführt , bevor Equals aufgerufen wird.
  Wie soll ich bei GetHashCode wissen, welche dynamischen Objekte , die ja erst bei Equals ein Ergebnis bekommen, welchen Wert ich da zurück geben soll?
  Denn eine Variante kann eine "oder" Bedingung enthalten.
  Entweder Dateiname oder Dateigröße kann gleich sein.
  Der HashCode kann aber nur einen Wert enthalten (für die ganze Klasse).
  Verfasst: Fr 04.01.19 02:59
  Warum kannst Du nicht vor Equals dafür sorgen, dass die Werte fest legen?
  Ist doch relativ egal, ob Du bis zum Aufruf von Equals wartest, oder schon vorher damit arbeitest?
  Verfasst: Fr 04.01.19 19:52
  Weil die Werte dynamisch sind.
  Bei einem Vergleich kann entweder die Dateigröße aber auch der Dateiname gleich sein (oder andere Werte).
  Verfasst: Fr 04.01.19 20:22
  Dann gib als HashCode immer den gleichen Wert zurück, egal für welches Objekt.
  Der HashCode muss nicht zwingend eindeutig sein, die große Aufgabe ist, dass der HashCode immer der selbe ist.
  Das heißt aber auch, dass die Methode im Zweifel, wenn die HashCodes gleich sind, noch Mal separat mit Equals prüfen muss.
  Wenn Du nun immer den selben Wert zurück gibst, zwingst Du die Methode dazu, immer mit Equals zu prüfen.
  Wenn Du stattdessen immer einen anderen Wert zurück gibst (z.B. Guid.NewGuid().GetHashCode()), dann wird Equals nie aufgerufen.
  Verfasst: Fr 04.01.19 21:21
  Ja, die Idee hatte ich auch schon, danke.
  War/Bin allerdings nicht so sicher gewesen, ob der Hashcode nicht auch für GroupBy (Dictionarys, Hashtables etc) benötigt wird.
  Ok, das erklärt, warum bei Variante 1 nur der Hashcode und bei Variante 2 Equals und Hashcode geprüft wird. Danke für die Informationen.
  Verfasst: Sa 05.01.19 04:28
  Im GroupBy wird definitiv beides genutzt, das hab ich selber getestet
  Dictionaries basieren intern auf einer HashTable, das impliziert ebenfalls, dass GetHashCode genutzt wird.
  Du kannst es auch einfach ausprobieren
  Verfasst: Sa 05.01.19 23:21
  Chiyoko hat folgendes geschrieben :
  Kommt mir etwas komisch vor. Generell solltest du ja eine Klasse haben, die deine Sachen repräsentiert. (File z.B.)
  Dann kannst du ja darin Equals() und GetHashcode() überschrieben, oder alternativ eine zweite Klasse coden, die beides bereitstellt (einen getrennten EqualityComparer)
  Es gilt immer die Regel, dass wenn Equals true liefert, auch die Hashcodes gleich sein müssen. Das heißt, du darfst im Hashcode auch nur ein subset der Properties verarbeiten, aber nicht mehr als Equals().
  Dann brauchst du vielleicht einen CompareFilesBySizeComparer und einen CompareFilesByNameComparer
  Was natürlich ebenso wichtig ist: Was auch immer du im Equals benutzt, darf während der sinnvollen Lebenszeit eines Objekts nicht verändert werden. Sonst brechen alle Hash-basierten Datenstrukturen zusammen.
  Also am besten benutzt du get-only Properties, die einmal im Konstruktor befüllt werden.
  Verfasst: So 06.01.19 17:00
  Das war nur eine Aufzählung. Bei Dictionarys wars mir bekannt aber Linq nutze ich noch nicht so lange.
  Wenn die Daten einmal eingelesen wurden, wird auch nichts mehr geändert. Die Klasse enthält nicht viel,
  u.a. eine FileInfo Instanz und ein par Vergleichswerte.
  Diese Dateien werden nun miteinander verglichen.
  Mein Ziel ist letztlich, doppelte Daten zu finden.
  Sobald GroupBy aufgerufen wird, sollen jeweils 2 Dateien verglichen werden.
  Diese beiden Dateien haben z.b. unterschiedliche Namen und eine gleiche Größe oder umgekehrt.
  Vergleiche ich nun beide Werte miteinander, so tritt Variante 1 in kraft bzw. ein einfacher Dateivergleich.
  Das funktioniert auch tadellos mit GetHash und Equals.
  Wenn aber entweder die Länge oder der Name gleich sein kann, so gibts ein Problem.
  Ich könnte natürlich die Daten auch mehrfach durchlaufen aber wozu?
  Deswegen habe ich eine dynamische Möglichkeit gesucht, eine "Oder"-Beziehung aufzubauen.
  Pro Property ein Comparer? Klingt etwas op, aber ich schaus mir an.
  Verfasst: So 06.01.19 17:47
  Das heißt, du würdest dein .Equals() gerne so schrieben?
  public override bool Equals(File other)
  return other.Name == this.name || other.Size == this.Size;
  Das wird nicht klappen, weil es nicht transitiv ist. Also wenn A == B gilt, und B == C, dann erwartet das GroupBy, dass auch A == C gilt.
  Wenn du Equals wie oben definierst, dann gilt das nicht.
  Insofern wirst du um die eigenen Comparer nicht drum herum kommen, vermute ich. Ich habe sowas auch schon mal geschrieben: