Sortowanie alfabetyczne rekordów

0

Do moderatorów - proszę nie usuwać tego posta. Wiem, że podobne już byłyb umieszczone na tym forum, ale nie rozwiązałem dzięki nim problemu.

Jak napisałem wyżej ten problem nie jest nowy, ale w moim przypadku specyficzny. Posiadam rekord typu:

TDataRecord = packed record
   imie,nazwisko,email:string[40];
   teldom: string[12];
   telkom: string[6];
   adres,uwagi: string[200];
   end;

Jest on wczytywany to komponentu TStringGrid bezpośredniu z pliku. Chcę posortować alfabetycznie tabelkę według jednego z elementów. Przyjmijmy - nazwisko.
W poprzednich postach opisaliście procedurkę QuickSort i korzystałem z niej, lecz kompilator generował mi błędy związane z nieprawidłowym adresowaniem pamięci ( czy coś takiego ;-) ). Było to chyba związane z dynamicznym tworzeniem tablicy. Nie bardzo wiedziałem jak to rozwiązać lecz zamiast zgłębiać powód tego błędu postanowiłem dać nowego posta, gdyż QuickSort nie była dla mnie idealna...
Po pierwsze: po wczytaniu danych do TStringGrid'a nie chce zmieniać struktury pliku.
Po drugie: w QuickSort parametrem jest tablica moich rekordów, a ja takowej nie posiadam więc musiałem stworzyć dodatjkową procedurę tworzącą tablicę. Po co? [???]

Dlatego mam pytanie: jak stworzyć procedurę, która po prostu zamienia kolejnością elementy w StringGridzi'e według kolejności alfabetycznej? Jeśli stworzenie tablicy jest konieczne to się jakoś z tym pogodzę ;-), ale wolałbym tego uniknąć.

P.S. Zapewne znajdziecie parę spraw w tym poście, które wydadzą się Wam banalne lub też uznacie go za całkowicie lamerski, ale jestem początkujący i się tego nie wstydze! :D
"Kto pyta - nie błądzi." <---- jakoś tak to było :]

0

Przecież TStringGrid to także tablica, tylko tablica wierszy tekstu.
Przekazujesz jedynie tablicę z nr wierszy i odwołujesz się do StringGrida lub nawet bezpośrednio na TStringGrid.Rows

0

Ze StringGridem sprawa rzeczywiście nie jest ciekawa. Można by np. wprowadzać do StringLista po kolei każdy wiersz StringGrida, następnie wywołać metodę StringList.Sort, następnie łańcuchy ze StringLista wprowadzać po kolei do StringGrida. Chociaż ja bym chyba czegoś takiego nigdy nie zrobił.

A dlaczego akurat StringGrid?!

Najlepszym rozwiązaniem jest zastosowanie rzeczywistej bazy danych (np. paradox) i komponentów TTable+DbGrid. Wówczas sortowanie jest sprawą raczej prostą. Wystarczy albo ustalić odpowiedni index (dodawany przy definiowaniu pól tabeli), albo wywołać polecenie SQL SELECT.... + ORDER BY.
To rozwiązanie jednak ma swoje wady. Potrzebna jest wersja Professional lub Enterprise Delphi, rozmiar pliku EXE drastycznie się zwiększa, a wprzypadku zastosowania BDE użytkownik takiej aplikacji również musiałby mieć zainstalowany BDE.

Ale jest jeszcze jedno w miarę sensowne rozwiązanie.
A mianowicie zastosowanie ListView.
Do sortowania trzeba wykorzystać metody OnColumnClick + OnCompare + AlphaSort. Opis i bardzo dobry przykład zastosowania jest w Helpie.
Ale przy zastosowaniu ListView powstałby mały problem z edycją. ListView umożliwia edycję danych tylko z 1-szej kolumny. Aby umożliwić użytkownikowi edycję danych z wszystkich kolumn, można np. dodać kilka Editów i wyświetlać w nich informacje z zaznaczonego itema ListView, a po zmianie wartości w Editach aktualizować zaznaczony item.
Podobny sposób edycji występuje w wielu aplikacjach bazodanowych.

0

Eh.. Nic nie ciężko.

procedure Sortuj(A: TStringGrid);

  procedure Zamien(Lo, Hi: Integer);
  var
    T: TStringList;
  begin
    T := TStringList.Create;
    T.Assign(A.Rows[Lo]);
    A.Rows[Lo].Assign(A.Rows[Hi]);
    A.Rows[Hi].Assign(T);
    T.Free;
  end;

  procedure QuickSort(iLo, iHi: Integer);
  var
    Lo, Hi: Integer;
    Mid: string;
  begin
    Lo := iLo;
    Hi := iHi;
    Mid := A.Cols[0][(Lo + Hi) div 2];
    repeat
      while A.Cols[0][Lo] < Mid do Inc(Lo);
      while A.Cols[0][Hi] > Mid do Dec(Hi);
      if Lo <= Hi then
      begin
        Zamien(Lo, Hi);
        Inc(Lo);
        Dec(Hi);
      end;
    until Lo > Hi;
    if Hi > iLo then QuickSort(iLo, Hi);
    if Lo < iHi then QuickSort(Lo, iHi);
  end;

begin
  QuickSort(0, A.RowCount-1);
end;

Można to oczywiście zoptymalizowąć i zrobić bardziej elastycznym, ale i tak za dużo już tutaj dałem.

0

Co do tego dlaczego wybrałem StringGrida to odpowiedź jest banalna:
nie bardzo mam pojęcie o bazach danych, a chciałem zacząć pisać jak najszybciej. Ale zdaje sobie sprawę, że to by rozwiązało cały problem.

Dryobates: dzięki za udostępnienie tej procedurki :D Widziałem już ją w innym poście o tym temacie :-) Włąśnie wtedy mi wyskoczyły błędy z kompilacją, ale skoro to naprawdę dobre rozwiązanie to jednak poświęce mu troszkę więcej czasu :-) Zamieściłbym efekty na forum, ale pewnie i tak nikt już tu nie zajrzy ;-)
W każdym razie - wielkie dzięki za pomoc. Dalej powinienem sobie poradzić.

Peace.

0

Nieco to pogmatwane i "toporne", ale wydaje się działać prawidłowo.

Procedura sortująca StringGrida według podanej kolumny:
[in] Grid - StringGrid do sortowania;
[in] Column - kolumna, wg której ma się odbywać sortowanie
{
sortowanie rosnące; tylko stringi;
Column = 1 - kolumna 0-wa StrinGrida,
Column = 2 - kolumna 1-sza StringGrida
itd.
}

procedure SortGrid(const Grid: TStringGrid; const Column: integer);
const
  ToRemove = #1'&#8240;_TO_REMOVE_&#8240;'#1;
var
  s, ToInsert, s2: string;
  c, r, i, x, k: integer;
  sl: TStringList;
begin
  if (Column > Grid.ColCount) or (Column <= 0) then
  begin
    s := 'Column must be a integer value between 1 and ' + IntToStr(Grid.ColCount) + ' !';
    MessageBox(0, PChar(s), 'Are you crazy ?!', MB_OK or MB_ICONEXCLAMATION);
    Exit;
  end;

  sl := TStringList.Create;
  try
    with Grid do
    begin

      for r := 0 to RowCount - 1 do
      begin
        s := Cells[Column - 1, r] + ToRemove;
        for c := 0 to ColCount - 1 do
          if c <> Column - 1 then
            s := s + Cells[c, r] + ToRemove;
        sl.Add(s);
      end; // for r

      sl.Sort;

      for i := 0 to sl.Count - 1 do
      begin
        s := sl[i];

        if Column > 1 then
        begin
          x := Pos(ToRemove, s);
          ToInsert := Copy(s, 1, x - 1);
          Delete(s, 1, Length(ToInsert) + Length(ToRemove));
          k := 0;
          x := 1;
          s2 := '';
          while x > 0 do
          begin
            x := Pos(ToRemove, s);
            s2 := s2 + Copy(s, 1, x - 1) + ToRemove;
            s := Copy(s, x + Length(ToRemove), Length(s));
            inc(k);
            if k = Column - 1 then Break;
          end;
          s := s2 + ToInsert + ToRemove + s;
        end; // if Column > 1

        for c := 0 to ColCount - 1 do
        begin
          x := Pos(ToRemove, s);
          Cells[c, i] := Copy(s, 1, x - 1);
          Delete(s, 1, x + Length(ToRemove) - 1);
        end; // for c
      end; // for i
    end; // with

  finally
    sl.Free;
  end;
end;

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