Recent

Author Topic: [Solved] Sort String Grid more than 1 column  (Read 11600 times)

dcelso

  • Full Member
  • ***
  • Posts: 158
[Solved] Sort String Grid more than 1 column
« on: October 01, 2013, 05:46:16 pm »
Hello,
Is possible to sort stringGrids by more than one column?

Currently I put column clic sort property to true.

But if I do to sort a column and after I do to sort another column, I lost the sorted first column.

For example in JAVA swing it does not happend.

An example may be the next


col1    col2
A         aa
B         ss
A         zz
C         aa
A         gg
D         zz


If i sort by col2 the result is

col1    col2
A         aa
C         aa
A         gg
B         ss
A         zz
D         zz

Then if I sort by col 1 now, the result is the next

col1    col2
A         aa
A         zz
A         gg

B         ss
C         aa
D         zz


Instead of I was spected, that is the next
col1    col2
A         aa
A         gg
A         zz

B         ss
C         aa
D         zz

Because col2 was sorted before, I do not understad what are doing string grid sort to do not do it.

Any Idea?
« Last Edit: October 03, 2013, 09:50:13 pm by dcelso »

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: Sort String Grid more than 1 column
« Reply #1 on: October 01, 2013, 06:18:59 pm »
IIRC, you can specify your own custom sorting algorithms for grids...
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: Sort String Grid more than 1 column
« Reply #2 on: October 01, 2013, 06:22:01 pm »
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4709
  • I like bugs.
Re: Sort String Grid more than 1 column
« Reply #3 on: October 01, 2013, 06:33:47 pm »
Yes, your custom sort could compare multiply columns.
There are also sorting algorithms that preserve the already sorted order better. One is TimSort:
  http://en.wikipedia.org/wiki/Timsort
If I understood right, it would behave just as you explained when sorting by one column. Mattias Gärtner had a plan to replace the current QuickSort with TimSort but maybe it was buried under other priorities. I must ask him.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4709
  • I like bugs.
Re: Sort String Grid more than 1 column
« Reply #4 on: October 02, 2013, 12:43:51 pm »
I took the liberty to copy parts of the answer from Mattias here:
"
TimSort is a more optimized version of QuickSort, so it is in many cases
faster and some cases slower. Another disadvantage is that it needs to
allocate n div 2 temporary memory. So it is not a good replacement for
the default QuickSort of TFPList.

José Mejuto <joshyfun@gmail.com> did one implementation, but in my
tests it had so much overhead that it was often slower than the normal
QuickSort.
"

There were some other problems in the code, too. Maybe José Mejuto (joshyfun) can explain more himself.

Yet I think TimSort would make lots of sense in Grid components if it really solves the problem presented here. If somebody makes a well-tested patch that implements TimSort as a default algorithm for grids, I am sure it can be applied.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12296
  • Debugger - SynEdit - and more
    • wiki
Re: Sort String Grid more than 1 column
« Reply #5 on: October 02, 2013, 01:05:20 pm »
This should not be about Sorting-Algorithm. And it should already be possible.

To sort by more than one column, it would be inefficient to sort twice (first by one column, then by another while preserving the last order for cells that equal in the 2nd run)

The sort should be done in a single run. When 2 rows are compared, then it should compare all involved columns (according to their priority in the sorting) until a column is found in which the rows differ.

StringGrid has OnCompareCells. This is called during sort. So it can be done

If you sort first by column n, and 2nd by column m, then it will be called with cells from various rows, but all from that column.
But if the 2 cells are equal then OnCompareCells should compare the values in column m (for the same rows).
This can be done with any amount of columns.

All that is needed, is to store the entire sorting info before calling sort. in a place readable from OnCompareCells.

There is no need to change the algorithm for this.

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4709
  • I like bugs.
Re: Sort String Grid more than 1 column
« Reply #6 on: October 02, 2013, 01:26:49 pm »
This should not be about Sorting-Algorithm. And it should already be possible.
To sort by more than one column, it would be inefficient to sort twice (first by one column, then by another while preserving the last order for cells that equal in the 2nd run)

The problem comes up when the user has set ColumnClickSorts = True, or implemented the same functionality otherwise.
Sorting by clicking columns is a very common thing, yet it had to be implemented again and again in user code for TStringGrid.
Finally I decided to implement a property for it. It sorts and shows an up/down icon for the sorted column.
I remember Vincent and Mattias already then mentioned the order of consecutive sorts is wrong because the algorithm does not preserve the order of keys with equal values.
TimSort was mentioned as a possible solution then.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

mattias

  • Administrator
  • Full Member
  • *
  • Posts: 210
    • http://www.lazarus.freepascal.org
Re: Sort String Grid more than 1 column
« Reply #7 on: October 02, 2013, 01:29:17 pm »
I agree with Martin that the column issue should be handled in the compare function, instead of applying multiple sorts.

For the column approach:
MergeSort for TFPList is available in LCLProc and keeps order.
It has a stable runtime of O(n*logn).

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4709
  • I like bugs.
Re: Sort String Grid more than 1 column
« Reply #8 on: October 02, 2013, 03:35:20 pm »
I agree with Martin that the column issue should be handled in the compare function, instead of applying multiple sorts.

Yes, if the column order is known in advance. It is not known when a user just clicks the column headers. Multiple clicks lead to multiple sorts.

Quote
For the column approach:
MergeSort for TFPList is available in LCLProc and keeps order.
It has a stable runtime of O(n*logn).

Ok, I did not realize that. Maybe somebody creates a patch with MergeSort.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

dcelso

  • Full Member
  • ***
  • Posts: 158
Re: Sort String Grid more than 1 column
« Reply #9 on: October 03, 2013, 02:45:50 am »
thanks to all responses,
Finally I solved it merging a litte of all ideas :D
I created a my custom tstringrid that i override the sort method, to use the stringlist sort method (that uses mergesort of tfplist) seems use quick sort too
So I need create a stringlist to sort, so I need convert a entire row in a string using a char separator. and put in the first place the contend of the column to sort.

The result may be a very slow sort but is enough for me for now ;D.

All feathures to this code are welcome :)

Code: [Select]
procedure TStringGridMio.Sort(ColSorting: Boolean; index, IndxFrom, IndxTo: Integer);
  procedure Split
     (const Delimiter: Char;
      Input: string;
      const Strings: TStrings) ;
  begin
     Assert(Assigned(Strings)) ;
     Strings.Clear;
     Strings.StrictDelimiter := true;
     Strings.Delimiter := Delimiter;
     Strings.DelimitedText := Input;
  end;

var
  list : TStringList;
  splitted : TStringList;
  i,j,k : integer;
  line : String;
  string1 : string;
begin
    if RowCount>FixedRows then begin
    CheckIndex(ColSorting, Index);
    CheckIndex(not ColSorting, IndxFrom);
    CheckIndex(not ColSorting, IndxTo);
    BeginUpdate;

    list := TStringList.Create;
    splitted := TStringList.Create;

    line :='';
    for i:= indxFrom to indxTo  do
    begin
      line := Cells[index,i] + chr(9);
      for j:= FixedCols to ColCount -1  do
      begin
        if j <> index then
        begin
          line := line + Cells[j,i] + chr(9);
        end;
      end;
      list.Add(line);
    end;

    list.Sort;

    for i:= 0 to list.Count-1  do
    begin
       split(chr(9),list.Strings[i],splitted);
       string1 := splitted[0];
       Cells[index,IndxFrom+i] := string1;
       k:= 1;
       for j:= FixedCols to ColCount -1  do
       begin
         if j <> index then
         begin
           string1:=splitted[k];
           Cells[j,IndxFrom+i]:=string1;
           k:=k +1;
         end;
       end;
    end;
    splitted.free;
    list.free;
    EndUpdate;
  end;
end;
« Last Edit: October 03, 2013, 03:36:00 am by dcelso »

dcelso

  • Full Member
  • ***
  • Posts: 158
Re: [Solved] Sort String Grid more than 1 column
« Reply #10 on: October 03, 2013, 11:28:27 pm »
This is another faster solution than before.
Instead of compare all entire data row, only compare the column selected data and uses mergesort to do the compare. To do it you need an auxiliar list with the old index and de data to compare to later with this list sorted, create the result list correcly.

Code: [Select]
procedure TStringGridMio.Sort(ColSorting: Boolean; index, IndxFrom, IndxTo: Integer);
  procedure DuplicateRow( index_row : integer) ;
  var
    j : integer;
  begin
    rowcount := rowcount +1;
    for j:= FixedCols to ColCount -1  do
    begin
        Cells[j,rowcount-1] := Cells[j,index_row];
    end;
  end;

var
  list : TFPList;
  i,j,k : integer;
  string1 : string;
  PDataSortable : ^DataSortable;
begin
    if RowCount>FixedRows then begin
    CheckIndex(ColSorting, Index);
    CheckIndex(not ColSorting, IndxFrom);
    CheckIndex(not ColSorting, IndxTo);
    BeginUpdate;

    list := TFPList.Create;

    for i:= indxFrom to indxTo  do
    begin
      new (PDataSortable);
      PDataSortable^.index:=i;
      PDataSortable^.value:=Cells[index,i];
      list.Add(PDataSortable);
    end;

    MergeSort(list,@CompareDataSortable) ;

    for i:= 0 to list.Count-1  do
    begin
      PDataSortable:=list.Items[i];
      DuplicateRow(PDataSortable^.index);
      Dispose(PDataSortable);
    end;
    for i:= indxFrom to indxTo  do
    begin
      DeleteRow(indxFrom);
    end;
    list.free;
    EndUpdate;
  end;
end;

« Last Edit: October 04, 2013, 02:37:45 am by dcelso »

 

TinyPortal © 2005-2018