03. juni 2009 - 13:56Der 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.
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.
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.
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 :-)
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.
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.
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;
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.
Synes godt om
Ny brugerNybegynder
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.