Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Zwei Listen filtern Denkproblem (https://www.delphipraxis.net/200808-zwei-listen-filtern-denkproblem.html)

DieDolly 28. Mai 2019 11:14

Zwei Listen filtern Denkproblem
 
Ich komme gerade nicht weiter weil ich einen Knoten im Kopf habe.
ich habe zwei Listen. Eine enthält Städtenamen und die andere enthält jede statt mit jeder Stadt mit Komma getrennt.

Zitat:

berlin
düsseldorf
köln
nürnberg
usw
und die andere Liste
Zitat:

berlin,düsseldorf
berlin,köln
berlin,nürnberg
(und alle auch noch einmal andersherum, also düsseldorf,berlin usw).
In der zweiten Liste können Städte sein (vor und nach dem Komma), die nicht in der ersten sind. Wie bekomme ich die da jetzt heraus? Ich habe gerade Probleme damit das in Schleifen zu packen und abzufragen ohne was falsches zu löschen.

Schokohase 28. Mai 2019 11:18

AW: Zwei Listen filtern Denkproblem
 
Du musst deine Problem in Teilprobleme zerlegen.

1. Aus einem String mit komma-getrennten Werten ein string-Array erstellen
2. Entfernen von Einträgen aus einem Array, wenn diese nicht in einem anderen Array enthalten sind
3. Aus einem String-Array wieder einen String mit den komma-getrennten Einträgen erstellen

Wo hast du Probleme?

DieDolly 28. Mai 2019 11:20

AW: Zwei Listen filtern Denkproblem
 
Zitat:

Aus einem String mit komma-getrennten Werten ein string-Array erstellen
Die sind schon in Klasseninstanzen und Records abgelegt. Ich kann auf alle Städte zugreifen.

Zitat:

Wo hast du Probleme?
Damit
Zitat:

2. Entfernen von Einträgen aus einem Array, wenn diese nicht in einem anderen Array enthalten sind

Arrays brauche ich nicht. Habe wie gesagt schon alles in Klassen und die erste Städteliste ist eine StringList.

Codehunter 28. Mai 2019 11:30

AW: Zwei Listen filtern Denkproblem
 
EDIT: Bitte ignorieren, habe einen Denkfehler drin.

Schokohase 28. Mai 2019 11:32

AW: Zwei Listen filtern Denkproblem
 
Zitat:

Zitat von DieDolly (Beitrag 1433248)
Arrays brauche ich nicht. Habe wie gesagt schon alles in Klassen und die erste Städteliste ist eine StringList.

Wenn du etwas flexibler wärst und abstrakter an die Sache herangehen würdest, dann könnte ich dir eine Lösung präsentieren.

Hier mal eine abstrakte Darstellung deines Problems aus Punkt 2:

Entferne alle Einträge aus Menge A die nicht in der Menge B enthalten sind.

So einfach, so schlicht, ... so trivial

DieDolly 28. Mai 2019 11:34

AW: Zwei Listen filtern Denkproblem
 
Ich möchte Städte aus Liste 2 löschen die nicht in Liste 1 sind.

Delphi-Quellcode:
Staedte1 := TStringList.Create;
 Staedte2 := TStringList.Create;
 try
  // Liste 1
  Staedte1.Add('berlin');
  Staedte1.Add('dusseldorf');
  Staedte1.Add('koln');
  Staedte1.Add('nurnberg');

  // Liste 2
  Staedte2.Add('berlin,berlin');
  Staedte2.Add('berlin,dusseldorf');
  Staedte2.Add('berlin,koln');
  Staedte2.Add('berlin,napoli');
  Staedte2.Add('berlin,nurnberg');

  Staedte2.Add('dusseldorf,berlin');
  Staedte2.Add('dusseldorf,dusseldorf');
  Staedte2.Add('dusseldorf,koln');
  Staedte2.Add('dusseldorf,napoli');
  Staedte2.Add('dusseldorf,nurnberg');

  Staedte2.Add('koln,berlin');
  Staedte2.Add('koln,dusseldorf');
  Staedte2.Add('koln,koln');
  Staedte2.Add('koln,napoli');
  Staedte2.Add('koln,nurnberg');

  Staedte2.Add('napoli,berlin');
  Staedte2.Add('napoli,dusseldorf');
  Staedte2.Add('napoli,koln');
  Staedte2.Add('napoli,napoli');
  Staedte2.Add('napoli,nurnberg');

  Staedte2.Add('nurnberg,berlin');
  Staedte2.Add('nurnberg,dusseldorf');
  Staedte2.Add('nurnberg,koln');
  Staedte2.Add('nurnberg,napoli');
  Staedte2.Add('nurnberg,nurnberg');



 finally
  Staedte1.Free;
  Staedte2.Free;
 end;
Bei diesem Beispiel sollen alle Einträge mit napoli aus Staedte2 entfernt werden.

Mein erster kläglicher Versuch
Delphi-Quellcode:
for i := 0 to Staedte1.Count - 1 do
   begin
    StadtListe1 := Staedte1.Strings[i];
    StadtGefunden := False;

    for j := Staedte2.Count - 1 downto 0 do
     begin
      Stadt1 := Staedte2.Strings[j].Split([','])[0];
      Stadt2 := Staedte2.Strings[j].Split([','])[1];

      StadtGefunden := (Stadt1 = StadtListe1) or (Stadt2 = StadtListe1);

      if StadtGefunden then
       Break;
     end;

    if not StadtGefunden then
     ShowMessage(StadtListe1);
   end;
Angezeigt bekomme ich nichts obwohl ich napoli sehen müsste.

Schokohase 28. Mai 2019 11:50

AW: Zwei Listen filtern Denkproblem
 
Wie gesagt, abstrakt formuliert kommt man auf so etwas
Delphi-Quellcode:
program StadtVerwaltung;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  System.Generics.Defaults,
  System.Generics.Collections;

type
  TArray = class(System.Generics.Collections.TArray)
  public
    class function &Intersect<T>(const A, B: array of T): TArray<T>; overload;
    class function &Intersect<T>(const A, B: array of T; const AComparer: IEqualityComparer<T>): TArray<T>; overload;
  end;

  { TArray }

class function TArray.Intersect<T>(const A, B: array of T; const AComparer: IEqualityComparer<T>): TArray<T>;
var
  vA, vB: T;
begin
  Result := [];
  for vA in A do
  begin
    for vB in B do
    begin
      if AComparer.Equals(vA, vB) then
      begin
        Result := Result + [vA];
        Break;
      end;
    end;
  end;
end;

class function TArray.Intersect<T>(const A, B: array of T): TArray<T>;
begin
  Result := Intersect<T>(A, B, TEqualityComparer<T>.Default);
end;

procedure Main;
var
  A, B, vBArr: TArray<string>;
  idx: Integer;
begin
  A := ['a', 'b', 'c', 'd'];
  B := ['a,b', 'a,e,d'];
  for idx := Low(B) to High(B) do
  begin
    vBArr := B[idx].Split([',']);
    vBArr := TArray.Intersect<string>(vBArr, A);
    B[idx] := String.Join(',', vBArr);
  end;
end;

begin
  try
    { TODO -oUser -cConsole Main : Code hier einfügen }
    Main;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;

end.
PS Ja, ich weiß, dass es sich hier nicht wirklich um die Schnittmenge handelt und somit die Methode anders benannt werden müsste

DieDolly 28. Mai 2019 11:53

AW: Zwei Listen filtern Denkproblem
 
Danke für die Hilfe aber das hilft mir alle nicht weiter. Das ist wie mit Kanonen auf Spatzen schießen.

Hier sehe ich am Ende dusseldorf obwohl es in der Liste1 ist. Was ich sehen muss ist napoli damit alles was napoli beinhaltet raus kann.
Delphi-Quellcode:
Staedte1 := TStringList.Create;
 Staedte2 := TStringList.Create;
 try
  // Liste 1
  Staedte1.Add('berlin');
  Staedte1.Add('dusseldorf');

  // Liste 2
  Staedte2.Add('berlin,berlin');
  Staedte2.Add('berlin,dusseldorf');
  Staedte2.Add('berlin,napoli');

  Staedte2.Add('dusseldorf,berlin');
  Staedte2.Add('dusseldorf,dusseldorf');
  Staedte2.Add('dusseldorf,napoli');

  Staedte2.Add('napoli,berlin');
  Staedte2.Add('napoli,dusseldorf');
  Staedte2.Add('napoli,napoli');

  for j := Staedte2.Count - 1 downto 0 do
   begin
    Stadt1 := Staedte2.Strings[j].Split([','])[0];
    Stadt2 := Staedte2.Strings[j].Split([','])[1];
    LoescheStadt := False;

    for i := 0 to Staedte1.Count - 1 do
     begin
      StadtListe1 := Staedte1.Strings[i];

      LoescheStadt := (StadtListe1 <> Stadt1) and (StadtListe1 <> Stadt2);

      if LoescheStadt then
       Break;
     end;

    if LoescheStadt then
     Staedte2.Delete(j);
   end;

  ShowMessage(Staedte2.Text);
 finally
  Staedte1.Free;
  Staedte2.Free;
 end;

Klaus01 28. Mai 2019 12:13

AW: Zwei Listen filtern Denkproblem
 
.. vielleicht ungefähr so, über eine dritte Liste.

Delphi-Quellcode:
  for i:=0 to staedte1.count -1 do
    for j:= 0 to staedte1.count -1 do
      begin
         idx := stadte2.indexOf(staedte1[i]+','+staedte1[j]);
         if idx > -1 then
           begin
             staedteTemp.add(staedte2[idx]);
             staedte2.delete(idx);
           end;
      end;

Grüße
Klaus

DieDolly 28. Mai 2019 12:16

AW: Zwei Listen filtern Denkproblem
 
Eine dritte Liste kann ich leider nicht anlegen. Alle notwendigen Informationen habe ich schon in einer unveränderbaren StringList und einer generischen TObjectList aus der herausgelöscht werden soll.

DeddyH 28. Mai 2019 12:17

AW: Zwei Listen filtern Denkproblem
 
Delphi-Quellcode:
 for j := Staedte2.Count - 1 downto 0 do
   begin
    Stadt1 := Staedte2.Strings[j].Split([','])[0];
    Stadt2 := Staedte2.Strings[j].Split([','])[1];
    if (Staedte1.IndexOf(Stadt1) < 0) or (Staedte1.IndexOf(Stadt2) < 0) then
      Staedte2.Delete(j);
   end;
Ungetestet und auch nicht wirklich performant, müsste aber tun.

DieDolly 28. Mai 2019 12:23

AW: Zwei Listen filtern Denkproblem
 
Eine kurze Lösung und die verstehe ich zum Glück auch. Ich führe das nur ein mal aus. Es dauert zwar eine ganze Sekunde aber ich glaube das ist verkraftbar.
Schneller wär immer schön aber damit bin ich erst einmal zufrieden.

Wäre es schneller die StringList durch eine TObjectList mit Klasseninstanzen zu ersetzen wo jede Klasse nur ein FCity: string hat?
Die THashedStringList ist zwar schon etwas schneller aber auch nicht viel.

Moombas 28. Mai 2019 12:42

AW: Zwei Listen filtern Denkproblem
 
Wieso nicht ungefähr so:

Delphi-Quellcode:
//Erst der Befehl zum lesen der einzelnen Städte
//dann folgende if abfrage
if Stadte1.indexof('zu suchender Stadtname') = -1 then
begin
  //Hier der Code zum löschen der Stadte in der Staedte2 StL
end;
XD zu lange noch nebenbei was anderes gemacht^^ DeddyH war daher schneller und hat dei komplette Lösung^^

DieDolly 28. Mai 2019 13:01

AW: Zwei Listen filtern Denkproblem
 
Ich hatte auch einen zweiten Denkfehler drin. ich hatte um das IndexOf noch eine schleife die durch die StringList geht. Deswegen dauerte das auch 1 Sekunde.

Uwe Raabe 28. Mai 2019 13:02

AW: Zwei Listen filtern Denkproblem
 
Zitat:

Zitat von DieDolly (Beitrag 1433260)
Wäre es schneller die StringList durch eine TObjectList mit Klasseninstanzen zu ersetzen wo jede Klasse nur ein FCity: string hat?

Nein, das wäre vermutlich nicht schneller. Die Staedte1 sortieren könnte vielleicht was bringen. Auch der doppelte Split-Aufruf könnte in einer lokalen Variable gecached werden.
Delphi-Quellcode:
var
  dummy: Integer;
  tempArr: TArray<string>;
...
    Staedte1.Sorted := True;
    for j := Staedte2.Count - 1 downto 0 do
    begin
      tempArr := Staedte2[j].Split([',']);
      Stadt1 := tempArr[0];
      Stadt2 := tempArr[1];
      if not Staedte1.Find(Stadt1, dummy) or not Staedte1.Find(Stadt2) then
        Staedte2.Delete(j);
    end;
Wenn man auf das Split verzichten will, kann man das alternativ auch mit dem NameValueSeparator erreichen:
Delphi-Quellcode:
    Staedte1.Sorted := True;
    Staedte2.NameValueSeparator := ',';
    for j := Staedte2.Count - 1 downto 0 do
    begin
      Stadt1 := Staedte2.Names[j];
      Stadt2 := Staedte2.ValueFromIndex[j];
      if not Staedte1.Find(Stadt1, dummy) or not Staedte1.Find(Stadt2, dummy) then
        Staedte2.Delete(j);
    end;

Luckie 28. Mai 2019 13:04

AW: Zwei Listen filtern Denkproblem
 
Zitat:

Zitat von DieDolly (Beitrag 1433258)
Eine dritte Liste kann ich leider nicht anlegen.

du wirst doch im Code eine temporäre dritte Liste anlegen können? Du kannst ja anscheinen auch Variablen anlegen. :roll:

freimatz 28. Mai 2019 15:08

AW: Zwei Listen filtern Denkproblem
 
Zitat:

Zitat von DieDolly (Beitrag 1433256)
Danke für die Hilfe aber das hilft mir alle nicht weiter. Das ist wie mit Kanonen auf Spatzen schießen.

Ey warum denn? Soll nicht der Spatz tot sein? Wenn das mit einer Kanone geht und du die hast, warum nicht. Und im Gegensatz zur Kanone gibt es bei der Lösung von Schokohase keinen Kollateralschaden.

DieDolly 28. Mai 2019 17:06

AW: Zwei Listen filtern Denkproblem
 
Eine Andere Idee hatte ich eben. Ich habe der Klasse der zweiten Liste ein Property verpasst was auf True gesetzt wird, wenn der Eintrag ignoriert werden soll.
Damit fällt das Löschen schon einmal weg. Ansonsten nutze ich jetzt die HashedStringList aus TIniFiles. Das dauert jetzt alles keinen Wimpernschlag mehr.


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:07 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz