...Sort integers with thousand separators in a TListView?

Author: Alex Libby

Category: Misc

(*********************************************************************
Ever tried sorting numbers ? Yes ?

Have you ever tried sorting numbers, with thousand separators, and in
numerical order ?

A little harder, I think ! I found the same whilst writing some code
for a program,until I discovered a way to do it. The slow way is to
convert the existing string in the TListView to remove the separator,
sort it, and then re-insert the separator.

However, there is another way. It is based on the method above, but with
one crucial difference. The trick is to convert the numbers, but then to
assign it to a temporary double-length integer, run the sort, and output
the result. The separators in the numbers shown on screen are never
removed - their order is changed based on the temporary conversion. Below
is some example code - it uses example filenames and filesizes to
demonstrate the sorting procedure.

To see this in action, you need to:

1. Start a new project, and add a TListView to a TForm.
2. Add a few example filenames in column 0, then some example filesizes
   in column 1.
3. Add / change the code, to match the code below.
4. Run the project.

Notes:
- The code can be adapted to use different columns, depending on your
  requirements - as long as the right column numbers are assigned,
  then it should still work OK.
- To change the direction of the sorting on the first click, change the
  ListView1.Tag numbers in the Object Inspector.
- ListView1.Tag numbers: -1 = reverse order; 1 = forward order.
- It should be noted that the ',' symbol is being used as the thousand
  separator - this can also be changed from the sThouSep constant declaration
*********************************************************************)



//CODE FOR SORTING NUMBERS WITH SEPARATORS:


unit cdSortNumbers;

interface

uses
  
SysUtils, Classes, Forms, Controls, ComCtrls;

type
  
TForm1 = class(TForm)
    ListView1: TListView;
    procedure bySizeClick(Sender: TObject);
    procedure ListView1ColumnClick(Sender: TObject; Column: TListColumn);
  private
    
{ Private declarations }
  
public
    
{ Public declarations }
  
end;

var
  
Form1: TForm1;     { main form }
  
n1: Double;
  { used to assign converted numbers to double length integers, before conversion }
  
n2: Double;     { n1 = first number in pair, n2 = second number in pair }

const
  
sThouSep = ',';
  { defines the thousand separator being used in TListView - change if required }

implementation

{$R *.dfm}


{ main sorting function - convert strings to numbers, then sort accordingly }
function CustomSizeSortProc(Item1, Item2: TListItem; ParamSort: Integer): Integer;
  stdcall;
begin
  
n1 := 0;
  n2 := 0;

  { string conversion and assignment process to n1 or n2, based on order being sorted }
  
if ParamSort = 1 then
  begin
    
n1 := StrToFloat(StringReplace(Item1.SubItems.Strings[0], sThouSep, '',
      [rfReplaceAll]));
    n2 := StrToFloat(StringReplace(Item2.SubItems.Strings[0], sThouSep, '',
      [rfReplaceAll]));
  end
  else if 
ParamSort = -1 then
  begin
    
n1 := StrToFloat(StringReplace(Item2.SubItems.Strings[0], sThouSep, '',
      [rfReplaceAll]));
    n2 := StrToFloat(StringReplace(Item1.SubItems.Strings[0], sThouSep, '',
      [rfReplaceAll]));
  end;

  { determines final position, based on comparing results from conversion process }
  
if n1 > n2 then Result := 1 
  else if n1 < n2 then Result := -1 
  else 
    
Result := 0;
end;

{ determine direction of sort, then call sort function }
{ can be called from other components, such as a TButton }
procedure TForm1.bySizeClick(Sender: TObject);
begin
  if 
ListView1.Tag = -1 then ListView1.Tag := 1 
  else 
    
ListView1.Tag := -1;
  ListView1.CustomSort(@CustomSizeSortProc, ListView1.Tag);
end;

{ optional extra - calls sorting routine from column header click }
{ if TListView being used to sort many numbers / strings, then these can be added in a similar manner }
procedure TForm1.ListView1ColumnClick(Sender: TObject; Column: TListColumn);
begin
  if 
column = ListView1.Column[1] then bySizeClick(Sender);
end;

end.

 

printed from
www.swissdelphicenter.ch
developers knowledge base