GTAVLover
GTAVLover

Reputation: 1427

Pascal Script Count number of times a string occurs in another string

I want to count how many times a String occurs in another String in Pascal Script like shown in the below example.

I've seen the answer to Delphi: count number of times a string occurs in another string, but there is no PosEx function in Pascal Script.

MyString := 'Hello World!, Hello World!, Hello World!, Hello World!';

If I count the number of times Hello or World occurs here, the result should be 4.

If I count the number of times , (comma) occurs here, the result should be 3.

UPDATE

The following function works, but it copies given String again to a new Variable, and deletes parts of Strings, so it works slowly.

function OccurrencesOfSubString(S, SubStr: String): Integer;
var
  DSStr: String;
begin
  if Pos(SubStr, S) = 0 then
    Exit
  else
    DSStr := S;
  Repeat
    if Pos(SubStr, S) <> 0 then
    Inc(Result);
    Delete(DSStr, Pos(SubStr, DSStr), Length(Copy(DSStr, Pos(SubStr, DSStr), Length(SubStr))));
  Until Pos(SubStr, DSStr) = 0;
end;

Upvotes: 2

Views: 1429

Answers (1)

Martin Prikryl
Martin Prikryl

Reputation: 202534

Your implementation is generally correct.

There are some optimizations to be made and useless code to be removed:

  • The second test for if Pos(SubStr, S) <> 0 (within repeat) is pointless. It's true always. You are testing S, which was tested at the function start already. And the DSStr is already tested in the until.
  • You should save Pos(SubStr, DSStr) to a variable not to call it multiple times.
  • Length(Copy(DSStr, Pos(SubStr, DSStr), Length(SubStr))) is actually the same as Length(SubStr).
  • No need to copy the S to DSStr. You can work directly with the S. It's by-value parameter, so you do not modify the variable that you use to call the function.
  • Replace the initial Pos(SubStr, S) = 0 check with the same check in the loop to save one Pos call.

Optimized version of your code:

function OccurrencesOfSubString(S, SubStr: String): Integer;
var
  P: Integer;
begin
  Result := 0;
  repeat
    P := Pos(SubStr, S);
    if P > 0 then
    begin
      Inc(Result);
      Delete(S, P, Length(SubStr));
    end;
  until P = 0;
end;

But actually with the Inno Setup StringChange function (which Delphi does not have), you do not have to code any algorithm yourself.

function OccurrencesOfSubString(S, SubStr: String): Integer;
begin
  Result := StringChange(S, SubStr, '');
end; 

This was inspired by the @RobertFrank's answer to Delphi: count number of times a string occurs in another string.

While the use of the StringChange looks inefficient (as it has significant side effects), it's actually faster. Probably because it is implemented in Pascal, not in Pascal Script.

Tested with 3 million calls to:

OccurrencesOfSubString('Hello World!, Hello World!, Hello World!, Hello World!', 'Hello')
  • With StringChange: 11 seconds
  • My optimized version of your code: 49 seconds
  • Your original code: 99 seconds

Though for few calls, all implementations are good enough.

Upvotes: 3

Related Questions