C++ Builder Snippets  


Daten im TStringGrid numerisch, alphanumerisch oder nach Datum sortieren:
 
Oft müssen die Inhalte einer Tabelle nach bestimmten Kriterien sortiert werden. Woodym postete am 16.03.2002 im Forum der RAD-Seiten einen Beitrag über die Sortierung der TStringGrid-Inhalte nach Datum. Die nachfolgend beschriebene Funktion StringGridBubbleSort() ist einer Weiterführung seines Vorschlags. Sie ermöglicht sowohl auf- als auch absteigende Sortierung von StringGrid-Zeilen. Es kann nach Strings, Zahlen oder Datumsangaben sortiert werden.

Die Funktion StringGridBubbleSort() verwendet langsames BubbleSort-Verfahren und führt ggf. zeitaufwendige Konvertierungen durch. Bei grösseren Datenmengen ist es daher sinnvoller, mit TList oder STL-Containern zu arbeiten und deren Sortierfunktionen (in der Regel Quicksort) zu verwenden. TStringGrid sollte ohnehin nur für die Visualisierung der Daten und nicht als "Datenarray" zur Verwaltung der Variablen vewendet werden.

Nachtrag vom 29.12.2002:
Peter Tuschik hat die Sortierroutine auf ShellSort-Verfahren umgestellt, wodurch die Ausführungsgeschwindigkeit sich bereits bei 250 Elementen im Beispielprogramm um Faktor 30 (!) erhöht hat. Seine Implementierung der Sortierfunktion befindet sich in der Routine StringGridShellSort().



Screenshot des Beispielprojekts

//---------------------------------------------------------------------------
// Art der zu sortierenden Daten:
enum GRIDSORT_TYPE
{
  SORT_NUMERIC,
  SORT_DATETIME,
  SORT_STRINGS
};
//----------------------------------------------------------------------------
// StringGridBubbleSort() sortiert den Inhalt eines TStringGrid
// (BubbleSort-Verfahren wird verwendet)
//----------------------------------------------------------------------------
// Übergabeparameter:
//
// TStringGrid* pGrid   - Zeiger auf die zu sortierende Tabelle
// int iCol         - Index der Spalte, nach der sortiert werden soll
// int ilSortType       - Art der Daten in der Spalte, folgende Werte möglich:
//                        SORT_NUMERIC  : Zelleninhalt numerisch
//                        SORT_DATETIME : Zellen mit Datumswerten
//                        SORT_STRINGS  : Zellen mit Strings
// bool blAsccending    - true für aufsteigende Sortierung, anderenfalls false
//----------------------------------------------------------------------------
void StringGridBubbleSort(TStringGrid* pGrid, int iCol,
  int iSortType, bool blAsccending)
{
  TCursor crOldCursor = Screen->Cursor;
  Screen->Cursor = crHourGlass;
  AnsiString sBuf;
  bool blSwap = false;
  bool blSwapped = false;

  try
  {
    for (int ilRow = 1; ilRow < pGrid->RowCount; ilRow++)
    {
        blSwapped = false;
        for (int iRow = 1; iRow < pGrid->RowCount-1; iRow++)
        {
          if (iSortType == SORT_NUMERIC)
          {
             if ((blAsccending &&
                ConvertToDoubleDef(pGrid->Cells[iCol][iRow], MaxDouble) >
                ConvertToDoubleDef(pGrid->Cells[iCol][iRow+1], MaxDouble)) ||
                (!blAsccending &&
                ConvertToDoubleDef(pGrid->Cells[iCol][iRow], MaxDouble) <
                ConvertToDoubleDef(pGrid->Cells[iCol][iRow+1], MaxDouble)))
                blSwap = true;
          }
          else if (iSortType == SORT_DATETIME)
          {
            try
            {
             if ((blAsccending &&
                (TDateTime)pGrid->Cells[iCol][iRow] >
                (TDateTime)pGrid->Cells[iCol][iRow+1]) ||
                (!blAsccending &&
                (TDateTime)pGrid->Cells[iCol][iRow] <
                (TDateTime)pGrid->Cells[iCol][iRow+1]))
                blSwap = true;
            }
            catch(...) {;}
          }
          else if (iSortType == SORT_STRINGS)
          {
            if ((blAsccending &&
               pGrid->Cells[iCol][iRow].AnsiCompare(
               pGrid->Cells[iCol][iRow+1]) > 0 )||
               (!blAsccending &&
               pGrid->Cells[iCol][iRow].AnsiCompare(
               pGrid->Cells[iCol][iRow+1]) < 0))
               blSwap = true;
          }
          if (blSwap)
          {
             sBuf = pGrid->Rows[iRow]->Text;
             pGrid->Rows[iRow]->Text = pGrid->Rows[iRow+1]->Text;
             pGrid->Rows[iRow+1]->Text = sBuf;
             blSwapped = true;
             blSwap = false;
          }
      }
      if (!blSwapped) break;
    }
  }
  __finally
  {
    Screen->Cursor = crOldCursor;
  }
}
//----------------------------------------------------------------------------
// StringGridShellSort() sortiert den Inhalt eines TStringGrid
// (ShellSort-Verfahren wird verwendet) by Peter Tuschik 
//----------------------------------------------------------------------------
// Übergabeparameter:
//
// TStringGrid* pGrid   - Zeiger auf die zu sortierende Tabelle
// int iCol             - Index der Spalte, nach der sortiert werden soll
// int ilSortType       - Art der Daten in der Spalte, folgende Werte möglich:
//                        SORT_NUMERIC  : Zelleninhalt numerisch
//                        SORT_DATETIME : Zellen mit Datumswerten
//                        SORT_STRINGS  : Zellen mit Strings
// bool blAsccending    - true für aufsteigende Sortierung, anderenfalls false
//----------------------------------------------------------------------------
void StringGridShellSort(TStringGrid *pGrid, int iCol,
  int iSortType, bool blAsccending)
{
  int iOffset, iLimit, iRow, iSwitch;
  int anz = pGrid->RowCount;
  AnsiString sBuf;
                                        // Vergleichsoffset auf Mitte der
  iOffset = anz / 2;                    // Datenmenge initialisieren

  while (iOffset)                       // Loop bis Offset == 0
  {
    bool blSwap = false;
    iLimit = anz - iOffset;
    do
    {
       iSwitch = 0;                     // Assume no switches at this offset
       /* Compare elements and switch ones out of order. */
       for (iRow = 1; iRow < iLimit; iRow++)
       {
           if (iSortType == SORT_NUMERIC)
           {
              if ((blAsccending &&
                  ConvertToDoubleDef(pGrid->Cells[iCol][iRow], MaxDouble) >
                  ConvertToDoubleDef(pGrid->Cells[iCol][iRow+iOffset], MaxDouble)) ||
                  (!blAsccending &&
                  ConvertToDoubleDef(pGrid->Cells[iCol][iRow], MaxDouble) <
                  ConvertToDoubleDef(pGrid->Cells[iCol][iRow+iOffset], MaxDouble)))
                  blSwap = true;
           }
           else if (iSortType == SORT_DATETIME)
           {
             try
             {
              if ((blAsccending &&
                  (TDateTime)pGrid->Cells[iCol][iRow] >
                  (TDateTime)pGrid->Cells[iCol][iRow+iOffset]) ||
                  (!blAsccending &&
                  (TDateTime)pGrid->Cells[iCol][iRow] <
                  (TDateTime)pGrid->Cells[iCol][iRow+iOffset]))
                  blSwap = true;
             }
             catch(...) {;}
           }
           else if (iSortType == SORT_STRINGS)
           {
              if ((blAsccending &&
                  pGrid->Cells[iCol][iRow].AnsiCompare(
                  pGrid->Cells[iCol][iRow+iOffset]) > 0 )||
                  (!blAsccending &&
                  pGrid->Cells[iCol][iRow].AnsiCompare(
                  pGrid->Cells[iCol][iRow+iOffset]) < 0))
                  blSwap = true;
           }

           if (blSwap)
           {
              sBuf = pGrid->Rows[iRow]->Text;
              pGrid->Rows[iRow]->Text = pGrid->Rows[iRow+iOffset]->Text;
              pGrid->Rows[iRow+iOffset]->Text = sBuf;
              iSwitch = iRow+1;
              blSwap = false;
           }
       } /* For */

       /* Sort on next pass only to where last switch was made. */
       iLimit = iSwitch - iOffset;
    }
    while(iSwitch);

    /* No switches at last offset, try one half as big. */
    iOffset = iOffset / 2;
  } /*while */
}
//---------------------------------------------------------------------------
//  Beschreibung: Konvertiert einen AnsiString zu double
//  (gibt dlDefValue zurück, falls nicht konvertiert werden kann)
//---------------------------------------------------------------------------
double ConvertToDoubleDef(AnsiString sValueString, double dlDefValue)
{
  double dResultValue = 0.0;
  sValueString = sValueString.Trim();

  if(sValueString == EmptyStr) return dResultValue;
  if(DecimalSeparator == ',' && sValueString.Pos(".") > 0)
    sValueString[sValueString.Pos(".")] = DecimalSeparator;
  else if(DecimalSeparator == '.' && sValueString.Pos(",") > 0)
    sValueString[sValueString.Pos(",")] = DecimalSeparator;

  try
  {
    dResultValue = sValueString.ToDouble();
  }
  catch(...) { return dlDefValue; }
  return dResultValue;
}


Original-Beitrag von Woodym im Forum der RAD-Seiten vom 16.03.02:

StringGrid sortierung nach Datum

ich will einen kleinen beitrag zu den snippets leisten.
das problem: ein StringGrid soll nach datum sortiert werden. das datum steht hier in der form TT.MM.JJJJ in einer zelle (wird direkt erzeugt durch ein DateTimePicker). durch aufruf von
DateStringGridSort(StringGrid1,0);
wird nun das StringGrid sortiert nach Datum. das Datum ist in spalte 0.
void changeStringGridRow(TStringGrid* strg,int source,int dest)
{
  int i=0;
  AnsiString buf;

  for(i=0;i<strg->ColCount;i++)
  {
    buf=strg->Cells[i][source];
    strg->Cells[i][source]=strg->Cells[i][dest];
    strg->Cells[i][dest]=buf;
  }
}

void DateStringGridSort(TStringGrid* slStrings,int zelle)
{
  int i, j, k,l,m;
  for(i = 1; i < slStrings->RowCount - 1; i++)
  {
    k = i;
    for(j=i+1; j < slStrings->RowCount; j++)
    {
      if(slStrings->Cells[zelle][j]=="" || slStrings->Cells[zelle][k]=="")
        continue;
      l=(TDate)slStrings->Cells[zelle][j];
      m=(TDate)slStrings->Cells[zelle][k];
      if(l < m) k = j;
    }
    changeStringGridRow(slStrings,k,i);
  }
}

  Download BCB6
Projekt-Quellcode
Download
Demo-Exe

© '99-2002 by S. Kreutzmann