### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: [Solved HowTo] Extra info for a TList.Sort somehow possible, how easiest?  (Read 772 times)

#### d7_2_laz

• Sr. Member
• Posts: 323
##### [Solved HowTo] Extra info for a TList.Sort somehow possible, how easiest?
« on: December 02, 2022, 02:21:20 pm »
Could it be possible to use an extra info particle (something like a "tag") being passed somehow to a TList.Sort?
Maybe there's some extended 'TListEx' around that does such?

Use case: to indicate a sort direction. For to avoid unelegant constructs like this:

Code: Pascal  [Select][+][-]
1. function Compare_ByDate(Item1, Item2: Pointer; bForward: Boolean): Integer;
2. begin
3.       // Here the comparision for the sort will be done, using an additional forward/backward flag for logic
4.      if bForward then
5.      // etc.
6.      Result := ...
7. end;
8.
9. function Compare_ByDate_Forward(Item1, Item2: Pointer): Integer;
10. begin
11.   Result := Compare_ByDate(Item1, Item2, True);
12. end;
13.
14. function Compare_ByDate_Backward(Item1, Item2: Pointer): Integer;
15. begin
16.   Result := Compare_ByDate(Item1, Item2, False);
17. end;
18.
19. // Called by:
20.   if bSortForward then
21.       MyList.Sort(@Compare_ByDate_Forward)
22.  else MyList.Sort(@Compare_ByDate_Backward)
;
« Last Edit: December 03, 2022, 05:50:04 pm by d7_2_laz »

#### jamie

• Hero Member
• Posts: 5069
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #1 on: December 02, 2022, 02:53:50 pm »
You can't use the observer code that is already in there?

With that you can signal and event with special indicators etc...

Attached an Observer to monitor the actions of that list.

Besides, The TLIST is one of those things that for the most part is expected to be classed forwarded so that add On's like you are looking for can be done. You simply create your own class based on that one.

The only true wisdom is knowing you know nothing

#### d7_2_laz

• Sr. Member
• Posts: 323
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #2 on: December 02, 2022, 03:25:12 pm »
Hello jamie,
attachment is missing? (a least i don't see it).

Hm, just had to realize that an additonal singular property (like 'tag') won't help.
The standalone (not attached to a class) listsort function passes pointers (to records); from there there's no bridge for to retrieve properties of the list itself.
So probably the slightly redundant way described is the easiest to pass a sort direction flag?

#### GetMem

• Hero Member
• Posts: 3953
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #3 on: December 02, 2022, 06:47:38 pm »
@d7_2_laz

Ideas: https://forum.lazarus.freepascal.org/index.php/topic,60154.0.html
I would do it like this(please see attachment):
Code: Pascal  [Select][+][-]
1. unit uListEx;
2.
3. interface
4.
5. uses
6.   Classes, SysUtils;
7.
8. type
9.   TListEx = class;
10.
11.   TItem = class
12.    FName: String;
13.    FDate: TDate;
14.    FListEx: TListEx;
15.   public
16.     constructor Create(AListEx: TListEx);
17.   end;
18.
19.   TSortType = (stAsc, stDesc);
20.   TListEx = class(TList)
21.   private
22.     FSortType: TSortType;
23.   public
24.     property SortType: TSortType read FSortType write FSortType;
25.   end;
26.
27. implementation
28.
29. constructor TItem.Create(AListEx: TListEx);
30. begin
31.   FListEx := AListEx;
32. end;

Then in the main form:
Code: Pascal  [Select][+][-]
1. uses DateUtils;
2.
3. function Compare(Item1, Item2: Pointer): Integer;
4. var
5.   SortType: TSortType;
6. begin
7.   SortType := TItem(Item1).FListEx.SortType;
8.   case SortType of
9.     stAsc: Result := CompareDate(TItem(Item1).FDate, TItem(Item2).FDate);
10.     stDesc: Result := CompareDate(TItem(Item2).FDate, TItem(Item1).FDate);
11.   end;
12. end;
13.
14. procedure TfMain.Button1Click(Sender: TObject);
15. var
16.   List: TListEx;
17.   Item: TItem;
18.   I: Integer;
19. begin
20.   List := TListEx.Create;
21.   try
22.     Item := TItem.Create(List);
23.     Item.FName := 'aaaa';
24.     Item.FDate := EncodeDate(2022, 12, 01);
26.
27.     Item := TItem.Create(List);
28.     Item.FName := 'zzzz';
29.     Item.FDate := EncodeDate(2022, 01, 01);
31.
32.     List.SortType := stAsc;
33.     List.Sort(@Compare);
34.     for I := 0 to List.Count - 1 do ShowMessage(TItem(List.Items[I]).FName);
35.   finally
36.     for I := List.Count - 1 downto 0 do
37.     begin
38.       TItem(List.Items[I]).Free;
39.       List.Delete(I);
40.     end;
41.     List.Free;
42.   end;
43. end;
44.

#### Nicole

• Hero Member
• Posts: 628
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #4 on: December 02, 2022, 08:00:51 pm »
I worked with this compare a lot, - and I hate it

Do you want to post an example of what you want to do?
In some situation, I could avoid the compare by adding items in sort already.
Or I can use an array instead.

How long is it?
How many sorts do you have?

#### d7_2_laz

• Sr. Member
• Posts: 323
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #5 on: December 02, 2022, 09:55:24 pm »
Hi GetMen, thank you for this interesting idea (as well as for the informative link)!

My question in the first line had not been to save four redundant looking procedures, but to know if there is a more efficient way.
But i wouldn't like to need to change the already existing getter and setters code for the data too much though.

In fact the sample might show up a very amazing approach! Really nice!
I'd need it to adapt it somehow to the different situaton (reconde instead of Item = class). I believe that is possible.
Below a minified picture of the data structure i actually have.

Nicole, thank you! On the fly i've not yet a short sample at hand, but GetMem's demo should be meaningful enough for it.
My data are a bit differently structured (as below). For the data amount, i have to take 0 - 30000 records to sort into account.

The TList Sort works quite fast. I'd say: 20000 items within a virtual listview instantly.
So, in a previous app i had sorted selfmade, but try to avoid now such loops and QuickSorts.

If i can succeed with the transfer to "records"  i'll come back to it here.
The data structure roughly is:

Code: Pascal  [Select][+][-]
1. type
2. ..
3.   PMyData = ^TMyData;
4.   TMyData = packed record
5.     FName: String[255];
6.     FTime: LongInt;
7.     // etc. etc., others
8.     //SortForward: Boolean;   // -> Would be an idea, but not a good idea ....
9.     //FListEx: TListEx;       // -> Oh! Could be a good idea i guess  .. gives access to the storer of the sortdirection information
10.   end;
11.
12. ... (class:) ...
13.  private
14.    FDataList: TList;
15.
16. ...  within Create: ...
17.    FDataList := TList.Create;
18.
19.
20. procedure DataSetter();
21. var MyData: PMyData;
22. begin
23.   GetMem(MyData, SizeOf(TMyData));
24.   MyData^.FName := ..something ..
25.   ...
27.
28. procedure DataGetter();
29. var MyData: PMyData; someindex: integer; S: String;
30. begin
31.   someindex := 17;
32.   MyData := FDataList.Items[someindex];
33.   S := MyData^.FName;

#### GetMem

• Hero Member
• Posts: 3953
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #6 on: December 02, 2022, 10:28:11 pm »
@d7_2_laz
Quote
I'd need it to adapt it somehow to the different situaton (reconde instead of Item = class). I believe that is possible.
Below a minified picture of the data structure i actually have.
If you switch to advanced records, you can easily adapt the code, somthing like this:
Code: Pascal  [Select][+][-]
1. unit uListEx;
2.
4. interface
5.
6. uses
7.   Classes, SysUtils;
8.
9. type
10.   TListEx = class;
11.
12. type
13.   PMyData = ^TMyData;
14.   TMyData = packed record
15.     FName: String[255];
16.     FTime: LongInt;
17.     FListEx: TListEx;
18.     constructor Create(AListEx: TListEx);
19.   end;
20.
21.   TSortType = (stAsc, stDesc);
22.   TListEx = class(TList)
23.   private
24.     FSortType: TSortType;
25.   public
26.     property SortType: TSortType read FSortType write FSortType;
27.   end;
28.
29. implementation
30.
31. constructor TMyData.Create(AListEx: TListEx);
32. begin
33.   FListEx := AListEx;
34. end;
35.
36. end.

#### d7_2_laz

• Sr. Member
• Posts: 323
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #7 on: December 02, 2022, 11:12:04 pm »
Oh, saw this too late, i had just tried it out and it works really fine.

II'll attach the adapted sample containing both variations (Item = class, and records) here probably tomorrow morning.

I already have had thought  to store a sort direction info within (one? all?)  record fields ... but, no good idea.
To store a reference to the list itself here, this is really excellent !

#### d7_2_laz

• Sr. Member
• Posts: 323
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #8 on: December 03, 2022, 10:45:57 am »
Attached the sample (as of reply #3) with the additions for records, as tried yesterday evening.

Differences when when using  modeSwitch advancedRecords   or not are marked in comments (// **).

The simplification when using "\$modeSwitch advancedRecords" (which would no longer require an extra field within the record) doesn't work yet. Because i probably have a wrong understanding of the Create statement (memory alloc for "MyData" which is a placeholder name in this sample).

#### GetMem

• Hero Member
• Posts: 3953
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #9 on: December 03, 2022, 02:24:05 pm »
@d7_2_laz

Thanks for the feedback. Advanced records are only optional. You can easily pass ListEx to the advanced record constructor, which is nice, however you can also assign ListEx to a regular record property, as you showed in your demo example.

#### d7_2_laz

• Sr. Member
• Posts: 323
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #10 on: December 03, 2022, 04:15:29 pm »
Yes GetMem, ok.
In fact it turned out for me that i really Must do this assignment, because otherwise, within the Compare funciton, the SortType resp. the FListEx itself went lost and an access violation appeared when i tried to determine the SortType here.

Mainly i had stumbled across that when using the same receiver variable for the "TMyData.Create(FDataList)", then the same storage location will be used and each overwritten (-> sequence 'zzzz', 'zzzz').
The Create does no memory allocaton (no reason to wonder) and i needed to keep that allocation somewhere (instead of simply replacing it by the Create).

Attached the sample v2 for the simplified version using \$modeSwitch advancedRecords. Could you take a look onto if it appears ok?

I'm very grateful for the very valuable info you gave, it think it is very useful for all that might be interested iin this topc.

« Last Edit: December 03, 2022, 04:17:39 pm by d7_2_laz »

#### GetMem

• Hero Member
• Posts: 3953
##### Re: Q.- only: extra info for a TList.Sort somehow possible, how easiest?
« Reply #11 on: December 03, 2022, 04:58:12 pm »
@ d7_2_laz

You have a small bug in your code:
Code: Pascal  [Select][+][-]
1.
2.   FDataList.SortType := stAsc;
3.   FDataList.Sort(@CompareMyData);
5.   for I := 0 to List.Count - 1 do begin //instead of List you need FDataList
6.      MyData := FDataList.Items[I];
8.   end;
9.   FDataList.SortType := stDesc;
10.   FDataList.Sort(@CompareMyData);
12.   for I := 0 to List.Count - 1 do begin //instead of List you need FDataList
13.      MyData := FDataList.Items[I];
15.   end;
16.

I also changed the order, please search for GetMem(MyData, SizeOf(TMyData)) in the attached project.

PS: TList is better suited for use with class types, because the instance of a class is already a pointer which exactly what TList holds. If you have the possibility, you should switch from records to class.
« Last Edit: December 03, 2022, 05:14:26 pm by GetMem »

#### d7_2_laz

• Sr. Member
• Posts: 323
##### Re: [Solved HowTo] Extra info for a TList.Sort somehow possible, how easiest?
« Reply #12 on: December 03, 2022, 05:58:31 pm »
Yes, saw it (list counter). About the order (was partially due to tests), ok, and meanwhile i think that the constructor is rather misleading than helpful (for the records), so i'd prefer here to replace it by the direct assignment (as you already mentioned).

Code: Pascal  [Select][+][-]
1.     GetMem(MyData, SizeOf(TMyData));
2.   //MyData^ := TMyData.Create(FDataList);
3.     MyData^.FListEx := FDataList;
4.     MyData^.FName := 'zzzz';

Quote
you should switch from records to class

Hint appreciated, makes sense

At least there's now a working model for both types. I'll indicate in the topic's title (Solved HowTo).
Big thanks!!

#### GetMem

• Hero Member
• Posts: 3953
##### Re: [Solved HowTo] Extra info for a TList.Sort somehow possible, how easiest?
« Reply #13 on: December 03, 2022, 06:10:25 pm »
@d7_2_laz

Quote
i think that the constructor is rather misleading than helpful (for the records), so i'd prefer here to replace it by the direct assignment
Yes, I agree. Direct assignment looks more natural for records.

Quote
At least there's now a working model for both types. I'll indicate in the topic's title (Solved HowTo).
Big thanks!!
You're more then welcome!