Avatar billede dallan2007 Nybegynder
03. juni 2009 - 13:56 Der er 15 kommentarer og
1 løsning

Mange-dimensionelt stringlist?

Umm... ikke sikker på at den overskrift er specielt dækkende, men kan det lade sig gøre, at påsætte mere data udfor hvert "felt" i en stringlist? Eller skal man bruge records i stedet?

Eksempel

Jeg har følgende data stående i en database:

Peter | Hansen | 010175 | post@mail.dk
Anders | Nielsen | 020175 | tralala@mail.dk
Marie | Sørensen | 010575 | marie@mail.dk

På nuværende tidspunkt trækker jeg dem ud til en stringlist som en sammensat streng e.g. "Peter:Hansen:010175:post@mail.dk" for derefter at kunne lave en meget simpel sortering via stringlists egen sort-procedure. Det virker såmænd fint nok, men måske det var bedre at trække data'en ud til individuelle felter/poster som f.eks. et record:

Klient.Fornavn := Peter
Klient.Efternavn := Hansen
Osv.

Problemet er så bare, at jeg ikke aner hvordan man sortere et record baseret på fornavn således at de resterende data ""følger med" so to speak. Mine stringlist-strenge er lidt kluderet, men alt hænger sammen.

Er der en smartere måde at gøre det? Bevars, jeg kunne godt sortere alt fra starten i nogle individuelle stringlists eller arrays, men det giver nok mere bøvl end en enkel streng.
Avatar billede martinlind Nybegynder
03. juni 2009 - 14:06 #1
du kan bruge Objects delen på din stringlist, evt. kan du lægge en record i Mystringlist.Object[X] := ^MyRecord
Avatar billede dallan2007 Nybegynder
03. juni 2009 - 14:28 #2
Det kan jeg vist ikke få til at virker. Aner ikke hvordan jeg kan tilgå SORT på stringlist når den så indeholder objekter.
Avatar billede martinlind Nybegynder
03. juni 2009 - 14:37 #3
Mystringlist.Strings[X] := 'Svend';
Mystringlist.Object[X] := ^MyRecord;

Så klarer MyStringList.Sort resten :-)
Objects[x] følger Strings[x] så det er lige ud af landevejen
Avatar billede dallan2007 Nybegynder
03. juni 2009 - 14:43 #4
Men skal jeg så ikke bruge et array af records?
Avatar billede dallan2007 Nybegynder
03. juni 2009 - 14:49 #5
Kan jeg få dig til at lave et simpelt eksempel?... min hjerne er smeltet sammen.
Avatar billede martinlind Nybegynder
03. juni 2009 - 14:50 #6
Type
pMyRec=^MyRec
MyRec=Record
        Felt1 : string;
        felt2 : integer;
      end;


VAR
  Buf : pMyRec;

begin
  MyList[0] := 'Ole';
  New(Buf);
  Buf^.Felt1 := 'Test1';
  MyList.Objects[0] := Buf;

  MyList[1] := 'Bole';
  New(Buf);
  Buf^.Felt1 := 'Test 2';
  MyList.Objects[1] := Buf;
end;
Avatar billede hrc Mester
03. juni 2009 - 15:05 #7
Hvorfor (og jeg har skrevet det mange gange her) ikke bare lave en TPersonList (TObjectList) og fodre den med TPersonData?

TPersonData = class
private
  fNavn: string;
  fAdresse: string;
  ...
public
  constructor Create(const aNavn, aAdresse, aTelefon, aEMail: string);
  property Navn: string read fNavn { evt. write fNavn }
  ..
end;

TPersonList = class(TObjectList)
private
  function GetItems(const aIndex: integer): TPersonData;
public
  property Items[const aIndex: integer]: TPersonData read GetItems; default;

  procedure SortByNavn;
  procedure SortByAdresse;
  ..
end;

implementation

{ TPersonData }

constructor TPersonData.Create(const aNavn, aAdresse, aTelefon, aEMail: string);
begin
  inherited Create;
  fNavn := aNavn;
  fAdresse := aAdresse;
  fTelefon := aTelefon;
  fEMail := aEMail;
end;

{ TPersonList }

function TPersonList.GetItems(const aIndex: integer): TPersonData;
begin
  result := inherited Items[aIndex] as TPersonData;
end;

function CompareByNavn(aP1, aP2: pointer): integer;
var
  PD1, PD2: TPersonData;
begin
  result := CompareStr(PD1.Navn,PD2.Navn);
  if result = 0 then // Ens, sortér på adresse
    result := CompareStr(PD1.Adresse,PD2.Adresse);
end;

procedure TPersonList.SortByNavn;
begin
  Sort(CompareByNavn);
end;

function CompareByAdresse(aP1, aP2: pointer): integer;
var
  PD1, PD2: TPersonData;
begin
  result := CompareStr(PD1.Adresse,PD2.Adresse);
  if result = 0 then // Ens, sortér på navn
    result := CompareStr(PD1.Navn,PD2.Navn);
end;

procedure TPersonList.SortByAdresse;
begin
  Sort(CompareByAdresse);
end;

end;

Der er mange fordele ved at lave sådan en liste. Du kan fylde data ind i den via listen hvorved det hele bliver pænt og objektorienteret.

Hvis jeg ellers kunne finde ud af at søge på den nye "Ekspert" så ville jeg have lagt et link til et af mine tidligere eksempler.
Avatar billede dallan2007 Nybegynder
03. juni 2009 - 15:13 #8
Heh. Ser pænt ud hrc, men det holder jeg mig vist fra.
Avatar billede hrc Mester
03. juni 2009 - 15:14 #9
Eksemplet føres lidt videre:

  TfrmMain = class(TForm)
    procedure OnCreate(aOwner: TObject);
    procedure OnDestroy(aOwner: TObject);
    procedure OnButtonClick(aOwner: TObject);
  private
    fPersonList: TPersonList;
  end;

implementation

{ TfrmMain }

procedure TfrmMain.OnCreate(aOwner: TObject);
begin
  fPersonList := TPersonList.Create;
  fPersonList.Initialize; // Fyld personer i (via SQL eller lign.)
end;

procedure TfrmMain.OnDestroy(aOwner: TObject);
begin
  fPersonList.Free; // Frigiver også objekter i listen
end;

procedure TfrmMain.OnButtonClick(aOwner: TObject);
var
  i: integer;
  PersonData: TPersonData;
begin
  for i := 0 to fPersonList.Count - 1 do
  begin
    PersonData := fPersonList[i];
    if SameText(PersonData.Navn,'Hans Clausen') then
    begin
      // Gør et eller andet med data
    end;
  end;
end;

Jeg synes det er en hammernem og meget læselig måde at lave det på - og jeg har fuld styr over hvad der sker med data.
Avatar billede hrc Mester
03. juni 2009 - 15:15 #10
Forstår ikke hvorfor du ikke bare bruger det. Det er næsten et færdigt eksempel.
Avatar billede martinlind Nybegynder
03. juni 2009 - 15:19 #11
hrc >> man skal jo kravle før man kan gå :-) ikk', jeg startede også med stringlist, og har tit valgt den til mange ting, men du har da helt ret, den OOP-rigtige måde er din, og i den udvidede udgave nedarver du dit persondata object fra TPersistent, så du får streaming med, uden at skule kode extra :-)
Avatar billede hrc Mester
03. juni 2009 - 15:27 #12
dallan2007: Jeg bliver ærlig talt en lille smule harm over, at eksemplet forkastes uden at der kigges ordentligt på det. Jeg bruger noget der hedder "objektorienteret programmering" (ironi) og viser det i et meget simpelt eksempel.

Jeg har ikke vist hvordan du laver en "initialize" som fylder listen med personer vha. SQL, men ellers er alt på plads. Der er sortering på navn og adresse og du kan bare føje felter til.

Var blevet glad om du havde spurt om det du ikke forstod. Jeg biddrog til dit spørgsmål for at gøre dig til en bedre programmør ved at vise en god løsning, ikke fordi jeg ville spilde min tid.

Martin: Jeg er har bevist ikke nedarvet data-klassen fra TPersitent, men det gør jeg de få steder hvor det kan gøres. Mange gange skal man kigge lidt på data og måske vælge noget fra - og så er jeg tilbage ved TReader og TWriter.
Avatar billede dallan2007 Nybegynder
04. juni 2009 - 09:55 #13
hrc, det burde fremgå tydeligt at jeg ikke just en nogen delphi-ekspert, og at dit eksempel er alt for kompliceret for mig et implementere. Du skriver det i en separat klasse som jeg ikke aner hvordan jeg bruger, forbinder til eller anvender.

Jeg er meget visuel i min forståelse så oop har altid være problematisk for mig - for abstrakt.
Avatar billede hrc Mester
04. juni 2009 - 11:44 #14
Det her er ikke spor svært, hold nu op. Det er end ikke abstrakt. Jeg har jo vist dig hvordan den bruges. Har næsten skrevet hele programmet! Det er derfor der er mere kode end du forventede.

Du bruger konstruktionen som var det en stringlist, men der hvor du før havde en streng, har du de forskellige person-felter (navn, adresse osv), eksempelvis fPersonList[5].Navn for at se navnet på den sjette person. Det hele er splittet op sådan som du vil have det.

Hvis du vil fylde navnene i et listbox (her ListBox1) sker det sådan her. Lav en FillList procedure i fPersonList:

procedure TPersonList.FillList(aList: TStrings);
var
  i: integer;
  PersonData: TPersonData;
begin
  aList.BeginUpdate;
  try
    aList.Clear;
    for i := 0 to fPersonList.Count - 1 do
    begin
      PersonData := fPersonList[i];
//      aList.AddObject(PersonData.Navn, PersonData); // den her er bedre
      aList.Add(PersonData.Navn);
    end;
  finally
    aList.EndUpdate;
  end;
end;

I programmet fylder du data i din Listbox sådan her: fPersonList.FillList(ListBox.Items);

-------------------- o --------------------

Hvis ikke du kan finde ud af at oprette et objekt, hvordan opretter du så stringlisten du p.t. bruger?


Tager man udgangspunkt i du laver en TPersonList.Initialize som foretager opslaget i databasen, så vil den se nogenlunde sådan her ud:

procedure TPersonList.Initialize;
var
  Query: TADOQuery;
  PersonData: TPersonData;
begin
  // DM er det datamodul hvorpå jeg har min forbindelse til databasen
  // liggende (en TADOConnection)

  Query := DM.CreateQuery; // Opretter og knytter til database
  try
    Clear;
    Query.SQL.Clear;
    Query.SQL.Add('select navn, adresse, telefon, email');
    Query.SQL.Add('from person');

    // SaveQueryToFile(Query,'c:\personlist.sql'); // for debug - ikke med her

    while not Query.Eof do
    begin
      PersonData := TPersonData.Create(Query.FieldByName('navn').AsString,
                                      Query.FieldByName('adresse').AsString,
                                      Query.FieldByName('telefon').AsString,
                                      Query.FieldByName('email').AsString);
      Add(PersonData);
      Query.Next;
    end;
  finally
    Query.Free;
  end;
end;

// Viser lige denne her så du ikke skal lede efter den (det er noget jeg har lavet)

function TDM.CreateQuery: TADOQuery;
begin
  result := TADOQuery.Create(nil);
  result.Connection := ADOConnection;
end;
Avatar billede friiiiis Novice
14. juni 2009 - 22:39 #15
hrc>> ...sej kodning - og rigtig godt forklaret - flot at du tager dig tid til den slags... Ovenstående lærte jeg en del af...
Avatar billede hrc Mester
16. juni 2009 - 11:38 #16
fri*ns: Tak. Ros fortjener et ordentligt svar.

Hvis du søger i mine indslag (det er blevet svært med den nye Ekspert), kan du se at jeg vistnok er kommet til at foreslå løsningen mange gange... Jeg får det næsten dårligt, når nogen foreslår dynamiske arrays og lignende. Hvorfor vælge den absolut dårligste løsning når man kan vælge min??? Ich fass es nicht! - og jeg er dedikeret programmør (a.k.a. System developer, a.k.a. ..) nok til at fare i kridthuset. hver gang det sker.

Jeg bruger tit TObjectList-løsningen og en gang imellem kommer der ændringer, f.eks. Martins forslag om at nedarve data-klassen fra TPersistent. Ikke noget jeg er kommet meget i gang med for jeg kan ikke lige se hvordan TPersistent implementede det med streaming. Kiggede i VCL'et og blev "." klogere.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester