How to sort an array of dates (TDate) descending? - arrays

I have an array[0..99] of TDate and need to sort it, in descending order. I can't find an example anywhere, not even here on StackOverflow...
Btw, not all array items have a value. Most of the time, the array is filled with 10 to 30 values. I do not need a "super fast" way to do this, but the simplest / easier way...

I don't know why you have an array of TDates, rather than using a TList that Delphi provides for you, but I'll assume you are making some sort of test project, and you just need a quick and dirty way to sort your array. Here's a way to do it that will be more than quick enough for such a small array
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, DateUtils;
var
A : array[0..99] of TDateTime;
T : TDateTime; //Used to exchange dates
I,J : integer;
begin
//Initialize with some test data:
for I := 0 to 99 do
A[I] := DateOf(Now) - 365 + random(365);
//Output the unsorted list:
for I := 0 to 99 do
writeln(DateToStr(A[I]));
readln;
//Here's our sorting algorithm (Bubble Sort, probably the easiest sorting algorithm to
//understand, and the quickes to implement - but the worst sorting algorithm in actual use
for I := 0 to 98 do //One less than the max index, but you really should use some constants
for J := I+1 to 99 do //Up to the max index
if A[I] < A[J] then //Change < to > to sort in ascending order
begin
T := A[I]; //We'll be overwriting A[I], so we should store the value
A[I] := A[J]; //Here we overwrite A[I] with A[J]
A[J] := T; //And here we put T back in the new position
end;
//Output the sorted list:
for I := 0 to 99 do
writeln(DateToStr(A[I]));
readln;
end.
I would really recommend using some other data structure (TList being the most obvious choice). Here's a way to do that:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, DateUtils, generics.collections;
var
A : TList<TDate>;
I : integer;
T : TDate;
begin
A := TList<TDate>.Create;
try
for I := 0 to 99 do
A.Add(DateOf(Now) - 365 + random(365));
A.Sort; //Sorts in ascending order by default
A.Reverse; //But you wanted descending order, so we'll reverse the list
for T in A do
writeln(DateToStr(T));
readln;
finally
A.Free;
end;
end.

Related

Fastest way to Convert Olevariant 2D Array of Double to Dynamic 2D Double Array

I have a 2D OleVariant matrix of doubles xyInput := VarArrayCreate([0, Count-1, 0, 1], varDouble );.
I want a conversion (fast as possible) to a plain 2D dynamic array DestArray : array[0..1] of array of Double using move().
In this process of solving this, I have used Count=5 giving an expected 40 bytes per dimension. But I have discovered that the address diff between Pointer(DestArray[0]) and Pointer(DestArray[1]) is 56 bytes.
So what are the 16 bytes in between? I dont know about the first 8 bytes, but the last 8 bytes are information about the array dimension.
As a consequence the move() does not work in one step Move(VarArrayData^, Pointer(DestArray[0])^, BytesToMove );.
I have found a way by using to 2 separate moves, but I still have a feeling it can be done more elegant.
Questions:
Is there in fact a much more simple and easy fast way?
Are there any compiler directives or similar that can change the memory layout of the dynamic array to allow move() to work in a single step?
Out of curiosity, what are the first 8 bytes in the memory gap between Pointer(DestArray[0]) and Pointer(DestArray[1])?
Here is a complete code snippet:
procedure TForm1.FormCreate(Sender: TObject);
procedure PrintEqualityVerdictLine( value1 : Double; value2 : Double );
const
cEqualVerdict : array[Boolean] of String = ( '!!!Not Equal!!!', 'Equal' );
begin
Memo1.Lines.Add(FloatToStr(value1) + ' =? ' + FloatToStr(value2) + ' ' + cEqualVerdict[ SameValue( value1, value2, 0.001 ) ] );
end;
procedure VariantArrayOfDoubleToDynamicDoubleArray;
var
xyInput : OleVariant;
Count: Integer;
n: Integer;
DestArray : packed array[0..1] of packed array of Double;
V_Ptr: PVarData;
VarArrayData: PVarData;
BytesToMove: Integer;
SourceBytePtr : PByte;
BytesToMovePerColumn: Integer;
DestBytePtr: PByte;
begin
// create 2 column OleVariant array:
Count := 5;
xyInput := VarArrayCreate([0, Count-1, 0, 1], varDouble );
// fill test data:
for n := 0 to Count-1 do
begin
xyInput[n, 0] := 1.0 * n;
xyInput[n, 1] := 2.0 * Count + n;
end;
SetLength(DestArray[0], Count);
SetLength(DestArray[1], Count);
V_Ptr := PVarData(#xyInput);
if ((V_Ptr^.VType and $F000 ) = varArray) and
((V_Ptr^.VType and varTypeMask ) = varDouble)
then
begin
VarArrayData := PVarData(V_Ptr^.VArray^.Data);
BytesToMovePerColumn := Count * V_Ptr^.VArray^.ElementSize;
BytesToMove := BytesToMovePerColumn*V_Ptr^.VArray^.DimCount;
// print 16 discovered intermediate bytes of the DestArray:
DestBytePtr := Pointer(DestArray[0]);
Inc(DestBytePtr, BytesToMovePerColumn);
for n := 1 to 16 do
begin
Memo1.Lines.Add('byte['+IntToStr(n) + ']: ' + IntToStr( DestBytePtr^ ) );
Inc(DestBytePtr);
end;
// This does NOT work: col 1 of arr gets offset due to 16 discovered intermediate bytes:
// Move(VarArrayData^, Pointer(DestArray[0])^, BytesToMove );
// This works:
SourceBytePtr := PByte(VarArrayData);
Move(SourceBytePtr^, Pointer(DestArray[0])^, BytesToMovePerColumn );
Inc(SourceBytePtr, BytesToMovePerColumn);
Move(SourceBytePtr^, Pointer(DestArray[1])^, BytesToMovePerColumn );
end;
// print:
Memo1.Lines.Add('VariantArrayOfDoubleToDoubleArray:');
Memo1.Lines.Add('col 0:');
for n := 0 to Count - 1 do
PrintEqualityVerdictLine( xyInput[n, 0], DestArray[0, n] );
Memo1.Lines.Add('');
Memo1.Lines.Add('col 1:');
for n := 0 to Count - 1 do
PrintEqualityVerdictLine( xyInput[n, 1], DestArray[1, n] );
end;
begin
Memo1.Lines.Clear;
VariantArrayOfDoubleToDynamicDoubleArray;
end;
But I have discovered that the address diff between Pointer(DestArray[0]) and Pointer(DestArray[1]) is 56 bytes.
That is very much to be expected. Well, to be more clear, there is no reason at all to expect that DestArray[0] and DestArray[1] will point to adjacent blocks of memory.
Your type is
array[0..1] of array of Double;
Note that I removed the packed keyword which is ignored when applied to arrays. What you have here is an array containing two pointers. These two pointers are independent. Look at how you allocate the dynamic arrays.
SetLength(DestArray[0], Count);
SetLength(DestArray[1], Count);
Each call to SetLength results in a separate heap allocation. No reason at all for the memory to be adjacent. That's before getting to the issue that a dynamic array has an extra block of meta data stored immediately before the payload of the array, and each block of memory has its own meta data used by the memory manager. So even if the memory manager by chance happened to serve up adjacent blocks of memory, the meta data would sit between the two arrays. Incidentally, this memory manager meta data is the answer to your question 3.
In technical terms, what you have here in DestArray is a jagged array. You on the other hand appear to be looking for a multi-dimensional array. Delphi does not actually support dynamic multi-dimensional arrays. All you have are jagged arrays. If you want a contiguous block of memory then you would need to allocate a one dimensional block of memory and perform the index calculation yourself.
So, as it stands, if you continue with jagged arrays then you will need to perform one copy for each inner array. If you switch to a linear array then you can get away with a single copy, but you will have to perform your own indexing. Of course, the indexing is very easy to do and that might be efficient. Finally, it's plausible that you could allocate a linear array in Delphi ahead of time, and put a pointer to that array into your variant and thereby avoid the copy completely.

Arrays in Delphi (object Pascal) using variables?

I have these 10 numbers (one in each line) in a Text File and I need them to be sorted in a chronological order starting with the highest number. I wrote a code which works just fine, but the problem is that the code isn't flexible, because once I add another number to the Text File it won't work since the code is set to sort 10 numbers only...this is due to my array of integers which is supposed to read the values before the sorting process starts, but won't allow me to add a variable to the array's properties so it will be able to read and sort any-size text file...I know there has to be a way of making a program which can sort any-size file of this structure, just please tell me how I could improve my code. (If you think my way isn't too efficient, it's because that's my homework from high school and I need to use these arrays to implement a bubblesort).
program Project2;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
numbers, sortednumbers : TextFile;
count : integer=0;
number : array[1..10] of integer;
I : integer;
Procedure Swap(var X, Y : Integer);
var
Temp : integer;
begin
Temp := X;
X := Y;
Y := Temp;
end;
procedure Assign;
var I : Integer;
begin
reset(numbers);
for I := 1 to count do ReadLn(numbers, number[I]);
end;
procedure BubbleSort;
var I, J : integer;
begin
for I := 2 to count do
begin
for J := count downto I do
if (number[J] > number[J - 1]) then
Swap(number[J - 1], number[J]);
end;
end;
begin
AssignFile(numbers, 'Numbers.txt');
AssignFile(sortednumbers, 'Sorted-Numbers.txt');
Reset(numbers);
While not EoF(numbers) do
begin
ReadLn(numbers);
Inc(count);
end;
Reset(numbers);
ReWrite(sortednumbers);
Assign;
BubbleSort;
For I := 1 to count do writeln(sortednumbers, number[I]);
CloseFile(numbers);
CloseFile(sortednumbers);
end.
Use a dynamic array. This is an array that can change the number of elements it holds.
Instead of declaring:
number : array[1..10] of integer;
instead skip the bounds declaration:
Number : array of integer;
Then, before you start using it, set its length:
SetLength(Number, 10);
Once you are done, free the memory by setting it to have length 0:
SetLength(Number, 0);
Note that:
Indexes of a dynamic array start from 0, not 1. That is, Number[0] is the first element and Number[9] is the tenth. This is quite common in programming but can be confusing if it's not something you've come across before.
There are other 'quality' changes you can make to your code too. As commenter David Heffernan said, stop using global variables. I would also suggest using try/finally for resource allocation and cleanup, and using streams (and another link, wrap this over TFileStream) instead of the old-style file IO you're currently doing. Also, consider your variable names - Number for an array of numbers is odd - why not add the S and call it Numbers, for example?
That might be a bit much all at once, so go slowly in small steps, save and backup often (preferably into source control), and have fun!
And if it had not been a school project:
function Comparer(List: TStringList; index1, index2: integer): integer;
begin
try
Result:= StrToInt(List[index1]) - StrToInt(List[Index2]);
except
raise Exception.Create('Your file does not contain numbers');
end;
end;
function SortNumbers
var
lStringList: TStringlist;
begin
lStringList := TStringlist.create;
try
lStringList.LoadFromFile('Numbers.txt');
lStringList.CustomSort(Comparer);
lStringList.SaveToFile('Sorted-Numbers.txt');
finally
lStringList.Free;
end;
end;
However the Delphi RTL uses Quicksort, not Bubblesort.

Making new array whose elements are every second element of another array.(Pascal)

I've been trying to make a program that will ask user to input elements of an array and then use that array to make a new one whose elements would be every 2nd element of inputted array. This is what I was writing:
program Keanu;
uses crt;
type Arr=array of integer;
var n,i:integer;
A,C:Arr;
begin
writeln('--Enter desired length of array--');
readln(n);
setlength(A,n);
setlength(C,n);
writeln('Elements of array A:');
for i:=1 to n do
readln(A[i]);
writeln('Elements of array C are:');
i:=1;
while (i<=n) do
begin
c[i]:=a[i];
i:=i+2;
end;
write('C = {');
for i:=1 to n do
begin
if c[i]=0 then continue else
begin
write(c[i],' ');
end;
end;
write('}');
readln;
end.
But as you can notice this is far from efficient way to make this program do the job.
First, because my new array will contain blank/empty elements(zeros) which I simply ignored with continue statement and I dont want to do that if possible.
Second,I have problem when inputting an even number for array length.Last element of new array in output window is very small,negative number and he shouldn't be there at all.I know this has to do something with my counter "i" crossing into "undefined" indexes of array.
I also tried replacing while loop with some variations of:
for i:=0 to n do
c[i]:=a[2*i-1] ;
Which is more elegant way but I still get , besides desired result , those large numbers , again because of crossing limits of an array.I suspect this has to be done with proper steps of how new array is made and moving those elements next to each other with no blank elements.
So, if anyone can give me some solutions of how to get these loop steps and limits into right order and make efficient,shortest algorithm, and preferably without using while loop if possible ,and definitely without ignoring those blank elements of new array.
Declaring variables by one character A, C: array of integer is a bad practice. The name of variable will tell you about it's type and meaning. (highly recommended)
And read about Hungarian notation, then you'll understand why TArr and arrA instead of Arr and A. Not necessary, but recommended. (also recommended)
As for the second arrC, you can use operator div to make it twice smaller than the first array arrA.
You can also crete a new variable that will be n div 2, in order not to change n div 2 in the whole code (good practice):
nDivided := n div 2;
SetLength(arrA,n);
SetLength(arrC, nDivided);
This will make your program quite a bit efficient: you'll save n - (n div 2) * sizeof(integer) bytes.
Here are both cases (for even and odd N). No "blank elements" and no "very small, negative number in the end of new array(-32768)".
N is 6 N is 5
Elements of array A: Elements of array A:
arrA[1]=1 arrA[1]=1
arrA[2]=2 arrA[2]=2
arrA[3]=3 arrA[3]=3
arrA[4]=4 arrA[4]=4
arrA[5]=5 arrA[5]=5
arrA[6]=6
Elements of array C are: Elements of array C are:
arrC[1]=2 arrC[1]=2
arrC[2]=4 arrC[2]=4
arrC[3]=6
Anyway, here is code (a little changed). Also not very good (efficent) but it does exactly what you need.
program WorkingWithArrays;
uses crt;
type
TArr = Array of Integer;
var
i, n: integer;
arrA, arrC: TArr;
begin
writeln('Enter the length of array:');
readln(n);
SetLength(arrA,n);
SetLength(arrC, n div 2);
writeln('Elements of array A:');
for i:=1 to (n) do begin
arrA[i]:=i;
writeln('#> arrA[',i,']=', arrA[i]);
end;
writeln('Elements of array C are:');
i:=1;
while (i <= n div 2) do
begin
arrC[i]:=arrA[i+i];
i:=i+1;
end;
for i:=0 to (n div 2) do begin
if arrC[i]=0 then continue else begin
writeln('#> arrC[',i,']=', arrC[i]);
end;
end;
readln;
end.
// compiled here: http://www.compileonline.com/compile_pascal_online.php

Pascal insert user input to an array

I am trying to get better at functional programming. As a start I am planning on trying out with couple of languages like Pascal, Scheme, ML etc. First I started with Pascal. I am trying to insert user input into a integer array in pascal and then get them reverse.
1 program ReverseList;
2
3 var
4 i: Integer;
5 k: Integer;
6 a: array[1..100] of Integer;
7 begin
8 i := 0;
9 repeat
10 writeln('Enter a number');
11 readln(k);
12 if k > -1 then
13 i := i + 1;
14 a[i] := k;
15 until(k < 0);
16 for i := 1 to i do
17 writeln(a[i]);
18 end.
In past I have mostly been a java developer so I was so custom to using all the lists thats available. Also ideally I was wondering if I can build a list where I can iterate over the list based on the number of elements in that list.
It would be great if anyone could point me on the direction of good tutorials in functional programming as well as syntax on above mentioned programming languages.
There are several problems with your program:
The array is not initialized.
There is no input checking, both i=0 and i>100 result in an illegal array index.
The array index and the value are the same, is that correct?
You only write the first 10 numbers (but you use a different index, which is certain to be out of range).
The output is not in reverse.
There are also several pascal tutorials.
By the way, Pascal isn't a functional language. So if you really want to learn a functional language, you better try another one (like Lisp, Ml or probably F#).
It was a good practice and I managed to figure a solution for this. I am sure there are better ways to do, and also this doesn't look like I am using the functionalities of functional programming. But if anyone wants to provide a better solution please do so,
{author: Null-Hypothesis}
program ReverseList;
var
i: Integer; {integer to keep the array length}
k: Integer; {user input value}
a: array[1..100] of Integer; {array to store the user inputs}
begin
i := 0;
repeat {iterate until user input is negative or number of inputs exceed array size}
writeln('Enter a number or enter negative value to exit the program.');
readln(k);
if(k > -1) and (i < 100) then {check for negative value and size of the array}
begin
i := i + 1; {increase array index}
a[i] := k {assign value to array}
end
else
break; {exit if array size exceed the limit of array}
until(k < 0);
writeln;
{Printing the user input before the reversing the list}
writeln('Original order of the list');
for i := 1 to i do
writeln(a[i]);
writeln;
{Printing the reverse list}
writeln('Reversed List');
for i := i downto 1 do {decrement array index}
writeln(a[i]);
writeln('Bye!!!');
end.
Happy Coding, off to the next language...

Array begin from 0 or 1 in Delphi 5.0 Pascal?

I want to do an ArrayList in Delphi 5.0. So I found a solution doing this code:
var arr: array of String;
OK, but every time I add something I do this:
var
Form1: TForm1;
var arr : array of String;
procedure TForm1.Button1Click(Sender: TObject);
var aux :string;
var len:integer;
begin
len := Length(arr) + 1;
SetLength(arr, len);
arr[len-1] := 'abc' + IntToStr(len);
Button1.Caption := arr[len-1]; // just to writeout something
end;
I'm a C++ programmer, and I do not know anything about Pascal. I always heard a Pascal index begins from 1, not 0. As in the above procedure I do arr[len-1] because of 0 index begin.
Is there a better way than Pascal arrays? Like with C++'s std::vector?
Dynamic arrays' indexes begin with zero
var
a: array of Integer;
begin
SetLength(a, 500);
a[0] := 0;
Static arrays can have arbitrary indexes
var
i: Integer;
b: array [50..100] of Integer;
c: array[-10..10] of Integer;
begin
for i := 50 to 100 do b[i] := i * i;
// Note negative starting index above in declaration
for i := -10 to 10 do c[i] := i * i;
Strings' indexes begin with one
var
c: String;
begin
c := 'Zap!';
c[1] := 'W';
ShowMessage(c); /// shows 'Wap!'
Anyway you can always use Low() and High() functions which return the lower and higher index of an array.
For handling a list of strings the most commonly used class is TStringList which is found in unit Classes.
What you're using is known as a dynamic array which is different from a Pascal classic array. Dynamic arrays are variable in size and the index is 0 based.
Classic Pascal arrays are not 0 nor 1 based... It's up to the programmer where the index start or ends. The only compiler restriction is that the index must be an ordinal type. You can declare
procedure x;
var
IntArr: array[50..75] of Integer;
StrArr: array[0..49] of string;
DblArr: array[1..10] of Double;
Delphi Pascal also has a nice feature that helps iterating through an array of any dimension:
Simply use for i:= Low(Array) to High(Array) do....
which is completely transparent to starting offset i.e. 0,1 or 5 or whatever.
I tried to edit the above answer to improve it but the editor keeps rejecting my posting. Arrays can have negative indexes.
var
A:array[-20..9] of integer;
B:array[-30..-10] of integer;
These are both the same, an array of 20 integers but will not be treated the same by the compiler because the index range is different. Allows you to make the data fit the problem domain, not the other way around.
Now, a string like var S:string[200]; is technically equivalent to var s:packed array[0..200] of char where byte 0 is the length except when you use a string with no length or the specified length is greater than 255, then the string is 1 to whatever current size it is. Because strings can be dynamic it's not good to depend on the 0th element to contain length.
When you use SetLength(array, length) it is worth mentioning that it has indexes starting from 0 as mentioned up to length-1. Also in pascal index on array can be character from ANSI table. So you can define array like a:array['A'..'Z'] of integer. This comes in handy when you need to count all characters in your Strings or Char Array.

Resources