Tablica rekordów/wyszukiwanie liniowe i warunek do tego.

0

Witam,mam do was pytanie.

Nie potrafię wymyślić algorytmu na wybór tabeli(combobox-em) po której ma wykonać się procedura wyszukiwania liniowego.
Mam tablicę rekordów:

type baza =record
       id:string[20];
     imie:string[20];
       nazwisko:string[20];
       wiek:integer;
       plec:string[20];
       telefon:LongInt;
       uczulony:string[20];
       zeby:string[20];
       zaplata:string[20];
       data:string[20];
       leczenie:string[200]
   end; 

Oraz kod do wyszukiwania liniowego osób ale tylko dla tabeli wiek,nie jestem w stanie wymyślić warunku dla sprawdzania innych tabel.
Czyli np wybiorę w comoboxie pozycje "Imie' i ma mi wyszukiwać osobę po tabeli 'imie'.

licznik:=0;
 for i := 0 to Length(tab) - 1 do
   if combobox3.caption='Wiek' then
 begin
       if edit11.Text=inttostr(tab[i].wiek)  then
       begin
       licznik:=licznik+1;

 StringGrid1.RowCount :=  StringGrid1.RowCount + 1;

 StringGrid1.cells[0,licznik]:= tab[i].Id;
 StringGrid1.cells[1,licznik]:= tab[i].imie;
 StringGrid1.cells[2,licznik]:= tab[i].nazwisko;
 StringGrid1.cells[3,licznik]:= IntToStr(tab[i].wiek);
 StringGrid1.cells[4,licznik]:= tab[i].plec;
 StringGrid1.cells[5,licznik]:= IntToStr(tab[i].telefon);
 StringGrid1.cells[6,licznik]:= tab[i].uczulony;
 StringGrid1.cells[7,licznik]:= tab[i].zaplata;
 StringGrid1.cells[8,licznik]:= tab[i].data;
 StringGrid1.cells[9,licznik]:= tab[i].leczenie;
        end;
 end;

Oczywiście mógłym zrobić kopiuj/wklej tego kodu i zamienić jedynie rekord tablicy który ma sprawdzać warunek,czyli np.
zamiast

 if combobox3.caption='Wiek' then 

zamienić na

if combobox3.caption='Imie' then 

oraz

 if edit11.Text=inttostr(tab[i].wiek) 

zamienić na

 if edit11.Text=inttostr(tab[i].Imie)

I tak dalej...dla wszystkich rekordów typu baza wklejając te kilkanaście linii kodu pod samą kilka razy,ale wierzę,że można uzyskać ten cel mniejszą ilością kodu.

0

Dla takiego rekordu raczej niczego sensownego nie da się wymyślić – ile pól struktury, tyle różnych funkcji przeszukujących.

for i := 0 to Length(tab) - 1 do
   if combobox3.caption='Wiek' then

Takich rzeczy się nie robi – najpierw sprawdzasz warunek, a dopiero po jego spełnieniu lecisz z pętlą. W przeciwnym razie dla każdej iteracji pętli wykonywany jest warunek, choć i tak wiadomo że będzie/nie będzie spełniony. Poza tym z ComboBox-a możesz pobrać indeks elementu i na jego podstawie wykonać np. case of.

Dodawania, usuwania czy modyfikowania elementów w kontrolce StringGrid nie robi się ot tak na pałę – przed całym procesem modyfikacji zawartości kontrolki woła się metodę BeginUpdate, a po wszystkim EndUpdate. Dzięki temu kontrolka nie odświeża i nie przemalowuje swojej powierzchni po każdorazowej zmianie zawartości.

Jeśli zależy Ci na wygodnym gromadzeniu i przetwarzaniu danych, powinieneś skorzystać z normalnej bazy danych, a nie z tak prymitywnych kontenerów jak tablice rekordów. Ich obsługa jest co prawda prosta, ale mało elastyczna i niewiele można z nimi zrobić, jeśli chodzi o filtrowanie.

0
furious programming napisał(a):

Dla takiego rekordu raczej niczego sensownego nie da się wymyślić – ile pól struktury, tyle różnych funkcji przeszukujących.

Delphi ma RTTI i na 100% coś w tym stylu można zrobić na klasach ale głowy sobie nie dam uciąć, że zadziała na rekordach. Trzeba by wniknąć w temat, bo ja nie bardzo w tym temacie ale jak ktoś chce wnikać to trzeba szukać właśnie w kierunku RTTI.

EDIT: Teraz zobaczyłem że choddzi o Lazarus a tam chyba nie ma tego mechanizmu.

0

@kAzek: zobacz na to – https://stackoverflow.com/questions/27803383/fpc-rtti-on-records

Nie wiem czy skorzystanie z RTTI w tym przypadku da jakieś korzyści, czy tylko zagmatwa kod.

1

Nie wiem na ile RTTI w Lazarus jest zgodne z tym z Delphi (ja nie wiedziałem że w ogóle jest) ale w Delphi działa taki przykład:

type
  TString10 = string[10];

  TTestRecord = record
    ItemInt: Integer;
    ItemStr: TString10; //nie może być string[10] bezpośrednio bo RTTIField.GetValue się wykrzacza
  end;

i teraz tylko:

uses System.Rtti;

procedure TForm2.Button1Click(Sender: TObject);

function GetFieldValueAsStr(ARTTIField: TRTTIField; ARec: TTestRecord): string;
var
  Value: TValue;
begin
  Value:= ARTTIField.GetValue(@ARec); //pobieram wartosc z pola
  case Value.Kind of //trzeba sprawdzic jakiego typu i zrobic stosowna konwersje na string
    tkInteger: result:= Value.ToString;
    tkString, tkUString: result:= Value.AsString;
  else
    result:= '';
  end;
end;

var
  Tab: array [0..100] of TTestRecord;
  i: Integer;

  RTTIContext: TRTTIContext;
  RTTIType: TRTTIType;
  RTTIField: TRTTIField;
  RTTIFields: TArray<TRTTIField>;
  Rec: TTestRecord;
begin
  //wprowadzam jakies przykladowe dane
  for i:= Low(Tab) to High(Tab) do
  begin
    Tab[i].ItemInt:= Random(10);
    Tab[i].ItemStr:= 'Str' + IntToStr(i);
  end;

  RTTIType:= RTTIContext.GetType(TypeInfo(TTestRecord));
  RTTIFields:= RTTIType.GetFields;

  StringGrid1.RowCount:= 1;
  StringGrid1.ColCount:= Length(RTTIFields);

  RTTIField:= RTTIType.GetField(ComboBox1.Text); //pobiera pole o nazwie wybranej z combobox
  if RTTIField <> nil then //czy jest takie pole?
  begin
     for Rec in Tab do
     begin
       if SameText(GetFieldValueAsStr(RTTIField, Rec), Edit1.Text) then //sprawdzenie czy rekord ma szukaną wartość (w Edit1 szukana wartość)
       begin
         for i:= Low(RTTIFields) to High(RTTIFields) do  //wpisanie wartości wszysakich pól rekordu do StringGrid
           StringGrid1.Cells[i, StringGrid1.RowCount -1]:= GetFieldValueAsStr(RTTIFields[i], Rec);
         StringGrid1.RowCount:= StringGrid1.RowCount + 1;
       end;
     end;
  end;
end;

Więc raczej kodu nie zagmatwa, nie ma też go wiele i nawet jest bardziej elastyczny, bo zadziała bez zmian jak się doda do rekordu nowe pola. Być może można to zrobić jeszcze krócej ale jak pisałem specjalistą od RTTI to ja nie jestem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1