Recent

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

nightrider43

  • Newbie
  • Posts: 4
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: 1644
    • http://www.cdbc.dk
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;
  10.                            aMask: string;
  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
  34.       false: if pos(aMask,aList[i]) > 0 then AddToArray(Result,i,aList[i],aList.Objects[i]);
  35.       true: if aMask = aList[i] then AddToArray(Result,i,aList[i],aList.Objects[i]);
  36.     end;
  37.   end;
  38.   SetLength(Result,acnt);
  39. end;
  40.  
Only quickly tested and it seems to work as expected  :D
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
90: # Facebook "Benny Christensen"
136: # Bennys test konto hos google for udvikling
144: # Bennys Ebay konto
148: # Bennys Netflix account
153: login:  BennyC123
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  8-)
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: 2336
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;
  22.   Readln;
  23. end.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1426
    • Lebeau Software
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: 2336
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: 1644
    • http://www.cdbc.dk
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: 2336
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: 1644
    • http://www.cdbc.dk
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  8)
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: 1426
    • Lebeau Software
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: 680
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: 4
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: 1644
    • http://www.cdbc.dk
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: 2336
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: 1644
    • http://www.cdbc.dk
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: 1426
    • Lebeau Software
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)

 

TinyPortal © 2005-2018