### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Access the next occurrence of an argument in a TStringList  (Read 3688 times)

#### nightrider43

• Newbie
• Posts: 2
##### Access the next occurrence of an argument in a TStringList
« on: June 06, 2024, 06:55:40 pm »
Hi pals!

Suppose I created a list with TStringList and I used the IndexOf method to find an item in the list. Let's assume it was found in element 69 of the list.
If I want to search for new occurrences of the same search argument first found in element 69, how should I proceed?
Imagine that after item 69 already mentioned, the search argument occurs again in item 93 of the list. How do I query the next occurrence after the first one I found and so on?

Greetings

Ari Ricardo
Sao Paulo, Brazil

#### cdbc

• Hero Member
• Posts: 1424
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #1 on: June 06, 2024, 08:08:29 pm »
Hi
One way, would be like this:
Code: Pascal  [Select][+][-]
1. type
2.   TScanResult = record
3.     srIdx: integer;
4.     srObj: TObject;
5.     srValue: string;
6.   end;
7.   TScanResultArray = array of TScanResult;
8.
9. function StringlistScanFor(aList: TStrings;
11.                            aStrict: boolean = false): TScanResultArray;
12.
13. implementation
14.
15. function StringlistScanFor(aList: TStrings; aMask: string; aStrict: boolean): TScanResultArray;
16. const szAlloc = 100;
17. var
18.   acnt: integer = 0;
19.   i: integer;
20.
21.   procedure AddToArray(var ra: TScanResultArray; anIdx: integer; aStr: string; anObj: TObject);
22.   begin
23.     if acnt = length(ra) then setlength(ra,length(ra)+szAlloc);
24.     ra[acnt].srIdx:= anIdx;
25.     ra[acnt].srObj:= anObj;
26.     ra[acnt].srValue:= aStr;
27.     inc(acnt);
28.   end;
29.
30. begin { main }
31.   if ((aList = nil) or (aMask = '')) then exit(nil);
32.   for i:= 0 to aList.Count-1 do begin
33.     case aStrict of
36.     end;
37.   end;
38.   SetLength(Result,acnt);
39. end;
40.
Only quickly tested and it seems to work as expected
edit:
Oh, I tested like this:
Code: Pascal  [Select][+][-]
1. var
2.   isl: IStringList;
3.   res: TScanResultArray;
4.   ...
5.   ...
6.   writeln('Testing StrLst Scan... Push enter'); readln;
7.   res:= StringlistScanFor(isl.List,'Benny',false);
8.   for i:= low(res) to high(res) do writeln(res[i].srIdx,': ',res[i].srValue);
9.   res:= [];
10.   res:= StringlistScanFor(isl.List,'# Bennys Lazarus account',true);
11.   for i:= low(res) to high(res) do writeln(res[i].srIdx,': ',res[i].srValue);
12.   res:= [];
13.
Where 'isl' is an IStringList and 'isl.List' is a TStringlist;
isl is loaded with a file containing some strings with my name in.
The result looks like this:
Quote
4: Author:     Benny Christensen /bc
47: # Bennys MitID
51: # Bennys Danske Bank konto
57: # Bennys Xiaomi Redmi 11 Pro 5G
78: # Bennys Lazarus account
82: # Bennys dropbox account primær
86: # Bennys dropbox sekundær
136: # Bennys test konto hos google for udvikling
144: # Bennys Ebay konto
148: # Bennys Netflix account
164: # Bennys primære email
168: # Bennys sekundære email
172: # Bennys tertiære email
188: # Bennys login på red-devil
78: # Bennys Lazarus account
HTH
Regards Benny
« Last Edit: June 06, 2024, 08:51:40 pm by cdbc »
If it ain't broke, don't fix it
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

#### ASerge

• Hero Member
• Posts: 2301
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #2 on: June 07, 2024, 01:39:27 am »
How do I query the next occurrence after the first one I found and so on?
Code: Pascal  [Select][+][-]
1. {\$MODE OBJFPC}
2. {\$APPTYPE CONSOLE}
3. {\$LONGSTRINGS ON}
4.
5. uses Classes;
6.
7. var
8.   L: TStringList;
9.   Index: SizeInt;
10. begin
11.   L := TStringList.Create;
12.   try
13.     L.CommaText := '1,2,3,2,4,5';
14.     Index := L.IndexOf('2');
15.     Writeln('First result: ', Index);
16.     if Index >= 0 then
17.       Index := TStrings(L).IndexOf('2', Index + 1);
18.     Writeln('Second result: ', Index);
19.   finally
20.     L.Free;
21.   end;
23. end.

#### Remy Lebeau

• Hero Member
• Posts: 1364
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #3 on: June 07, 2024, 03:17:54 am »
Code: Pascal  [Select][+][-]
1. Index := TStrings(L).IndexOf('2', Index + 1);

You shouldn't need the type-cast since TStringList inherits from TStrings and has access to all of the public TStrings methods:

Code: Pascal  [Select][+][-]
1. Index := L.IndexOf('2', Index + 1);
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

#### ASerge

• Hero Member
• Posts: 2301
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #4 on: June 07, 2024, 03:59:07 am »
You shouldn't need the type-cast since...
Should. TStringList overrides only one indexOf. Try it yourself.

#### cdbc

• Hero Member
• Posts: 1424
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #5 on: June 07, 2024, 07:41:17 am »
Hi
Thanks @ASerge, that was golden insight on your part, I totally overlooked it.
I've just now implemented, surfacing the method in my "IStringList", so if you use that, you don't have to typecast...
If it could be of interest, you can find it(v 5.07.06.2024) here: https://gitlab.com/cdbc-public/ibcstringlist
Regards Benny
If it ain't broke, don't fix it
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

#### ASerge

• Hero Member
• Posts: 2301
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #6 on: June 07, 2024, 04:46:30 pm »
If it could be of interest, you can find it(v 5.07.06.2024) here: https://gitlab.com/cdbc-public/ibcstringlist
OK, I looked at the source code. There are questions and suggestions.

1. You can use a constant when declaring the interface IID
Code: Pascal  [Select][+][-]
1. IStringList = interface(IInterface)[SGUIDIStringList]

2. It is necessary to repeat the method header in the implementation. Example:
Code: Pascal  [Select][+][-]
1. function QueryInterface({\$IFDEF FPC_HAS_CONSTREF}constref{\$ELSE}const{\$ENDIF} iid : tguid;out obj) : longint;{\$IFNDEF WINDOWS}cdecl{\$ELSE}stdcall{\$ENDIF};
2. ...
3. function TiStringList.QueryInterface({\$IFDEF FPC_HAS_CONSTREF}constref{\$ELSE}const{\$ENDIF} iid : tguid;out obj) : longint;{\$IFNDEF WINDOWS}cdecl{\$ELSE}stdcall{\$ENDIF};
4.
Otherwise, as in your case, you got an explicit cdecl declaration in the implementation, because you edited on unix, but on Windows there should be stdcall, and the unit is not even compiled.

3. Why are some of the functions in the implementation virtual? Are you going to make several implementations?

4. There is no need for a constructor that just calls the inherited one and does nothing by itself.

5. You have copied the implementation code for the indexOf function. Maybe it was easier this way:
Code: Pascal  [Select][+][-]
1. function TiStringList.IndexOf(const S: string): Integer;
2. begin
3.   Result := inherited;
4. end;
5.
6. function TiStringList.IndexOf(const S: string; aStart: Integer): Integer;
7. begin
8.   Result := TStrings(Self).IndexOf(S, aStart);
9. end;

5. There is no need for the IslStringsEnumerable type at all. The compiler recognizes GetEnumerator.

#### cdbc

• Hero Member
• Posts: 1424
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #7 on: June 07, 2024, 06:03:32 pm »
Hi
@ASerge: Thanks mate.
1) I simply forgot halfway... what I was doing.
2) I didn't know that and overlooked it, so used to 'cdecl', that I don't see it,
fixed it.
3) I usually make protected methods virtual, just in case, but no, I don't
think I'll make more versions... so I removed them.
4) Removed, you're right, it's enough TStringList does it
5) I kinda forgot how it goes with skipping an override, so I lifted the code.
EDIT: Tried your code and got a fat AV in that exact line 'TStrings(Self)...'
The copied code stays.
6) Dotting the i's and crossing the t's... and didn't know that.

Thank you for the review, I've followed your suggestions and learnt a couple of new things along the way
Regards Benny
« Last Edit: June 07, 2024, 06:39:03 pm by cdbc »
If it ain't broke, don't fix it
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

#### Remy Lebeau

• Hero Member
• Posts: 1364
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #8 on: June 07, 2024, 06:30:26 pm »
You shouldn't need the type-cast since...
Should. TStringList overrides only one indexOf.

And? Yes, it overrides a virtual method, but that shouldn't change the visibility of other overloads.  Both overloads of TStrings.IndexOf() are public, and so both should be callable on a TStringList object variable without casting.  If that is not the case in FreePascal, then I would consider that to be a compiler bug.

Try it yourself.

I don't have FreePascal installed.  But it works fine in Delphi.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

#### bytebites

• Hero Member
• Posts: 657
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #9 on: June 07, 2024, 08:55:59 pm »
If you add overload directive then Index := L.IndexOf('2', Index + 1);  works without type cast.
Code: Pascal  [Select][+][-]
1. TStringList = class(TStrings)
2.   function IndexOf(const S: string): Integer; override;

#### nightrider43

• Newbie
• Posts: 2
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #10 on: June 07, 2024, 09:46:46 pm »
Code: [Select]
`I'll test it and use it if it really works. I just don't understand how the second call uses the IndexOf method with a different signature than the one in the documentation (see marking in the code snippet below).Thank you very much for the answer and I apologize for not understanding.uses Classes;var  L: TStringList;  Index: SizeInt;begin  L := TStringList.Create;  try    L.CommaText := '1,2,3,2,4,5';    Index := L.IndexOf('2');    Writeln('First result: ', Index);    if Index >= 0 then      Index := TStrings(L).IndexOf('2', Index + 1); <-- This signature differs of that is explained in the documentation.    Writeln('Second result: ', Index);  finally    L.Free;  end;  Readln;end.`[/quote]

#### cdbc

• Hero Member
• Posts: 1424
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #11 on: June 07, 2024, 09:53:30 pm »
Hi
There are 2 of them:
Code: Pascal  [Select][+][-]
1.     // from TStrings...
2.     function IndexOf(const S: string): Integer; overload;
3.     function IndexOf(const S: string; aStart : Integer): Integer; overload;
Which one you call depends on the parameters passed in.
Regards Benny
If it ain't broke, don't fix it
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

#### ASerge

• Hero Member
• Posts: 2301
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #12 on: June 08, 2024, 04:00:29 am »
If that is not the case in FreePascal, then I would consider that to be a compiler bug.
I agree, but are where we are.
A simple example that is not compiled in FPC:
Code: Pascal  [Select][+][-]
1. {\$MODE OBJFPC}
2. {\$LONGSTRINGS ON}
3.
4. type
5.   TBase = class
6.   public
7.     procedure Method(V: SizeInt); virtual; // with overload; compiled
8.     procedure Method(const V: string); virtual;
9.   end;
10.
11.   TDerived = class(TBase)
12.     procedure Method(V: SizeInt); override;
13.   end;
14.
15. procedure TDerived.Method(V: SizeInt);
16. begin
17.   inherited;
18. end;
19.
20. procedure TBase.Method(V: SizeInt);
21. begin
22. end;
23.
24. procedure TBase.Method(const V: string);
25. begin
26. end;
27.
28. var
29.   C: TDerived; // with TBase - compiled
30. begin
31.   C := TDerived.Create;
32.   try
33.     C.Method('12'); // project1.lpr(33,18) Error: Incompatible type for arg no. 1: Got "Constant String", expected "Int64"
34.   finally
35.     C.Free;
36.   end;
37. end.

This example also does not compile in Delphi, because the overload directive is mandatory.

#### cdbc

• Hero Member
• Posts: 1424
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #13 on: June 08, 2024, 07:40:36 am »
Hi
@ASerge: Those are exactly the 'Shenanigans' I ran into...
Regards Benny
If it ain't broke, don't fix it
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

#### Remy Lebeau

• Hero Member
• Posts: 1364
##### Re: Access the next occurrence of an argument in a TStringList
« Reply #14 on: June 08, 2024, 08:32:34 pm »
A simple example that is not compiled in FPC:

Which seems to contradict FreePascal's documentation in this case:

https://www.freepascal.org/docs-html/ref/refsu86.html

Quote
The overload modifier tells the compiler that this function is overloaded. It is mainly for Delphi compatibility, as in Free Pascal, all functions and procedures can be overloaded without this modifier.

But, the examples given in that page are for free-standing functions, not class methods.  Maybe that makes a difference?

This example also does not compile in Delphi, because the overload directive is mandatory.

In Delphi's case, yes.  FreePascal is supposedly more lenient on this.
« Last Edit: June 08, 2024, 08:34:40 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)