I must compare 2 stringlist , I wonder if the search the first stringlist inside the second stringlist is the only or the recommended version to execute this problem
My code would go like this
var
aFirstStrList: TStringList ;
aSecondStringList: TStringList;
MissingElement_firstElement_not_inside_second: TStringList;
MissingElement_SecondElement_not_inside_First: TStringList;
...
for i := 0 to aFirstStrList.Count - 1 do
begin
if aSecondStringList.IndexOf(aFirstStrList[i] < 0 ) then
begin
// react on not found elements
....
MissingElement_firstElement_not_inside_second.add(...);
end;
end;
// and now same code just opposite search direction ....
....
Instead of using IndexOf, sort both lists before, to prevent searching the whole list at every cycle. Furthermore, it is possible to achieve this in one single loop.
Assuming you want the results in two separate string lists, try the following:
procedure CompareStringLists(List1, List2: TStringList;
Missing1, Missing2: TStrings);
var
I: Integer;
J: Integer;
begin
List1.Sort;
List2.Sort;
I := 0;
J := 0;
while (I < List1.Count) and (J < List2.Count) do
begin
if List1[I] < List2[J] then
begin
Missing2.Add(List1[I]);
Inc(I);
end
else if List1[I] > List2[J] then
begin
Missing1.Add(List2[J]);
Inc(J);
end
else
begin
Inc(I);
Inc(J);
end;
end;
for I := I to List1.Count - 1 do
Missing2.Add(List1[I]);
for J := J to List2.Count - 1 do
Missing1.Add(List2[J]);
end;
Usage:
procedure TForm1.Button1Click(Sender: TObject);
var
List1: TStringList;
List2: TStringList;
begin
List1 := TStringList.Create;
List2 := TStringList.Create;
try
List1.CommaText := 'A, C, F, G, H, K, L, M, N, O, Q, R';
List2.CommaText := 'C, D, E, F, J, P, Q, S, T, U, V, W';
Memo1.Lines.Assign(List1);
Memo2.Lines.Assign(List2);
CompareStringLists(List1, List2, Memo3.Lines, Memo4.Lines);
finally
List2.Free;
List1.Free;
end;
end;
The best performance:
Sort input string lists
Compare with algorithm similar to merge sorted lists(there you will find what is missing in each list)
Simpler but slower:
Copy stringlists to result stringlists
One loop for first string list (use reversed loop for this for Count-1 downto 0 do)
Search same string in second, if found - delete from both lists
Differences will remain in string lists
You have to be careful with that implementation of CompareStringLists. By default the Sort routine of TStringList will sort case insensative but the Comparison used is case-sensative, the up shot being if you have any mixed case in your lists you will get a blowout of the supposed miss-matches.
Either need to set the sorts to be case-sensative by
List1.CaseSensative := true ;
List1.Sort ;
List2.CaseSensative := true ;
List2.Sort ;
OR
Make the comparisons case-insensative
if UpperCase(List1[I]) < UpperCase(List2[J]) then
and
else if UpperCase(List1[I]) > UpperCase(List2[J]) then
respectively.
Related
procedure ReverseArray(var A : array of string);
var I,J,L : integer;
begin
for I := Low(A) to High(A) do
begin
L := length(A[I]);
for J := L downto 1 do M := M + A[I];
end;
writeln(M);
end;
begin
for I := 1 to 4 do readln(T[I]);
ReverseArray(T);
sleep(40000);
end.
What I'm trying to do here basically is reverse every string in the array but I'm unable to do it , what the code above do is basically repeat the words depends on their length (I write 'bob' in the array , the procedure will give me 'bob' three times because the length is 3) ... not sure why it's not working properly and what I'm missing
Delphi has a ReverseString() function in the StrUtils unit.
uses
StrUtils;
type
TStrArray = array of string;
procedure ReverseArray(var A : TStrArray);
var
I: integer;
begin
for I := Low(A) to High(A) do
A[I] := ReverseString(A[I]);
end;
var
T: TStrArray;
I: Integer
begin
SetLength(T, 4);
for I := 1 to 4 do Readln(T[I]);
ReverseArray(T);
...
end.
A string is an array of char with some extra bells and whistles added.
So an array of string is a lot like an array of array of char.
If you want to reverse the string, you'll have to access every char and reverse it.
procedure ReverseArray(var A : array of string);
var
i,j,Len : integer;
B: string;
begin
for i := Low(A) to High(A) do begin
Len := length(A[i]);
SetLength(B, Len); //Make B the same length as A[i].
//B[Len] = A[i][1]; B[Len-1]:= A[i][2] etc...
for j := Len downto 1 do B[j]:= A[i][(Len-J)+1];
//Store the reversed string back in the array.
A[i]:= B;
//Because A is a var parameter it will be returned.
//Writeln(B); //Write B for debugging purposes.
end;
end;
var
i: integer;
Strings: array [0..3] of string;
begin
for i := 0 to 3 do readln(Strings[i]);
ReverseArray(Strings);
for i := 0 to 3 do writeln(Strings[i]);
WriteLn('Done, press a key...');
ReadLn;
end.
Some tips:
Do not use global variables like M but declare a local variable instead.
Don't do AStr:= AStr + AChar in a loop, if you can avoid it. If you know how long the result is going to be use the SetLength trick as shown in the code. It's generates much faster code.
Instead of a Sleep you can use a ReadLn to halt a console app. It will continue as soon as you press a key.
Don't put the writeln in your working routine.
Note the first element in a string is 1, but the first element in a array is 0 (unless otherwise defined); Dynamic arrays always start counting from zero.
Note that array of string in a parameter definition is an open array; a different thing from a dynamic array.
Single uppercase identifiers like T, K, etc are usually used for generic types, you shouldn't use them for normal variables; Use a descriptive name instead.
Come on! 'bob' is one of those words you shouldn't try to test a reverse routine. But the problem goes beyond that.
Your problem is in here
for J := L downto 1 do
M := M + A[I];
You are trying to add the whole string to the M variable instead of the character you are trying to access. So, it should be
for J := L downto 1 do
M := M + A[I][J];
Also you need to set M := '' inside the first loop where it will have nothing when you start accumulating characters in to it.
Third, move the writing part, WriteLn(M), inside the first loop where you get a nice, separated outputs.
Putting together, it is going to be:
for I := Low(A) to High(A) do
begin
L := length(A[I]);
M := '';
for J := L downto 1 do
M := M + A[I][J];
writeln(M);
end;
My preferred solution for this is
type
TStringModifier = function(const s: string): string;
procedure ModifyEachOf( var aValues: array of string; aModifier: TStringModifier );
var
lIdx: Integer;
begin
for lIdx := Low(aValues) to High(aValues) do
aValues[lIdx] := aModifier( aValues[lIdx] );
end;
and it ends up with
var
MyStrings: array[1..3] of string;
begin
MyStrings[1] := '123';
MyStrings[2] := '456';
MyStrings[3] := '789';
ModifyEachOf( MyStrings, SysUtils.ReverseString );
end;
uses
System.SysUtils, System.StrUtils;
var
Forwards, backwards : string;
begin
forwards:= 'abcd';
backwards:= ReverseString(forwards);
Writeln(backwards);
Readln;
end;
// dcba
I'm trying to remove the same element of array in delphi.
For examples :
R[1] := 33332111111111111111111111323333333334378777433333344333333333277
I want to make it become 32132343787434327. and saved in the new array.
Could you give some idea?
I already tried to make each of R[1] element to Array. And tried some code.
for i:=1 to length(NR) do
begin
found:=false;
for k:=i+1 to length(NR) do
begin
if (NR[i]=NR[k]) then
begin
found:=true;
end;
end;
if (not found) then
begin
Memo1.Lines.Add(NR[i]);
end;
end;
But the result is 184327.
could you guys help me? thanks a lot. I'm so desperate to do this.
You appear to be working with strings rather than arrays. In which case you need this function:
function RemoveAdjacentDuplicates(const X: string): string;
var
i, j: Integer;
begin
SetLength(Result, Length(X));
j := 0;
for i := 1 to Length(Result) do
if (i=1) or (X[i]<>X[i-1]) then
begin
inc(j);
Result[j] := X[i];
end;
SetLength(Result, j);
end;
Let's work through this.
First of all I allocate the result variable. This is likely to be an over allocation. We know that the result cannot be larger than the input.
We use two indexing local variables, the rather weakly named i and j. We could give them descriptive names but for such a short function one might decide that it was not necessary. Do feel free to come up with other names if you prefer. You might choose idxIn and idxOut for instance.
One variable indexes the input, the other indexes the output. The input index is used in a simple for loop. The output index is incremented every time we find a unique item.
The if condition tests whether the input index refers to a character that differs from the previous one. The first element has no previous element so we always include it.
Once the loop completes we know how long the output is and can perform the final allocation.
Adapting this for an array is simple. You just need to account for arrays using zero-based indexes. For a bit of fun, here's a generic version for arrays:
type
TMyArrayHelper = class
class function RemoveAdjacentDuplicates<T>(const X: array of T): TArray<T>;
static;
end;
class function TMyArrayHelper.RemoveAdjacentDuplicates<T>
(const X: array of T): TArray<T>;
var
i, j: Integer;
Comparer: IEqualityComparer<T>;
begin
Comparer := TEqualityComparer<T>.Default;
SetLength(Result, Length(X));
j := 0;
for i := 0 to high(Result) do
if (i=0) or not Comparer.Equals(X[i], X[i-1]) then
begin
Result[j] := X[i];
inc(j);
end;
SetLength(Result, j);
end;
Note the subtly different placement of inc(j). This is necessitated by the switch to zero-based indexing.
A slightly more complex alternative with fewer tests would be:
class function TMyArrayHelper.RemoveAdjacentDuplicates<T>
(const X: array of T): TArray<T>;
var
i, j, len: Integer;
Comparer: IEqualityComparer<T>;
begin
Comparer := TEqualityComparer<T>.Default;
len := Length(X);
SetLength(Result, len);
if len=0 then
exit;
Result[0] := X[0];
j := 1;
for i := 1 to len-1 do
if not Comparer.Equals(X[i], X[i-1]) then
begin
Result[j] := X[i];
inc(j);
end;
SetLength(Result, j);
end;
Here is my current code:
function StudentQuickSort(StudentList:TStudentArray;ArrayLength:integer):TStudentArray;
var
Pivot:TstudentArray;
LesserList:TStudentArray;
GreaterList:TstudentArray;
ArrayCount:Integer;
LesserCount:Integer;
GreaterCOunt:integer;
procedure ConcatArrays(const A,B,C: TStudentArray; var D: TStudentArray);
var i, nA,nB,nC: integer;
begin
nA := length(A);
nB := length(B);
nC := Length(C);
SetLength(D,nA+nB+nC);
for i := 0 to nA-1 do
D[i] := A[i];
for i := 0 to nB-1 do
D[i+nA] := B[i];
for i := 0 to nC-1 do
D[i+nA+nB] := C[i];
end;
begin
if Arraylength<=1 then
begin
Result:=(StudentList);
end
else
begin
SetLength(StudentList,ArrayLength);
SetLength(LesserList,ArrayLength);
SetLength(GreaterList,ArrayLength);
SetLength(Pivot,1);
LesserCOunt:=0;
GreaterCount:=0;
Pivot[0]:=StudentList[0];
for ArrayCount := 1 to ArrayLength-1 do
begin
if strtoint(StudentList[ArrayCount].StudentNo)>strtoint(Pivot[0].StudentNo) then
begin
GreaterList[GreaterCOunt]:=StudentList[ArrayCount];
GreaterCount:=GreaterCount+1;
end
else
begin
LesserList[LesserCOunt]:=StudentList[ArrayCount];
LesserCount:=LesserCount+1;
end;
end;
SetLength(LesserLIst,LesserCount);
SetLength(GreaterList,GreaterCount);
ConcatArrays(StudentQuickSort(LesserList,LesserCount),Pivot,StudentQuickSort(GreaterList,GreaterCount),Result)
end;
end;
How can this be stabilized, ideally changing as little code as possible. IS it a problem with using dynamic arrays? I need to be able to sort through at least 600 records without error.
Your code cannot be salvaged. You are going about solving this problem in the wrong way and I advise you to abandon your existing code. Here is how I believe sorting should be done.
Note that I am assuming that you don't have generics available to you. In modern Delphi versions you can use TArray.Sort<T> from Generics.Collections to sort. If you have access to that, you should use it
First of all the key is to separate the sorting from the array being sorted. To achieve that define the following types:
type
TCompareIndicesFunction = function(Index1, Index2: Integer): Integer of object;
TExchangeIndicesProcedure = procedure(Index1, Index2: Integer) of object;
The point is that all the common algorithms that can sort an array need only to be able to compare two items, and exchange two items. These procedural types enable separation of the sorting algorithm from the underlying array storage and types.
With these definitions in place, we are ready to write our general purpose sorting algorithms. For quicksort it looks like this:
procedure QuickSort(Count: Integer; Compare: TCompareIndicesFunction;
Exchange: TExchangeIndicesProcedure);
procedure Sort(L, R: Integer);
var
I, J, P: Integer;
begin
repeat
I := L;
J := R;
P := (L+R) div 2;
repeat
while Compare(I, P)<0 do inc(I);
while Compare(J, P)>0 do dec(J);
if I<=J then
begin
if I<>J then
begin
Exchange(I, J);
//may have moved the pivot so we must remember which element it is
if P=I then
P := J
else if P=J then
P := I;
end;
inc(I);
dec(J);
end;
until I>J;
if L<J then
Sort(L, J);
L := I;
until I>=R;
end;
begin
if Count>0 then
Sort(0, Count-1);
end;
In order to use this you need to wrap your array in a class which exposes compare and exchange methods.
I'm coding this function where if a string differs only by one character, returns the distinct characters position, if they're right the same is supposed to return -1 and -10 in the case they differ by more than 1 character.
Just for giving and example, '010' and '110' or '100' and '110' works good, returning 0 and 1 each...
However, when I try with '100' and '101'or with '110' and '111' I get a result of -1 when it should be 2! I've done the desktop testing and I can't just see the mistake.
function combine (m1, m2 : string) : integer;
var
dash : integer;
distinct : integer;
i : integer;
begin
distinct := 0;
dash := -1;
for i := 0 to Length(m1)-1 do
begin
if m1[i] <> m2[i] then
begin
distinct := distinct+1;
dash := i;
if distinct > 1 then
begin
result:= -10;
exit;
end;
end;
end;
result := dash;
end;
I'm always getting same length strings,
What am I doing wrong?
The main problem is that Delphi strings are 1-based. Your loop needs to run from 1 to Length(m1).
If you enabled range checking in the compiler options, then the compiler would have raised an error at runtime which would have led you to the fault. I cannot stress enough that you should enable range checking. It will lead to the compiler finding errors in your code.
Note also that this means that the returned values will also be 1-based. So an input of '100', '101' will give the result 3 since that is the index of the first difference.
You should also check that m1 and m2 are the same length. If not raise an exception.
Another tip. The idiomatic way to increment a variable by 1 is like so:
inc(distinct);
If you want to increment by a different value write:
inc(distinct, n);
So, I would write the function like this:
function combine(const m1, m2: string): integer;
var
i: integer;
dash: integer;
distinct: integer;
begin
if Length(m1)<>Length(m2) then begin
raise EAssertionFailed.Create('m1 and m2 must be the same length');
end;
distinct := 0;
dash := -1;
for i := 1 to Length(m1) do
begin
if m1[i] <> m2[i] then
begin
inc(distinct);
dash := i;
if distinct > 1 then
begin
result := -10;
exit;
end;
end;
end;
result := dash;
end;
I want to randomize a number of string lists.
The string lists all contain the same number of items, and I wish to apply the same shuffle to each list. So if List1[0] is swapped with List1[7], then I want to swap List2[0] with List2[7], and so on for all the lists.
I'm going to consider the case where you have two lists. I'll leave it up to you to generalise the ideas to handle more than two lists. The key understanding is best gained using the most simple case where there are two lists.
I would solve the problem like this:
Generating a permutation of the integers 0, 1, ... N-1. Use the Fisher–Yates shuffle to achieve this.
Using that permutation to shuffle both lists.
The key is to use the same permutation to shuffle both lists.
type
TIntegerArray = array of Integer;
procedure Swap(var i1, i2: Integer); overload;
var
tmp: Integer;
begin
tmp := i1;
i1 := i2;
i2 := tmp;
end;
function GeneratePermutation(Count: Integer): TIntegerArray;
//Fisher-Yates shuffle
var
i, j: Integer;
begin
SetLength(Result, Count);
for i := 0 to Count-1 do
Result[i] := i;
for i := Count-1 downto 1 do begin
j := Random(i+1);
Swap(Result[i], Result[j]);
end;
end;
procedure ApplyPermutation(List: TStringList;
const Permutation: TIntegerArray);
var
i: Integer;
Temp: TStringList;
begin
Assert(List.Count=Length(Permutation));
Temp := TStringList.Create;
try
Temp.Assign(List);
for i := 0 to List.Count-1 do
List[i] := Temp[Permutation[i]];
finally
Temp.Free;
end;
end;
And then you can apply to your situation like this:
Permutation := GeneratePermutation(List1.Count);
Apply(List1, Permutation);
Apply(List2, Permutation);
This is an exceedingly general solution that can be extended to more than two lists, and can be applied to other data types. If you want a very short and simple dedicated routine then you can do it like this:
procedure PermuteListsInTandem(List1, List2: TStringList);
var
i, j: Integer;
begin
Assert(List1.Count=List2.Count);
for i := List1.Count-1 downto 1 do begin
j := Random(i+1);
List1.Exchange(i, j);
List2.Exchange(i, j);
end;
end;
I'm struggling to think of a good name for this procedure. Can anyone help me out by offering something better?