question

Serge A avatar image
1 Like"
Serge A asked Phil BoBo commented

String .replace method: the number of occurences replaced

Currently, .replace method of the string objects is supposed to replace all occurrences, but replaces only the first one. It is probably a bug:

In the same time, this unintended behavior can be actually useful. Many other programming languages use a count parameter of the string replacement method or implement two methods. Some examples: Python has a count parameter string.replace(), Java implements two methods String.replaceFirst() and String.replaceAll(), C# has a count parameter in Regex.Replace(), Javascript allows both by accepting regular expressions as search objects.

In Flexscript there are only two options:

  • use stringreplace() to replace all occurences
  • abuse the bug in .replace() method to replace only the first one

The second option is not particularly elegant. I suppose either the method or the documentation should be fixed, and the complementary method or an optional count parameter should be added.

FlexSim 17.0.3
flexscriptstring
5 |100000

Up to 12 attachments (including images) can be used with a maximum of 23.8 MiB each and 47.7 MiB total.

1 Answer

Matthew Gillespie avatar image
2 Likes"
Matthew Gillespie answered Phil BoBo commented

This is a documentation error. The replace method uses regular expressions so you need to use /o/g to replace all instances of o.

Here is the updated documentation that will be in 17.0.4:

string.replace

string replace( string findStr , string replaceWith )
string replace( RegExp findPattern , string replaceWith )

Parameters

findStr The string to look for.
replaceWith The string to replace findStr with.
findPattern The pattern of characters to look for, a regular expression.

Returns

string A copy of the string with a find pattern replaced with a new string.

Description

Replaces a series of characters with another.

While you can use a regular string for the findStr, this will only replace the first instance of findStr.

string text = "I want you to want me.";
return text.replace("want", "need");  // "I need you to want me."
      		

In order to replace all instances of a string you need to use a regular expression. The regular expression is defined between the forward slashes. Note the g modifier that modifies the search pattern to apply to all occurences. See the following section on Regular Expressions for more details.

string text = "I want you to want me.";
return text.replace(/want/g, "need");  // "I need you to need me."
			

Regular Expressions

A regular expression is a concise way of specifying a pattern of characters to look for. You start and end a regular expression with a forward slash "/" (similar to how you start and end a string with quotes) and then add modifiers:

/pattern/modifiers

Here is a brief explanation of some regular expression syntax.

Modifiers

These come after the closing / of the regular expression and modify their behavior

g - Global Match, matches all occurences, and not just the first one.

i - Case-insensitive match.

Brackets

[] - Matches any character in the brackets. Can use a dash to specify a range of characters.

[abc] - matches an a, b or c.
[a-z] - matches any lowercase letter.
[0-9] - matches any numerical digit.
[^abc] - matches any character that is not an a, b or c.

Or

| - Matches any of the alternatives separated by a |.

(abc|cba)- matches the sequence "abc" or "cba".
(gray|grey) or gr(a|e)y - matches "gray" and "grey".

Quantifiers

Define the quantity of characters the preceding expression will match

* - Matches any string that has zero or more of the specified characters.

ab*c - matches "ac", "abc", "abbc", "abbbc", etc. Basically any sequence of an a, any number of b's, and then a c.

+ - Matches any string that has at least one of the specified characters.

ab+c - almost the same as ab*c except that "ac" no longer matches.

? - Matches any string that has at zero or one of the specified characters.

colou?r - matches both "color" and "colour".

Repetitions

{n} - Matches n number of occurences.

{m,n} - Matches at least m, up to n number of occurences.

Periods

. - Matches any character.

\. - Matches a period.

Examples

This code removes all numbers. We are using [0-9] to find characters in the numeric range and then we put a plus after it to find any sequence of numbers no matter how long. Finally we add the g modifier to match all occurences.

string str = "abcd123efg456hij789klm0";
string letters = str.replace(/[0-9]+/g, "");  // "abcdefghijklm"
      		

Replace any instances of blue or green regardless of case with red.

string text = "Blue cars are really green.";
return text.replace(/blue|green/gi, "red");  // "red cars are really red."
      		

Replace all 3 letter words starting with b and ending with t.

string text = "My favorite words are: bit bat but bot bet.";
return text.replace(/b[aeiou]t/g, "Money"); // My favorite words are: Money Money Money Money Money.
      		

Hide any email addresses. Note that "\." is how you specify you want to see a period and not just any character.

string text = "Email me at [email protected] or [email protected]";
return text.replace(/([a-z0-9_\.-]+)@([0-9a-z\.-]+)\.([a-z\.]{2,6})/g, "###"); //Email me at ### or ###
			
· 10
5 |100000

Up to 12 attachments (including images) can be used with a maximum of 23.8 MiB each and 47.7 MiB total.

Serge A avatar image Serge A commented ·

> The replace method uses regular expressions

That's great and solves the problem.

I suppose the documentation should be updated to reflect that in the names of the parameters. Rather than

string replace( string findStr , string replaceWith )

the signature can be

string replace( string findRegExp , string replaceWith )

This would immediately hint the user that regular expressions are supported. Also in the documentation, the description of the parameter can be described as

findStr The string or the regular expression to look for

rather than just

findStr The string to look for.

0 Likes 0 ·
Steven Hamoen avatar image Steven Hamoen commented ·

@Matthew Gillespie If I look at the updated documentation it states that there are 3 parameters (findStr, replaceWith, findPattern). But in the signature you only show 2 and in the examples you also only show 2. So shouldn't that be updated again?

0 Likes 0 ·
Mischa Spelt avatar image Mischa Spelt Steven Hamoen commented ·

@Matthew Gillespie it feels like a bit of overkill to ask non-power users to learn Regular Expressions if they just want to replace all occurrences of a string. Perhaps you could consider creating a string.replaceAll wrapper for it?

0 Likes 0 ·
Matthew Gillespie avatar image Matthew Gillespie ♦♦ Mischa Spelt commented ·

I am inclined to agree with you, I think it would be great if the string overload continued to work just like the stringreplace command did, but, unfortunately, I don't have the final say over the design of FlexScript. You'll have to convince @anthony.johnson and/or Phil. Their position is that we modeled it after the JavaScript method and that's how JavaScript does it.

0 Likes 0 ·
Matthew Gillespie avatar image Matthew Gillespie ♦♦ Steven Hamoen commented ·

It's not that there are three parameters, but that there are two signatures that each take two parameters, but the first one is different, i.e.

replace(string findStr, str replaceStr)
replace(regEx findPattern, str replaceStr)

Both signatures are used in the examples, I just need to add the second signature at the top. Thanks for pointing that out!

0 Likes 0 ·
Pulkit J avatar image Pulkit J commented ·

How to replace a "/" ?

0 Likes 0 ·
Serge A avatar image Serge A Pulkit J commented ·

Normally you would "escape" the special symbol with a backslash. Something like this:

"foo/bar".replace(/\//, "::")

Unfortunately, this kind of escaping doesn't work for me in FlexSim (17.1).

1 Like 1 ·
Phil BoBo avatar image Phil BoBo ♦♦ Serge A commented ·

Wrap the escaped slash in []:

string fred = "foo/bar/ret/qwer";
return fred.replace(/[\/]/g,"-");
1 Like 1 ·
Axel Kohonen avatar image Axel Kohonen commented ·

Hi @Matthew Gillespie

Is there some way to use strings defined in global macros or parameters in the regular expresssions? Something like this

#define TEXT_FILE_DELIMITER "\t"

text.replace(/TEXT_FILE_DELIMITER + TEXT_FILE_DELIMITER/g, TEXT_FILE_DELIMITER + " " + TEXT_FILE_DELIMITER)

So that two consecutive delimiters would get a whitespace in between them.

The reason I want to know is that string.split handles consecutive delimiters as one so I need to insert a whitespace in between the delimiters to make split work as I want.

Thank you!

Kind regards,

Axel

0 Likes 0 ·
Phil BoBo avatar image Phil BoBo ♦♦ Axel Kohonen commented ·

You can't concatenate in the middle of a regex literal. + has a specific meaning in regex.

You also can't use macros within a regex literal. (You can't use macros within a string literal either.) It can't tell whether the macro characters are part of the literal or whether they should be replaced by the value of the macro.

Your code is essentially:

text.replace(/"\t" + "\t"/g, "\t" + " " + "\t")

Which isn't what you want to do. What you want is:

text.replace(/\t\t/g, "\t \t")

In JavaScript, you can create regex objects using a string. This isn't possible in FlexSim right now, but potentially in the future, we could add an object and a constructor for that object so that you can use string literals to define a regex, which would enable you to concatenate a macro string with a string literal or variable to construct a regex. Something like this:

#define TEXT_FILE_DELIMITER "\t"
var regex = RegEx("/" + TEXT_FILE_DELIMITER + TEXT_FILE_DELIMITER + "/g");
text.replace(regex, TEXT_FILE_DELIMITER + " " + TEXT_FILE_DELIMITER)

I'll add a case to the dev list with this suggestion.

In the meantime, your code will be easiest to write, read, and maintain if you just type the characters into your code rather than using the macro:

string text = "fred\t\tbob\tjoe";
text = text.replace(/\t\t/g, "\t \t");
return text;
1 Like 1 ·