Recent

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

d7_2_laz

  • Sr. Member
  • ****
  • Posts: 323
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: 5076
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: 3961
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);
  25.     List.Add(Item);
  26.  
  27.     Item := TItem.Create(List);
  28.     Item.FName := 'zzzz';
  29.     Item.FDate := EncodeDate(2022, 01, 01);
  30.     List.Add(Item);
  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: 634
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  >:D

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.   ...
  26.   FDataList.Add(MyData);
  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: 3961
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.  
  3. {$modeSwitch advancedRecords}
  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.
I didn't know anything about advanced records yet. Thank you!

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: 3961
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: 3961
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);
  4.   Memo1.Lines.Add('- Ascending: ');
  5.   for I := 0 to List.Count - 1 do begin //instead of List you need FDataList
  6.      MyData := FDataList.Items[I];
  7.      Memo1.Lines.Add(MyData^.FName);
  8.   end;
  9.   FDataList.SortType := stDesc;
  10.   FDataList.Sort(@CompareMyData);
  11.   Memo1.Lines.Add('- Descending: ');
  12.   for I := 0 to List.Count - 1 do begin //instead of List you need FDataList
  13.      MyData := FDataList.Items[I];
  14.      Memo1.Lines.Add(MyData^.FName);
  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';
  5.     FDataList.Add(MyData);

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: 3961
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!

 

TinyPortal © 2005-2018