![]() |
Dubikate in TStringGrid anzeigen (den Rest rausschmeißen)...
Ich glaube ich habe da mal eine kleine Denkblokade oder so. Normalerweise ist es ja so, dass man versucht Dublikate zu entfernen. Aber bei mir ist es andersrum, denn die Einträge, die nicht doppelt sind, sind in Ordung und die will ich aus dem StringGrid schmeißen. Genauer gesagt geht es nur um eine Spalte des StringGrids, die nur Integerwerte enthält und sortiert ist.
Es ist wahsrcheinlich total banal udn ich komme einfach nicht drauf :wall: . Also, wäre nett, wenn mir einer helfen würde *Hundeblick* |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Hallo
Du kannst dir die Cols-eigenschaft vom Grid zu Hilfe nehmen, sind auch tstrings. Durchlauf sie vom ende und prüf mit indexof, ob der Wert nochmal in der Spalte enthalten ist. Wenn indexof dir die aktuelle Position zurückgibt, ist der Eintrag nicht doppelt drin. Liefert indexof einen kleineren Wert, steht der gleiche Eintrag schon mal vorher in der Liste, dann löschen und nochmal suchen. Mfg Frank |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Hai yankee,
hier noch eine alternative Methode (zum testen habe ich eine ListBox genommen):
Delphi-Quellcode:
Ich durchlaufe von unten nach oben die Einträge von count -2 (vorletzter) bis 1 (zweiter) und prüfe ob der Eintrag davor oder danach gleich dem aktuellen ist. Wenn dies nicht zutrifft lösche ich den aktuellen.
procedure TForm1.Button1Click(Sender: TObject);
var ndx: integer; begin with ListBox1 do begin for ndx := Count - 2 downto 1 do begin if not ((Items[ndx] = Items[ndx + 1]) or (Items[ndx] = Items[ndx - 1])) then begin Items.Delete(ndx); end; end; // Schleifen ende if not (Items[0] = Items[1]) then // erster <> zweiter begin Items.Delete(0); end; if not (Items[Count-1] = Items[Count-2]) then // letzer <> vorletzer begin Items.Delete(Count-1); end; end; end; Ganz zum schluss prüfe ich noch ob der erste = dem zweiten ist bzw. der letze = dem vorletzten. Aber die Methode von Keldorn ist auch sehr elegant :stupid: |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Die von Frank beschriebene Methode kenne ich aus meiner code library:
Delphi-Quellcode:
Grüße vom marabu
procedure KeepDuplicates(sl: TStringList; allDuplicates: boolean = false);
var i, dist: integer; begin if not sl.Sorted then sl.Sort; for i := sl.Count - 1 downto 0 do begin dist := i - sl.IndexOf(sl[i]); if dist > i then Continue; if (dist = 0) or (not allDuplicates and (dist > 1)) then sl.Delete(i); end; end; |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Liste der Anhänge anzeigen (Anzahl: 1)
Hai marabu,
kann es sein das dein Code einen Fehler hat? Wenn ich diesen mit meiner Listbox verwende (vorher alles in eine SL kopier). stimmt das Ergebniss nicht mit der Aufgabe überein. :gruebel: Ich hänge mein Testprojekt mal an. |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Kann ich mich bei einem TStringList.IndexOf denn darauf verlassen, das bei doppeltem Vorkommen immer der erste Eintrag gefunden wird, vor allen Dingen, wenn die TStringList.sorted = True ist? Ich weiss, eine ketzterische Frage.
Um auf Nummer Sicher zu gehen, würde ich mir die Mühe machen, das IndexOf durch eine kleine eigene Routine ersetzt, die prüft, ob ein Wert in der Unterliste (0..n) vorhanden ist, oder nicht. Bei einer sortierten Liste (wie in deiner Codelibrary) liegen Duplikate naturgemäß nebeneinander, insofern erübrigt sich eine Suche mit IndexOf: Entweder ist mein Vorgänger mit mir identisch, oder eben nicht. Desweiteren sollten Duplikate nur 1x auftreten. Hier also mein Versuch (er erstellt aber eine zweite Liste, die die doppelten Einträge enthält):
Delphi-Quellcode:
Procedure FindDuplicates (aList, aDuplicates : TStringList);
Var i : Integer; sSample : String; Begin aList.Sort; i := 1; sSample := aList [0]; While i<aList.Count - 1 do begin j := i; While aList[i] = sSample do inc (i); if j< i - 1 then aDuplicates.add(sSample); sSample := aList[i]; End; End; |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Zitat:
Das mit dem sortieren geht sicher auch, aber es ist nicht unbedingt immer gewünscht, das nach dem Löschen der doppelten Einträge die Liste auch noch sortiert ist und die ursprüngliche Reihenfolge verloren ist. |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Sharky, du hast tatsächlich einen Fehler in meiner Routine gefunden und alzaimar hat ihn korrekt erklärt. Vielen Dank euch beiden.
Delphi-Quellcode:
Die Routine stammt aus einer Sammlung von Mengenoperationen auf Strings. Benutzt habe ich sie nie, aber der zugehörige Testcode erzeugte seine Testdaten so:
procedure KeepDuplicates(sl: TStringList; allDuplicates: boolean = false);
var i, j, dist: integer; begin if not sl.Sorted then sl.Sort; for i := sl.Count - 1 downto 0 do begin j := i; // "j := sl.IndexOf(sl[i])" würde einen sporadischen Fehler produzieren while (j > 0) and (sl[j - 1] = sl[j]) do Dec(j); dist := i - j; if (dist = 0) or (not allDuplicates and (dist > 1)) then sl.Delete(i); end; end;
Delphi-Quellcode:
Was soll ich sagen, damit produzierte der Code in den ersten Durchläufen korrekte Ergebnisse. Ich werde meinen Testcode wohl etwas verfeinern müssen...
for i := 1 to 20 do
sl.Add(IntToStr(Random(30))); Verlegene Grüße vom marabu PS: Fehler nach Hinweis von alzaimar korrigiert |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Wenn Du bei einer TStringList Sorted auf True setzt, dann wird beim IndexOf nicht sequentiell, sondern binär gesucht (binarysearch). Das findet aber nur irgendeinen Eintrag, aber nicht unbedingt den Ersten. Beispiel: Liste = (1,2,2,2,3). Suche nach 2 liefert im ersten Durchgang einen Treffer, nämlich l[2] ='2', das ist aber nicht das erste Auftreten. Deshalb ist die Verwendung des Suchergebnisses als Kriterium für doppelte Einträge bei sortierten Stringlisten (Sorted = True) nur bedingt anwendbar. Im Beispiel vom marabu umgeht er das, indem er von hinten an die Sache rangeht.
Noch eine Anmerkung zum marabu-Verfahren: Man kann die Zeile
Delphi-Quellcode:
durch
j := sl.IndexOf (sl[i]);
Delphi-Quellcode:
ersetzen, da die Liste sortiert ist und j danach sowieso auf das erste Element gesetzt wird.
j := i;
|
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Hallo alzaimar,
man KANN nicht nur IndexOf() durch die Gleichsetzung von j mit i ersetzen, man MUSS es sogar. Habe ich auch so in meiner library geändert und doch den falschen Code hier bereit gestellt. Muss unbedingt ein Seminar zum Thema Copy & Paste besuchen... Nochmal danke marabu |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
Zitat:
auch beim ersten Durschlauf war das Ergebniss falsch im Sinne der Anforderung ;-) |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
@marabu: Auch wenn's völlig unwichtig ist: Ich bleib dabei, man 'kann', muss aber nicht;
Sei j0 der kleinste Index mit sl[j0] = KEY zu irgendeinem KEY und j1 der größte Index. Ziel ist es, j0 zu finden. Ob ich bei j1 anfange (so wie ich mit meiner Änderung) oder bei einem beliebigen j (j0<=j<=j1), so wie Du mit dem IndexOf, ist unerheblich. Allerdings ist meine Version schneller, insofern kann man müssen, muss aber nicht. :wiejetzt: Ach, was ist das für ein Sonntag, an dem man sich über SOWAS auseinandersetzen kann. Dann geht's uns wohl doch ganz gut :zwinker: |
Re: Dubikate in TStringGrid anzeigen (den Rest rausschmeißen
@yankee: sorry, dein thread ist etwas entartet und ich bin wahrscheinlich der Schuldige. Hier der Code, der dein Problem lösen könnte. Er setzt die Idee von keldorn um, ohne den IndexOf()-Fehler. Hoffentlich korrekt:
Delphi-Quellcode:
@keldorn: Bin auch drauf hereingefallen - dabei habe ich sogar älteren Code bei mir gefunden, der die Falle umgeht.
procedure TDemoForm.DeleteRow(iRow: integer);
var i: integer; begin with StringGrid do begin if iRow < FixedRows then raise Exception.Create('you cannot delete a fixed row'); for i := Succ(iRow) to RowCount - 1 do Rows[i-1].Assign(Rows[i]); RowCount := RowCount - 1; end; end; procedure TDemoForm.KillButtonClick(Sender: TObject); var i, iDup, iCol, iDist: integer; allDuplicates: boolean; begin iCol := 1; // column to check allDuplicates := false; with StringGrid do for i := RowCount - 1 downto 0 do begin iDup := i; while (iDup > 0) and (cells[iCol, iDup - 1] = cells[iCol, iDup]) do Dec(iDup); iDist := i - iDup; if (iDist = 0) or (not allDuplicates and (iDist > 1)) then DeleteRow(i); end; end; @sharky: Bist ein guter Tester. Selbst die ersten 3000 mit dem gezeigten Random-Code erzeugten Testfälle werden von meiner ersten / fehlerhaften Prozedur korrekt verarbeitet. Da musstest erst du kommen mit deinen wahrscheinlich handgeschmiedeten Testdaten. @alzaimar: Hast heute als einziger so richtig den Überblick gehabt - kein Wunder, warst ja auch lange genug im Urlaub. Grüße vom marabu |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:18 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz