Delphi - descending sort of string grid rows, sorting by 1 column - sorting

I've run into a bit of a wall with my sorting, I managed to sort the rows of my string grid from smallest to largest but now I'm not sure how to sort it in descending order. I've tried using the code I used from the other sort and I only changed the 2nd last loop in the code to see if I can read from the bottom of the TStringList, but it hasn't worked and only takes one row from the list and duplicates it into the rest of the rows. Is there perhaps a way to reverse read a TStringList after sorting?
Code I used for the other sort I have and tried to implement for this sort (only changed the 2nd last loop):
procedure TfrmPuntehou.SortLTSGrid(var grid: TStringGrid; columntotal: Integer);
const
separator = ',';
var
iCount,i,j,k,iPos:integer;
TheList:TStringList;
sString,sTempString:string;
m: Integer;
o: Integer;
begin
//procedure to sort from large to small values
//get row amount
iCount:=grid.RowCount-1;
//create list
TheList:=TStringList.Create;
TheList.Sorted:=False;
//start of try..finally block
try
begin
//fill the list
for i := 1 to (iCount - 1) do
begin
TheList.Add(grid.Rows[i].Strings[columntotal]+separator+grid.Rows[i].Text);
end;
//sort the list
TheList.Sort;
for k := 1 to TheList.Count do
begin
//take the line of the list and put it in a string var
sString:= TheList.Strings[(k-1)];
//get separator pos in that string
iPos:=AnsiPos(separator,sString);
sTempString:='';
//remove separator and the column text at the front of the string
sTempString:=Copy(sString,(iPos+1),Length(sString));
TheList.Strings[(k-1)]:= '';
TheList.Strings[(k-1)]:= sTempString;
end;
//fill the grid
for j:= (iCount - 1) downto 1 do
begin
for o := 1 to (iCount - 1) do
begin
grid.Rows[j].Text := TheList.Strings[(o-1)] ;
end;
end;
//fill the row numbers
for m := 1 to iCount do
begin
grid.Cells[0,m]:= IntToStr(m);
end;
end;
finally
TheList.Free;
end;
//end of try..finally block
end;
Thanks in advance for the help!Kind RegardsPrimeBeat

Use TStringList.CustomSort to sort the list using a specific method for comparison.
The specification for the comparer is given here.
Example:
function Compare1( // Normal alphanum sort
List : TStringList;
Index1 : Integer;
Index2 : Integer) : Integer;
begin
if List[Index1] = List[Index2] then
Result := 0
else if List[Index1] < List[Index2] then
Result := -1
else
Result := 1;
end;
function Compare2( // Reverse alphanum sort
List : TStringList;
Index1 : Integer;
Index2 : Integer) : Integer;
begin
if List[Index1] = List[Index2] then
Result := 0
else if List[Index1] < List[Index2] then
Result := 1
else
Result := -1;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
SList : TStringList;
S : String;
begin
SList := TStringList.Create;
try
SList.Add('Pierre');
SList.Add('Albert');
SList.Add('Paul');
SList.Add('Jean');
SList.Add('Simon');
Memo1.Lines.Add('=== Compare1 ===');
SList.CustomSort(Compare1);
for S in SList do
Memo1.Lines.Add(S);
Memo1.Lines.Add('=== Compare2 ===');
SList.CustomSort(Compare2);
for S in SList do
Memo1.Lines.Add(S);
finally
SList.Free;
end;
end;

Related

TStringList.CustomSort: Compare() with variables

I am trying to custom sort a TStringList by a column in a .CSV file. My code below works (slowly, about 14 seconds for 200,000+ lines):
function Compare(List: TStringList; Index1, Index2: Integer): Integer;
function ColStr(const Ch: Char; const S: String; First, Last: Integer): String;
var
p1, p2: Integer;
function GetPos(const N: Integer; Start: Integer = 1): Integer;
var
I, Len, Count: Integer;
begin
Result := 0;
Len := Length(S);
if (Len = 0) or (Start > Len) or (N < 1) then Exit;
Count := 0;
for I := Start to Len do begin
if S[I] = Ch then begin
Inc(Count);
if Count = N then begin
Result := I;
Exit;
end;
end;
end;
end;
begin
p1 := GetPos(4, 1); // 4 should be a variable
p2 := GetPos(5, 1); // 5 should be a variable
if Last = 0 then Result := Copy(S, p1 + 1, length(S)) else Result := Copy(S, p1 + 1, p2 - p1 - 1);
end;
begin
Result := AnsiCompareStr(ColStr(',', List[Index1], 0, 1), ColStr(',', List[Index2], 0, 1));
end;
What I would want to do is not have this hard-coded but (where commented "should be a variable" depending on which column to sort). I know I can't use:
function Form1.Compare(List: TStringList; Index1, Index2: Integer): Integer;
for inserting variables, as I get the error:
Incompatible types: 'method pointer and regular procedure'.
I have searched through SO looking for instances of this error but cannot find one that fits my question. I would appreciate any pointers in the right direction.
This has to be done with Delphi 7 and Windows 11.
TStringList.CustomSort() does not let you pass in extra parameters, and it does not accept class methods or anonymous procedures. But, what it does do is pass the actual TStringList itself to the callback, so I would suggest deriving a new class from TStringList to add extra fields to it, and then you can access those fields inside the callback, eg:
type
TMyStringList = class(TStringList)
public
Count1: Integer;
Count2: Integer;
end;
function Compare(List: TStringList; Index1, Index2: Integer): Integer;
...
p1 := GetPos(TMyStringList(List).Count1, 1);
p2 := GetPos(TMyStringList(List).Count2, 1);
...
begin
...
end;
...
List := TMyStringList.Create;
// fill List ...
List.Count1 := ...;
List.Count2 := ...;
List.CustomSort(Compare);
So you are performing searching for k-th occurence of Ch and substring creation at every comparison.
You can optimize this process - before sorting make list/array of stringlists, created from every string, separated by needed character - use DelimitedText.
Inside compare function just work with this array and column numbers - sadly, you have to define them as global variables in current unit (for example, after Form1: TForm1)

Not understanding array merging procedure in merge sort procedure

I've been trying to learn the procedure for merging two arrays in merge sort, and I've been given a fixed code which I have to strictly follow .The following is a program for sorting a list of numbers in ascending/ descending order depending on the user's choice is as follows:
const
max = 200000;
MaxDisp = 20;
type
list = array[1..max] of real;
var
a: list;
na: longint;
is_asc:boolean;
procedure GenList(var L: list; n: longint);
var
i: longint;
begin
randomize;
for i := 1 to n do begin
L[i] := random;
end;
end;
procedure DispList(L: list; n: longint);
var
i: longint;
begin
for i := 1 to MaxDisp do begin
if i <= n then begin
writeln(i:10, ' - ', L[i]:0:10);
end;
end;
if n > MaxDisp then begin
writeln(n - MaxDisp, ' more ...');
end;
end;
procedure sort(var L: list; n: longint;is_asc:boolean);
procedure Merge(L1,L2,R1,R2:longint);
var
M:list;//this is C
i1,i2,iM,i,j:longint;
begin
i1:=L1;
i2:=L2;
j:=1;
while (i1<=R1) and (i2<=R2) do begin
if (is_asc and (L[i1]<L[i2])) or not is_asc and (L[i1]<L[i2]) then begin
M[j]:=L[i1];
j:=j+1;
i1:=i1+1;
end
else begin
M[j]:=L[i2];
j:=j+1;
i2:=i2+1;
end;
j:=________;
end;
if(i1>R1) and (i2<=R2) then
for i:=________ do begin
M[j]:=_______;
end
else if (i2>R2) and (i1<=R1) then
for i:=1 to _______ do begin
_________;
end;
num:=_______;
i:=______;
for j:=1 to num do begin
_________;
end
end;
procedure MSort(LL,RR:longint);
var mid:integer;
begin
if LL<RR then begin
mid:=(LL+RR) div 2;
MSort(LL,mid);
MSort(mid+1,RR);
Merge(LL,mid,mid+1,RR);
end
end;
begin
MSort(1,n);
end;
function is_sorted(L: list; n:longint;is_asc:boolean): boolean;
var
i: longint;
flag: boolean;
begin
flag := true;
i := 1;
while flag and (i < n) do begin
flag := ((L[i]<=L[i+1]) and (is_asc)) or (not(is_asc) and (L[i]>=L[i+1]));
i := i + 1;
end;
is_sorted := flag;
end;
begin
na := MaxDisp;
GenList(a, na);
writeln(na, ' random items:');
DispList(a, na);
writeln('Press <Enter> to sort the list in ascending order ...');
readln;
sort(a, na,is_asc);
DispList(a, na);
writeln('Sorted in ascending order: ', is_sorted(a, na,is_asc));
write('Press <Enter> to continue ...');
readln;
end.
Except for the blank parts, I understand what the other parts of the code are doing, including the first part of the procedure Merge, which I think is just merging the arrays in L into M, and I think the following part is writing about the cases in case i1 and i2 are larger than L1 and L2, but I don't understand what the problem if this happens or what should be done. After this the following parts of the procedure Merge I have no idea what it is supposed to be doing.
I don't know this language (indexes start at 1?), but this is what I think needs changing:
i1:=L1;
i2:=L2;
j:=1;
while (i1<=R1) and (i2<=R2) do begin
if (is_asc and (L[i1]<L[i2])) or not is_asc and (L[i1]<L[i2]) then begin
M[j]:=L[i1];
j:=j+1;
i1:=i1+1;
end
else begin
M[j]:=L[i2];
j:=j+1;
i2:=i2+1;
end;
// j:=________; // don't change j
end;
while(i1 <= R1) do begin // copy rest of run 1 if any elements
M[j]:=L[i1];
j:=j+1;
i1:=i1+1;
end
while(i2 <= R2) do begin // copy rest of run 2 if any elements
M[j]:=L[i2];
j:=j+1;
i2:=i2+1;
end
for(i = 1 to j) do begin // copy M back into L
L[i+L1-1] := M[j]; // I'm not sure about the -1
end
end;

procedures don't work properly when dealing with files in pascal

In this program when I try to put the code blocks [1] and [2] in procedures they don't work properly when calling them, and when I keep them in the main program only the first one works properly and the second doesn't and when I comment out the first one the second one works as it's supposed...can you please spot the error, I think it's with getting the files names from the user cause when I choose the file's name it works properly
program Linked_lists_files;
type
Node = ^T;
T = record
num : integer;
next : Node;
End;
var
File1 : Text;
N, i, j, cmp, y, x, Num, matrixvalue : integer;
Head, Tail, Head2, Tail2, Head3, Tail3 : Node;
s : string;
matrix : array [1..20, 1..20] of Integer;
// procedures
// [1]
Procedure fillFile();
Begin
write('Input file name to create : ');
readln(s);
assign(File1,s);
rewrite(File1);
Repeat
write('Enter a number : ');
readln(N);
if (N>=0) then
writeln(File1, N);
Until (N<0);
close(File1);
End;
// [2]
Procedure GetFromFile();
Begin
cmp := 0;
write('Enter file name to read from : ');
readln(s);
assign(File1, s);
reset(File1);
while not eof(File1) Do
Begin
readln(File1, N);
if (Head = Nil) then
Begin
new(Head);
Tail := Head;
End
Else
Begin
new(Tail^.next);
Tail := Tail^.next;
End;
Tail^.num := N;
Tail^.next := Nil;
cmp := cmp + 1;
End;
Close(File1);
Write('Elements of the list : ');
Tail := Head;
if (Head<>nil) then
Begin
while(Tail <> nil) Do
Begin
write('[',Tail^.num,']', ' ');
Tail := Tail^.next;
End;
End
Else
Writeln('[!] The list is empty');
writeln;
writeln('Number of elements in the list : ', cmp);
End;
// [3]
Procedure SaveFromFile();
Begin
Head := Nil;
write('Enter file name to read from : '); readln(s);
writeln('[+] Fill in your list');
Repeat
write('+ Enter an integer : ');
read(y);
if (y>=0) then
Begin
if (Head = Nil) then
Begin
new(Head);
Tail := Head;
End
Else
Begin
new(Tail^.next);
Tail := Tail^.next;
End;
Tail^.num := y;
Tail^.next := Nil;
End;
Until y<0;
assign(File1, s);
rewrite(File1);
Tail := Head;
while(Tail <> nil) Do
Begin
Num := Tail^.num;
Tail := Tail^.next;
Writeln(File1, Num);
End;
Close(File1);
Writeln('[+] Elements of the lists have been successfully added to the new file');
End;
// [4]
Procedure SquareMatrix();
Begin
cmp := 0;
x := 0;
writeln('[+] Fill in your list');
Repeat
write('+ Enter an integer : ');
read(y);
if (y>=0) then
Begin
if (Head3 = Nil) then
Begin
new(Head3);
Tail3 := Head3;
End
Else
Begin
new(Tail3^.next);
Tail3 := Tail3^.next;
End;
Tail3^.num := y;
Tail3^.next := Nil;
cmp := cmp + 1;
End;
Until y<0;
while (cmp<>1) Do
Begin
if (cmp mod 2 <> 0) and (cmp <> 1) then
x := x + 1;
cmp := cmp div 2;
End;
if (x>0) then
writeln('[-] False')
Else
writeln('[+]True');
End;
// [5]
Procedure ElementsOfSM();
Begin
cmp := 0;
x := 0;
writeln('[+] Fill in your list');
Repeat
write('+ Enter an integer : ');
read(y);
if (y>=0) then
Begin
if (Head = Nil) then
Begin
new(Head);
Tail := Head;
End
Else
Begin
new(Tail^.next);
Tail := Tail^.next;
End;
Tail^.num := y;
Tail^.next := Nil;
cmp := cmp + 1;
End;
Until y<0;
Tail := Head;
i := 1;
j := 1;
while(Tail <> nil) Do
Begin
matrixvalue := Tail^.num;
matrix[i,j] := matrixvalue;
Tail := Tail^.next;
j := j + 1;
if (j = sqrt(cmp)+1) then
Begin
i := i + 1;
j := 1;
End;
End;
for i:=1 to cmp Do
for j:=1 to cmp Do
Begin
if (matrix[i,j]<>0) then
writeln('[',matrix[i,j],']',' : ','[',i,',',j,']');
End;
End;
// [6]
Procedure Element();
Begin
write('Enter the number of the line : ');
read(i);
write('Enter the number of the column : ');
read(j);
write('The element corresponding to ','[',i,',',j,'] is : ','[',matrix[i,j],']');
writeln;
End;
// [7]
Procedure WriteP();
Begin
write('Enter the number of the line : ');
read(i);
write('Enter the number of the column : ');
read(j);
write('Enter the value you want to pass in : ');
read(N);
matrix[i,j] := N;
writeln('The new matrix');
for i:=1 to cmp Do
for j:=1 to cmp Do
Begin
if (matrix[i,j]<>0) then
writeln('[',matrix[i,j],']',' : ','[',i,',',j,']');
End;
writeln;
End;
// [8]
Procedure Content();
Begin
write('Enter the number of the line : ');
read(i);
write('Enter the number of the column : ');
read(j);
writeln('The value stored inn this cell is : ','[', matrix[i,j],']');
End;
// Start of main program
Begin
Head := Nil;
Repeat
writeln('*********** MENU ***********');
writeln('[1] fillFile');
writeln('[2] GetFromFile');
writeln('[3] SaveFromFile');
writeln('[4] SquareMatrix');
writeln('[5] ElementsOfSM');
writeln('[6] Element');
writeln('[7] WriteP');
writeln('[8] Content');
writeln('*********** End ***********');
write('Choose one : ');
read(N);
case N of
1 : fillFile();
2 : GetFromFile();
3 : SaveFromFile();
4 : SquareMatrix();
5 : ElementsOfSM();
6 : Element();
7 : WriteP();
8 : Content();
End;
Until (N<>1) and (N<>2) and (N<>3) and (N<>4) and (N<>5) and (N<>6) and (N<>7) and (N<>8) and (N<>9);
End.
The problem you describe is being caused by the known behaviour of the Read statement; basically, the second (and subsequent) times you call it, it returns immediately, without waiting for any keyboard input and without reading anything.
This happen because FPC is closely based on Object Pacal in the commercial Delphi development package, and in Delphi's case, this is the officially documented behaviour.
From the Delphi (v7) online help:
Delphi syntax:
Text files:
procedure Read( [ var F: Text; ] V1 [, V2,...,Vn ] );
Description
The Read procedure can be used in Delphi code in the following ways.
[...]
With a type string variable:
Read reads all characters up to, but not including, the next end-of-line
marker or until Eof(F) becomes true; it does not skip to the next line after reading. If the resulting string is longer than the maximum length of the string variable, it is truncated.
After the first Read, each subsequent Read sees the end-of-line marker and returns a zero-length string (emphasis added).
Use multiple Readln calls to read successive string values.
Fortunately, the solution is simple, use readln, instead of read, as in
readln(s);
Update Make sure you replace all instances of read by readln, as you have left a number of them unchanged, as #TomBrunberg has commented.
After that, run your code again and select 1 from the menu and you will find that fillFile executes, but the program terminates on the until ... line. And that's because it is a very bad idea to use the same global variable, in this case N for several different purposes throughout the program. So, you should edit your code further (and carefully) so that as far as possible it only uses global variables for global purposes. Turn all all the other variables into local variables, preferably with different names than the global ones. If after that you are still having problems, submit a new question focused on that.

Insert values ordered from least to greatest in an Array

I need to create an array within a register that is a sequence of natural numbers, these are inserted by the user and cannot be repeated. My problem is sorting the array values as they are inserted in ascending order. I have tried this code without success. Thank you.
const
MAX = 10;
type
Natural = 0..MAXINT;
Secuencia = RECORD
valores : ARRAY [1..MAX] OF Natural;
tope : 0..MAX;
END;
TipoResultado = (Fallo, Creado, Agregado);
Resultado = RECORD
CASE quePaso : TipoResultado OF
Fallo: ();
Creado: ();
Agregado: (posicion: Natural);
END;
//Non-repeating value search function in array
Function BLineal(valor: Natural; sec: Secuencia ): boolean;
var i : integer;
begin
i := 1;
while (i <= sec.tope) and (sec.valores[i] <> valor) do
i := i + 1;
BLineal := i <= sec.tope
end;
Procedure to order values within the arrangement from smallest to largest by insertion.
//Procedure ORDER
Procedure OrdIns (var sec: Secuencia);
var
i,j: integer;
aux: Natural;
begin
for i := 2 to sec.tope do begin
j := i;
while (j >= 2) and ((sec.valores[j]) < (sec.valores[j-1])) do
begin
aux:=sec.valores[j-1];
sec.valores[j-1]:=sec.valores[j];
sec.valores[j]:=aux;
j := j - 1
end
end;
end;
Do the problem in two steps. First, create a binary tree and insert values as entered. Then when no more values are found simply walk the tree and create the array.

Pascal Bubble Sort

I have a project where the program must accept 10 words and display the words in descending order (alphabetical order from Z-A)
using bubble sorting.
Here's what I know so far:
Program sample;
uses crt;
TYPE
no._list=ARRAY(1...10)OF REAL;
CONST
no.:no._list=(20.00,50.50.35.70....);
VAR
x:INTEGER;
small:REAL;
BEGIN clrscr:
small:=no.(1);
FOR x:=2 TO 10 DO
IF small>number(x);
writeln('Smallest value in the array of no.s is',small:7:2);
END
I really don't know how to do this though and could use some help.
Here's a video by Alister Christie on Bubble sort describing the principle :
http://codegearguru.com/index.php?option=com_content&task=view&id=64&Itemid=1
The algorithm in Pascal can be found # http://delphi.wikia.com/wiki/Bubble_sort
function BubbleSort( list: TStringList ): TStringList;
var
i, j: Integer;
temp: string;
begin
// bubble sort
for i := 0 to list.Count - 1 do begin
for j := 0 to ( list.Count - 1 ) - i do begin
// Condition to handle i=0 & j = 9. j+1 tries to access x[10] which
// is not there in zero based array
if ( j + 1 = list.Count ) then
continue;
if ( list.Strings[j] > list.Strings[j+1] ) then begin
temp := list.Strings[j];
list.Strings[j] := list.Strings[j+1];
list.Strings[j+1] := temp;
end; // endif
end; // endwhile
end; // endwhile
Result := list;
end;

Resources