I have an array of records each of 2 fields: Filepath, a string and
CreationDate, TDateTime. I want to sort the array in ascending order
on the CreationDate field. Can anyone shed any light on how to do
this.
Thanks
Alex
"Alex Schulz" <alex....@superweb.ca> wrote in message
news:k94f7vk7hme3srk12...@4ax.com...
Look, if you don't want to help don't, but don't make stupid remarks.
I have just come to Delphi from Vb and can use some help rewriting an
app I already wrote in VB in Delphi. Unlike yourself, I was not born
with an intimate knowledge of every programing language under the sun.
As for your suggestion, I don't see how a stringlist which holds a
single list of strings, can help me sort an array of records
containing a string and a TDateTime field.
add record pointers to a tlist
call list.sort and supply an appropriate comparer function
the pointers get sorted , then reload the array if necessary
or just use the list index to pick from the array
you could look at classes.pas for quicksort function code
tstringlist can have an object associated with each entry and has
a virtual sort method, exchanged strings take their object with them
ie initially store your string+object(dates) into a class you derived
from Tstringlist
Depends on your needs after sorting.
also demo\threads\sortthds.pas
This is a snippet of a similar task I had. There are four differences from your
requirement ...
1 I put the files (with a certain extension) into a TStringList.
2 I used a datetime value from within the file (not the file date) - a
TWaveFile is one of my object classes to do that
3 I sorted on the minute count of the TDateTime value (ie minutes from 31 Dec
1899) because the storage needs an integer. This was near enough for me.
4 I sorted it with my code.
{put filenames in strings, file date in minutes in objects}
{... get list of files in strings}
SL := TStringList.Create;
if FindFirst(PathName + '\*.' + LexWaveExt, 0, SrchRec) = Success then begin
repeat
SL.Add(PathName + '\' + SrchRec.Name);
until FindNext(SrchRec) <> Success;
FindClose(SrchRec);
end;
{... get dict time (minutes) in objects}
for i := 0 to SL.Count - 1 do begin
WF := TWaveFile.CreateOpen(SL.Strings[i]);
SL.Objects[i] := TObject(trunc(WF.DictData.DateTime2 * 24 * 60)); // date
as minutes
WF.Free;
end;
{... sort list on dict time in Objects}
repeat {until ListSorted}
ListSorted := true;
with SL do
for i := 0 to Count - 2 do
if integer(Objects[i]) < integer(Objects[i + 1]) then begin
{i is earlier (older) than i+1, so swap to get earliest at end}
Exchange(i, i + 1);
ListSorted := false;
end;
{end; for i := 0 to Count - 2}
{end; with SL}
until ListSorted;
{list is now sorted on dict age, earliest at top}
You can (if you don't already) use FileTimeToDateTime(FileAge(filepathname)) to
get the file dates into a TDateTime value.
Alan Lloyd
alang...@aol.com
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
Forms,
Dialogs, StdCtrls, FileCtrl;
type
pFileInfo = ^TFileInfo;
TFileInfo = record
Name : string;
Date : TDateTime;
end;
type
TForm1 = class(TForm)
ListBox1: TListBox;
btnExec: TButton;
FileListBox1: TFileListBox; // hidden on form
procedure btnExecClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function CompareDates(Item1, Item2: Pointer): Integer;
begin
if pFileInfo(Item1)^.Date < pFileInfo(Item2)^.Date then
result := -1
else if pFileInfo(Item1)^.Date = pFileInfo(Item2)^.Date then
result := 0
else
result := 1;
end;
procedure TForm1.btnExecClick(Sender: TObject);
const PATH = 'C:\Documents and Settings\Alex Schulz\My Documents\My
Pictures\Cars\';
var
Info : pFileInfo;
FileList : TList;
ix : integer;
s : String;
begin
FileList := TList.Create;
// set the hiddne filelistbox to the test path
FileListBox1.Directory := PATH;
try
// add the file names and dates to the list
// for each file in the filelistbox
for ix := 0 to (FileListBox1.Count -1) do begin
s := FileListBox1.Items[ix];
New(Info);
Info^.Name := s;
Info^.Date := FileDateToDateTime(FileAge(Path + s));
FileList.Add(Info);
end;
// sort the results
FileList.Sort(CompareDates);
// show results in the listbox on the form
for ix := 0 to (FileList.Count - 1) do begin
Info := FileList.Items[ix];
ListBox1.AddItem(Info^.Name, nil);
end;
// cleanup
for ix := 0 to (FileList.Count - 1) do begin
Info := FileList.Items[ix];
Dispose(Info);
end;
finally
FileList.Free;
end;
end;
end.
>On Tue, 18 Mar 2003 15:43:16 -0800, "Val Krzyzaniak"
><shar...@hotmail.com> wrote:
>
>Look, if you don't want to help don't, but don't make stupid remarks.
>I have just come to Delphi from Vb and can use some help rewriting an
>app I already wrote in VB in Delphi. Unlike yourself, I was not born
>with an intimate knowledge of every programing language under the sun.
>As for your suggestion, I don't see how a stringlist which holds a
>single list of strings, can help me sort an array of records
>containing a string and a TDateTime field.
>
It sounds as if you have an Array of Records
(or should have - they are just like VB's UDTs)
You then need an array of pointers to each of the records
( in VB an array of Longs containing 1,2,3,4 ... )
You then need to sort the pointers
Note: TDateTime is the same as VB's Date type
- it is a Double (Real) in drag
Here is a Shell Sort example :-
unit Unit1;
// USSort Demonstration - 26/5/01 J French
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls;
Type
TIsGreater = Function( Const Item1, Item2:Integer):Boolean;
TSwap = Procedure( Const Item1, Item2:Integer);
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
Procedure USSort(Const Min, Max:Integer; IsGreater:TIsGreater;
Swap:TSwap);Forward;
var
Form1: TForm1;
implementation
{$R *.DFM}
Var
I : Array Of Integer ;
{Callback for Compare}
Function MyIsGreater( Const Item1, Item2:Integer ):Boolean;
Begin
Result := I[ Item1 ] > I[ Item2 ] ;
End; {MyIsGreater}
{Callback for Swap}
Procedure MySwap( Const Item1, Item2:Integer );
Var
H : Integer ;
Begin
H := I[ Item2 ] ;
I[ Item2 ] := I[ Item1 ] ;
I[ Item1 ] := H ;
End; {MySwap }
{Demonstration of USSort }
procedure TForm1.Button1Click(Sender: TObject);
Var
L9, Min, Max : Integer;
GoodFlag, BadFlag : Integer ;
begin
Min := 0 ;
Max := 1000 ;
// Set up the Integer Array
SetLength( I, Max ) ;
// Set reverse sort order
For L9 := Min To Max Do
I[L9] := Max - L9 ;
USSort( Min, Max, @MyIsGreater, @MySwap ) ;
// Now Check the Sorting
GoodFlag := 0 ;
BadFlag := 0 ;
For L9 := Min + 1 To Max Do
If I[L9] <> I[L9-1] + 1 Then
Begin
ShowMessage( 'Error ' + IntToStr( L9 ) ) ;
Inc( BadFlag ) ;
Break;
End
Else
Inc( GoodFlag ) ;
ShowMessage( 'Completed '
+ IntToStr( GoodFlag )
+ ' ' + IntToStr( BadFlag )
) ;
End;
{ #####################################################
Sort Utility with Callback for Compare and Swap
Method : Shell Sort
Min must be >= 0
Max must be >= Min
}
Procedure USSort(Const Min, Max:Integer; IsGreater:TIsGreater;
Swap:TSwap);
Var
L9, L8, Gap : Integer;
Begin
Gap := (Max - Min + 1) Div 2 + 1 ;
While Gap > 0 Do
Begin
For L9 := Min To Max - Gap Do
Begin
L8 := L9 ;
While L8 >= Min Do
Begin
If IsGreater(L8,L8 + Gap) Then
Swap( L8, L8 + Gap )
Else
L8 := Min - 1 ;
L8 := L8 - Gap ;
End; {While}
End; {For}
Gap := Gap Div 2;
End; {While}
End; {USSort}
end.
from 30 Dec 1899 00:00:00 local time.
--
© John Stockton, Surrey, UK. j...@merlyn.demon.co.uk Turnpike v4.00 MIME. ©
Web <URL:http://www.merlyn.demon.co.uk/> - FAQish topics, acronyms, & links.
Proper <= 4-line sig. separator as above, a line exactly "-- " (SonOfRFC1036)
Do not Mail News to me. Before a reply, quote with ">" or "> " (SonOfRFC1036)
If you are using D4 or above, then I suggest that you look into
Dynamic Arrays.
In your case you *know* how many records you have, so you only have
one memory allocation - via SetLength()
- which has to speed things up.
Also using FindFirst instead of a FileListbox is a bit more direct, as
it saves (effectively) scanning the directory twice
Speed of operation is not a real issue here, getting the app up and
running is.
The TList seems to be ideal for this.
Once again, thanks to all for your suggestions.l
>Actually I will stick with the TList and not bother iwth an array at
>all. I will indeed be using FindFirst to populate the list. As I said,
>this was only a test program. The actual application searches a number
>of folders for AutoCAD drawings matching a specific filename criteria,
>so I don't really know how many files I will have in a given search
>
>Speed of operation is not a real issue here, getting the app up and
>running is.
>
>The TList seems to be ideal for this.
>
Fair enough
However, you should be aware that with Dynamic Arrays, SetLength() is
the Delphi equivalent of VB's Redim Preserve
It enables one to allocate contiguous records without using 'New' and
de-allocate them without using 'Dispose'
Using a tlist/tstringlist/etc instead of arrays often comes down to only
between 2 and 4 times access overhead compared to an array.
Better overhead if you need to move/insert/delete elements. e.g. sort
If you spend significant time manipulating the data then that access time
difference is correspondingly less significant.
List code that works for nil,1, and 2 record should work until memory
limits,
usually has much simpler code, thus fewer bugs.
e.g. List objects already have all those little helper/cursor variables
maintained
you would have to write code for.
By the time arrays would be much more efficient you find managing arrays
of many megabytes to be much more work. Chicken eats egg.
The possibility of thrashing memory depending on access pattern
can be a factor against using lists, along with much larger memory
requirements compared to arrays for very small records.
The disk find operations would be the major bottleneck here ,
by a factor of 1000 and more.
>Thanks for the info. People like you make the transition from VB to
>Delphi that much easier.
>
The transition is not that bad
- mostly it is understanding the 'Delphi way'
- and that is not necessarily the Pascal way
One small thing that helped me a lot when it 'clicked', is to rigidly
stick to 'pure' Delphi formatting
- for some time I tried to mimic VB formatting
A 'Begin' is terminated with an 'End'
A ';' separates a statement from the next one
- sounds obvious, but fighting with that made my code less clear
Another thing is to turn on Bounds Checking - and keep it on
Also when dealing with Records that may go down to disk, watch out for
'Packed'
Also declaring proceedures using 'Forward' helps considerably
Given that one declares most of the routines at the top, one might as
well declare the 'private' ones - for completeness.
And, from the mouse right click menu, Complete Class at Cursor, saves
a lot of grunt work
Good Luck with Delphi