Autor Beitrag
Chiyoko
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 298
Erhaltene Danke: 8

Win 98, Win Xp, Win 10
C# / C (VS 2019)
BeitragVerfasst: Mi 02.01.19 02:22 
Hallo,

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).

Variante 1:
Geschachtelte Gruppierung (nach Dateigröße, danach nach Name, danach nach Erstelldatum)
Funktioniert.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
// 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();


Variante 2:
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.

EDIT:
Eine Gruppierung mittels anonymer Typen habe ich schon probiert, funktioniert nicht.


Zuletzt bearbeitet von Chiyoko am Fr 04.01.19 20:31, insgesamt 1-mal bearbeitet
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4791
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 02.01.19 08: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).
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Mi 02.01.19 14:09 
Entweder so, oder Du baust eine Methode, die sozusagen die "Basis-Query" zurück gibt:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
IEnumerable<IGrouping<object, Data>> GetGroupedQuery()
{
    if (GroupByFileLength)
        return _data.GroupBy(t => t.File.Length);

    if (GroupByFileLastWriteTime)
        return _data.GroupBy(t => t.File.LastWriteTime);

    throw new Exception();
}

_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.
Chiyoko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 298
Erhaltene Danke: 8

Win 98, Win Xp, Win 10
C# / C (VS 2019)
BeitragVerfasst: Mi 02.01.19 15:23 
Danke euch, das löst mein Problem.
Chiyoko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 298
Erhaltene Danke: 8

Win 98, Win Xp, Win 10
C# / C (VS 2019)
BeitragVerfasst: Fr 04.01.19 01: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).
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Fr 04.01.19 01: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?
Chiyoko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 298
Erhaltene Danke: 8

Win 98, Win Xp, Win 10
C# / C (VS 2019)
BeitragVerfasst: Fr 04.01.19 18:52 
Weil die Werte dynamisch sind.
Bei einem Vergleich kann entweder die Dateigröße aber auch der Dateiname gleich sein (oder andere Werte).
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Fr 04.01.19 19: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.
Chiyoko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 298
Erhaltene Danke: 8

Win 98, Win Xp, Win 10
C# / C (VS 2019)
BeitragVerfasst: Fr 04.01.19 20: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.
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Sa 05.01.19 03: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 ;)
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Sa 05.01.19 22:21 
user profile iconChiyoko hat folgendes geschrieben Zum zitierten Posting springen:
Ich habe beide Varianten erfolgreich getestet.
Entweder Dateiname oder Dateigröße kann gleich sein.
Der HashCode kann aber nur einen Wert enthalten (für die ganze Klasse).

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().

user profile iconChiyoko hat folgendes geschrieben Zum zitierten Posting springen:
Weil die Werte dynamisch sind.
Bei einem Vergleich kann entweder die Dateigröße aber auch der Dateiname gleich sein (oder andere Werte).

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.
Chiyoko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 298
Erhaltene Danke: 8

Win 98, Win Xp, Win 10
C# / C (VS 2019)
BeitragVerfasst: So 06.01.19 16:00 
@Palladin007
Das war nur eine Aufzählung. Bei Dictionarys wars mir bekannt aber Linq nutze ich noch nicht so lange. :D


@jfheins

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.
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: So 06.01.19 16:47 
Das heißt, du würdest dein .Equals() gerne so schrieben?
ausblenden C#-Quelltext
1:
2:
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:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
  internal class StateIdentical : EqualityComparer<State>
  {
    public override bool Equals(State a, State b)
    {
      if (a == null && b == null)
        return true;
      if (a == null || b == null)
        return false;

      return a.Items.SequenceEqual(b.Items);
    }

    public override int GetHashCode(State x)
    {
      return HashCode.Combine(x.Items);
    }
  }
Chiyoko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 298
Erhaltene Danke: 8

Win 98, Win Xp, Win 10
C# / C (VS 2019)
BeitragVerfasst: Do 24.01.19 23:13 
Ich habe mich für die Variante von Palladin007 entschieden und per Except zusammengeführt.
Nicht sonderlich schön, ist aber einmalig und löst mein Problem.

Danke für die Vorschläge.