I have just started to use generics, and I am currently having a problem doing sorting on multiple fields.
Case:
I have a PeopleList as a TObjectList<TPerson>
and I want to be able to make an Excel-like sorting function, by selecting one sort-field at a time, but keeping the previous sorting as much as possible.
EDIT: It must be possible to change the field sort sequence at runtime. (Ie. in one scenario, the user wants the sort order A,B,C - in another scenario he wants B,A,C - in yet another A,C,D)
Lets say we have an unsorted list of people :
Lastname Age
---------------------
Smith 26
Jones 26
Jones 24
Lincoln 34
Now if I sort by LastName :
Lastname ▲ Age
---------------------
Jones 26
Jones 24
Lincoln 34
Smith 26
Then if I sort by Age, I want this :
Lastname ▲ Age ▲
---------------------
Jones 24
Jones 26
Smith 26
Lincoln 34
In order to do this, I have made two Comparers - One TLastNameComparer and one TAgeComparer.
I now call
PeopleList.Sort(LastNameComparer)
PeopleList.Sort(AgeComparer)
Now my problem is that this does not produce the output I want, but
Lastname ? Age ?
---------------------
Jones 24
Smith 26
Jones 26
Lincoln 34
where Smith,26 appears before Jones,26 instead. So it seems like it doesn't keep the previous sorting.
I know that I can make just one comparer that compares both LastName and Age - but the problem is, that I then have to make comparers for each combination of the fields present in TPerson.
Is it possible to do what I want using multiple TComparers or how can I accomplish what I want?
Just for reference to future visitors, this is (almost) the code I am using now.
First I made a base class TSortCriterion<T>
and a TSortCriteriaComparer<T>
in order to be able to use these in multiple classes in the future.
I have changed the Criterion and the list to TObject
and TObjectList
respectively, as I found it easier if the objectlist automatically handles destruction of the Criterion.
TSortCriterion<T> = Class(TObject)
Ascending: Boolean;
Comparer: IComparer<T>;
end;
TSortCriteriaComparer<T> = Class(TComparer<T>)
Private
SortCriteria : TObjectList<TSortCriterion<T>>;
Public
Constructor Create;
Destructor Destroy; Override;
Function Compare(Const Right,Left : T):Integer; Override;
Procedure ClearCriteria; Virtual;
Procedure AddCriterion(NewCriterion : TSortCriterion<T>); Virtual;
End;
implementation
{ TSortCriteriaComparer<T> }
procedure TSortCriteriaComparer<T>.AddCriterion(NewCriterion: TSortCriterion<T>);
begin
SortCriteria.Add(NewCriterion);
end;
procedure TSortCriteriaComparer<T>.ClearCriteria;
begin
SortCriteria.Clear;
end;
function TSortCriteriaComparer<T>.Compare(Const Right, Left: T): Integer;
var
Criterion: TSortCriterion<T>;
begin
for Criterion in SortCriteria do begin
Result := Criterion.Comparer.Compare(Right, Left);
if not Criterion.Ascending then
Result := -Result;
if Result <> 0 then
Exit;
end;
end;
constructor TSortCriteriaComparer<T>.Create;
begin
inherited;
SortCriteria := TObjectList<TSortCriterion<T>>.Create(True);
end;
destructor TSortCriteriaComparer<T>.Destroy;
begin
SortCriteria.Free;
inherited;
end;
Finally, in order to use the sort criteria : (this is just for the sake of the example, as the logic of creating the sort order really depends on the application) :
Procedure TForm1.SortList;
Var
PersonComparer : TSortCriteriaComparer<TPerson>;
Criterion : TSortCriterion<TPerson>;
Begin
PersonComparer := TSortCriteriaComparer<TPerson>.Create;
Try
Criterion:=TSortCriterion<TPerson>.Create;
Criterion.Ascending:=True;
Criterion.Comparer:=TPersonAgeComparer.Create
PersonComparer.AddCriterion(Criterion);
Criterion:=TSortCriterion<TPerson>.Create;
Criterion.Ascending:=True;
Criterion.Comparer:=TPersonLastNameComparer.Create
PersonComparer.AddCriterion(Criterion);
PeopleList.Sort(PersonComparer);
// Do something with the ordered list of people.
Finally
PersonComparer.Free;
End;
End;
Excel Sort Multiple Levels 1 Select the entire data set that you want to sort. 2 Click the Data tab. 3 Click on the Sort Icon (the one shown below). This will open the Sort dialog box. 4 In the Sort Dialogue box, make the following selections. 5 Click on Add Level (this will add another level of sorting options). 6 ... (more items) See More....
1. A Simple Sort in Excel Sorting can be a very simple, two-click process to reorganize the data in your spreadsheet. Let's learn how. In an Excel workbook, start off by clicking in a cell of the column you want to sort. Now, make sure that you're on the Home tab of Excel's ribbon, and find the Sort and Filter button on the far right side of it.
A big advantage of the SORTBY formula over Excel's Custom Sort feature is that the formula updates automatically whenever the original data changes, while the feature requires cleaning up and re-sorting with each change. How this formula works:
Here are three Excel data sorting techniques you'll learn in this tutorial: Sort data simply with just a couple of clicks. Set multiple, cascading sorting rules, such as sorting alphabetically by state, and then by county.
Put your sort criteria in a list that includes the direction to sort and the function to use to compare items. A record like this could help:
type
TSortCriterion<T> = record
Ascending: Boolean;
Comparer: IComparer<T>;
end;
As the user configures the desired ordering, populate the list with instances of that record.
var
SortCriteria: TList<TSortCriterion>;
The Comparer
member will refer to the functions you've already written for comparing based on name and age. Now write a single comparison function that refers to that list. Something like this:
function Compare(const A, B: TPerson): Integer;
var
Criterion: TSortCriterion<TPerson>;
begin
for Criterion in SortCriteria do begin
Result := Criterion.Comparer.Compare(A, B);
if not Criterion.Ascending then
Result := -Result;
if Result <> 0 then
Exit;
end;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With