StrReplace() should have option to replace from rear of string

Propose new features and changes
Qriist
Posts: 82
Joined: 11 Sep 2016, 04:02

StrReplace() should have option to replace from rear of string

23 Jul 2018, 11:22

I often have the need to remove a trailing aspect of a string, but not necessarily a unique one. In the compelling instance of this post I have the need to remove the letter E from the rear of a string, but it's not the last character and E is (probably) not unique. With more steps than should really be required I can get it done, BUT I respectfully request that StrReplace() gets an updated option field:

Code: Select all

ReplacedStr := StrReplace(Haystack, SearchText [, ReplaceText, OutputVarCount, Limit := -1], Reverse := 0)
Reverse = 0 for most operations, or 1 if you want to start replacing from the tail end.

Thanks!

Addendum:
Also, an option to skip the first X matches would work.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: StrReplace() should have option to replace from rear of string

28 Jul 2018, 17:59

- Hello. I think it's always good to post any workarounds, I've provided some here:

Code: Select all

q:: ;replace last occurrence of string (see notes about escape characters lower down)
vText := "ABCDEF_ABCDEF_ABCDEF"
MsgBox, % RegExReplace(vText, "E(?!.*E)")
MsgBox, % RegExReplace(vText, "CDE(?!.*CDE)")
return

;note: use 'i)' for a case-insensitive replace
;Regular Expressions (RegEx) - Quick Reference | AutoHotkey
;https://autohotkey.com/docs/misc/RegEx-QuickRef.htm

;note: see here for details on escaping characters
;simplest way to make a RegEx needle literal? - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=5&t=30420

;12 characters that need escaping in RegEx generally: \.*?+[{|()^$
;4 characters that need escaping in a RegEx character class: ^-]\

;==================================================

w:: ;replace last occurrence of string (AHK v1)
vText := "ABCDEF_ABCDEF_ABCDEF"
vNeedle := "E"
vPos := InStr(vText, vNeedle, 1, 0, 1)
MsgBox, % RegExReplace(vText, ".{" StrLen(vNeedle) "}", "",, 1, vPos)

vNeedle := "CDE"
vPos := InStr(vText, vNeedle, 1, 0, 1)
MsgBox, % RegExReplace(vText, ".{" StrLen(vNeedle) "}", "",, 1, vPos)
return

;==================================================

e:: ;replace last occurrence of string (two-way compatible)
vIsV1 := !!InStr(1,1,1,0)

vText := "ABCDEF_ABCDEF_ABCDEF"
vNeedle := "E"
vPos := InStr(vText, vNeedle, 1, vIsV1?0:-1, 1)
MsgBox, % RegExReplace(vText, ".{" StrLen(vNeedle) "}", "",, 1, vPos)

vText := "ABCDEF_ABCDEF_ABCDEF"
vNeedle := "CDE"
vPos := InStr(vText, vNeedle, 1, vIsV1?0:-1, 1)
MsgBox, % RegExReplace(vText, ".{" StrLen(vNeedle) "}", "",, 1, vPos)
return
- On a previous occasion I had thought that maybe the Limit parameter could become an Options parameter. (I had wanted a case-sensitive option/parameter so that I could replace text without having to worry about setting/resetting A_StringCaseSense.)
- Another idea for this situation would be to have a StartingPos parameter, to make StrReplace consistent with RegExReplace. You could use InStr to get the start of the nth-to-last occurrence, and start replacing from there. Cheers.
StrReplace(Haystack, SearchText [, ReplaceText, OutputVarCount, Limit := -1])
RegExReplace(Haystack, NeedleRegEx [, Replacement := "", OutputVarCount := "", Limit := -1, StartingPosition := 1])
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: StrReplace() should have option to replace from rear of string

12 Aug 2018, 02:12

- I noticed that this was raised on GitHub:
Adding CaseSensitive parameter to StrReplace. by HelgeffegleH · Pull Request #103 · Lexikos/AutoHotkey_L · GitHub
https://github.com/Lexikos/AutoHotkey_L/pull/103
- I would suggest the following:

Code: Select all

;before:
ReplacedStr := StrReplace(Haystack, SearchText [, ReplaceText, OutputVarCount, Limit := -1])
;after (add StartingPosition and CaseSensitive):
ReplacedStr := StrReplace(Haystack, SearchText [, ReplaceText, OutputVarCount, Limit := -1, StartingPosition := 1, CaseSensitive := false])
- Re. usage, I use the Count parameter all the time, and would even suggest a separate StrCount function to retrieve the output directly for if statements.
- A StrReplace case-sensitive parameter would be very useful for libraries. StrReplace could then be used within a one-liner, versus, get SCS (A_StringCaseSense), set SCS, StrReplace, restore SCS. RegExReplace is an alternative, but is slow and ill-suited to handling variable replacement strings.
- I find the existing Limit parameter useful for doing 1 replacement exactly, and sometimes for doing special replacement operations.
- I had never previously needed a StartingPosition parameter, however, that may be because I'm used to using RegExReplace, or the situations are rare so I was OK with using SubStr, or because often Trim would do. This example gave a good reason why a StartingPosition parameter would be useful. And it would be good to keep StrReplace and RegExReplace consistent.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
lexikos
Posts: 9554
Joined: 30 Sep 2013, 04:07
Contact:

Re: StrReplace() should have option to replace from rear of string

12 Aug 2018, 22:47

I think there is very little reason to have a StartingPos parameter, especially when you have to count commas because there are so many parameters. If you want to operate on a substring, get one (with SubStr), then do that. (Ok, yes, you have to take multiple substrings and concatenate then back together...)

RegEx is a different matter, because of look-behind and start-of-string assertions.

It will take some convincing before I add CaseSensitive to StrReplace; I don't see it happening at this point.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: StrReplace() should have option to replace from rear of string

13 Aug 2018, 19:46

- Thanks for your comments.
- Yes there would be a lot of parameters. But I believe that on balance the alternatives are more unwieldy, with a lot of lines.
- With case sensitivity, a further disadvantage is that you must remember to do StringCaseSense, % vSCS to restore the mode before any returns in a function. This can then cause the entire flow of the function to need restructuring, to accommodate it.
- My instinct is that if a StartingPos parameter were present, I would use it. It would definitely help with fiddly repetitive code, and dealing with help requests for newbies. And since there are already a lot of parameters (i.e. commas) needed, one more won't be any worse.
- (Since it wasn't there in the past, I used what was available, but I've come up with a good number of use cases since contemplating its possible inclusion. Personally, I feel that AHK could be regarded as an excellent language for string handling, and mostly already is. The StartingPos parameter would make removing the last n occurrences of a string reasonably straightforward: InStr and StrReplace, versus InStr/SubStr/StrReplace/SubStr.)
- I tend to feel that adding a CaseSensitive parameter but no StartingPos parameter, may lead to regret. Also, if we include a StartingPos parameter, we would have a simple symmetry with RegExReplace.
- I don't see multiple commas as a big problem, one multiple comma example in the StrReplace documentation would fix this.
- StrReplace with the additional parameters makes for greater readability, you chop away extra line/function use.
- Here are two examples demonstrating unwieldiness without the additional parameters. Unwieldiness is multiplied when multiple similar bits of code are needed back-to-back.

Code: Select all

;possible new look:
;vRet := StrReplace("ABCabc", "a", "",,, 1) ;HNR CL C (haystack/needle/replace text, count/limit, case sen.)
;vRet := StrReplace("ABCabc", "a", "",,,, 1) ;HNR CLS C (haystack/needle/replace text, count/limit/start pos., case sen.)

;replace last 2 occurrences of 'DEF'
;without a StartingPos parameter
vIsV1 := !!InStr(1,1,1,0)
vText := "ABCDEF_ABCDEF_ABCDEF"
vNeedle := "DEF"
vPos := InStr(vText, vNeedle, 1, vIsV1?0:-1, 2)
vOutput := SubStr(vText, 1, vPos-1) StrReplace(SubStr(vText, vPos), vNeedle)
MsgBox, % vOutput

;replace case sensitive
;without a CaseSensitive parameter
vText := "ABCabc"
vSCS := A_StringCaseSense
StringCaseSense, On
vOutput := StrReplace(vText, "a")
StringCaseSense, % vSCS
MsgBox, % vOutput
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: StrReplace() should have option to replace from rear of string

13 Aug 2018, 22:23

jeeswg wrote:I don't see multiple commas as a big problem, one multiple comma example in the StrReplace documentation would fix this.
Some times parameters really stress me out, so I use objects instead!

Code: Select all

f()
f({CaseSense: "On"})
f({StartPos: "10"})
f({Reverse: "On"})
f({CaseSense: "On", StartPos: "10", Reverse: "On"})


F(Options := "")	;__________________ Function ________________
{
msgbox, % ""
. "CaseSense = " Options["CaseSense"]    "`n"
. "StartPos = " Options["StartPos"]      "`n"
. "Reverse = " Options["Reverse"]        "`n"
}
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: StrReplace() should have option to replace from rear of string

13 Aug 2018, 22:44

- Thanks User.
- An alternative would be a space-separated list. I mentioned that alternative earlier.
- However, I find RegExReplace very workable, and StrReplace would only have one more parameter than that.
- People could use mnemonics like so:
vRet := StrReplace("ABCabc", "a", "",,, pos:=-4, cs:=1)
- For InputBox, I start with a hotstring, and delete the parameters I don't want:

Code: Select all

InputBox, vInput,, % vPrompt
InputBox, vInput,, % vPrompt,,,,,,,, % vDefault
InputBox, vInput,, % vPrompt,, % vPosW, % vPosH,,,, % vTimeout, % vDefault
InputBox, vInput,, % vPrompt,, % vPosW, % vPosH, % vPosX, % vPosY,, % vTimeout, % vDefault
InputBox, vInput, % vTitle, % vPrompt,, % vPosW, % vPosH, % vPosX, % vPosY,, % vTimeout, % vDefault
- If AHK ever introduced named parameters, that would be welcome. But it's not a personal priority of mine. (I tend to only suggest function additions or amendments. And syntax changes only if they are in dire need, because generally they require more work.)
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: StrReplace() should have option to replace from rear of string

14 Aug 2018, 14:34

jeeswg wrote:vRet := StrReplace("ABCabc", "a", "",,, pos:=-4, cs:=1)
For me that's really not suitable, in that example of yours, "pos" and "cs" variable from the script would be modified! (Example below)

Code: Select all

test := F()
msgbox, % test " - " Options

test := F("Sript")
msgbox, % test " - " Options

test := F(Options := "Sript")		;not suitable ("Options" variable from sccript will be modified)
msgbox, % test " - " Options


f(Options := "Default")		;_____________ function __________
{
return, Options
}
by using objects instead parameters, I don't have to worry about the order, about counting the commas and I can omit what I don't need!

All the examples below are valid:

f({CaseSense: "On"})
or
f({StartPos: "10"})
or
f({Reverse: "On"})
or
f({CaseSense: "On", StartPos: "10"})
or
f({StartPos: "10", CaseSense: "On"})
or
f({CaseSense: "On", StartPos: "10", Reverse: "On"})
or
f({StartPos: "10", Reverse: "On", CaseSense: "On"})
or
f({Reverse: "On", CaseSense: "On",StartPos: "10"})
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: StrReplace() should have option to replace from rear of string

14 Aug 2018, 15:29

- @User: I think your ideas are fine, it's just that I tend to suggest things in line with what currently exists. Something very conservative that hopefully wouldn't shock anyone.
- I wouldn't start a new syntax convention just for one function.
- You could start a thread in Wish List stating that you want this for all functions.
- I had this kind of functionality in mind when I suggested standardised parameter names for all functions.
Suggestions on documentation improvements - Page 20 - AutoHotkey Community
https://autohotkey.com/boards/viewtopic ... 60#p216760
- Preferably each name would be short e.g. 'StartPos'.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User
Posts: 407
Joined: 26 Jun 2017, 08:12

Re: StrReplace() should have option to replace from rear of string

14 Aug 2018, 18:42

jeeswg wrote:I wouldn't start a new syntax convention ...
I don't want to start a new syntax convention, I just suggested a simple solution to add several new parameters to existing functions!

Lets say in the future it will be necessary to add 10 new parameters to "StringReplace" function!

The "Objects" solution I suggested seems to be suitable! (Example below)

StrReplace(Haystack, SearchText , ReplaceText, OutputVarCount, Limit, Options)

StrReplace(Haystack, SearchText , ReplaceText, , , {CaseSense: "On"})
or
StrReplace(Haystack, SearchText , ReplaceText, , , {StartPos: "10"})
or
StrReplace(Haystack, SearchText , ReplaceText, , , {Reverse: "On"})
or
StrReplace(Haystack, SearchText , ReplaceText, , , {CaseSense: "On", StartPos: "10"})
or
StrReplace(Haystack, SearchText , ReplaceText, , , {StartPos: "10", CaseSense: "On"})
or
StrReplace(Haystack, SearchText , ReplaceText, , , {CaseSense: "On", StartPos: "10", Reverse: "On"})
or
StrReplace(Haystack, SearchText , ReplaceText, , , {StartPos: "10", Reverse: "On", CaseSense: "On"})
or
StrReplace(Haystack, SearchText , ReplaceText, , , {Reverse: "On", CaseSense: "On", StartPos: "10"})
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: StrReplace() should have option to replace from rear of string

15 Aug 2018, 01:03

Objects have performance issues when it comes to creation.
Your suggestion wouldnt work with AHK as it is right now.

Havin that many commas is not an option either though.
Its a pain to read - we need named parameters but they are not a pressing matter right now.
Recommends AHK Studio
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: StrReplace() should have option to replace from rear of string

15 Aug 2018, 02:50

- @nnnik: I'm sympathetic to the points you made. I considered all the options, and I came to what I believe is, all things considered, the best option.
- Some people might think it's too many parameters, but I really don't think it's a big problem.
- Another solution could be a space-separated options parameter instead of the Limit parameter. But, I think that moving from 5 to 7 parameters would be fine. It just doesn't seem that big a deal. RegExReplace was fine to me, and this is basically just that plus 1 parameter.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: StrReplace() should have option to replace from rear of string

15 Aug 2018, 03:00

I think 4 parameters is already too much for me.
It just doesn't look readeable anymore after that.
Space seperated options have the same problem as objects as in they have a performance issue. (although its much lower)
I also feel that just replacing a , with a space doesn't increase readability.

That being said I don't think I will ever use the option to start replacing from the rear of the string so the parameter amount doesn't matter to me.
I would just submit a PR about this.
Recommends AHK Studio
Qriist
Posts: 82
Joined: 11 Sep 2016, 04:02

Re: StrReplace() should have option to replace from rear of string

17 Aug 2018, 00:07

This spawned a whole lot of discussion. Lawl.
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: StrReplace() should have option to replace from rear of string

15 Mar 2019, 18:03

- Here's a prototype 7-parameter function.
- I don't mind having *one* function that has 'too many' parameters. And there is no easy answer, unless you want StrReplaceCI / StrReplaceCS functions.
- InputBox was even worse re. the parameter count, and I didn't mind that.
- I've always valued being able to easily convert between StrReplace and RegExReplace, hence why I value the 6th parameter being consistent as StartPos.
- One additional comma before you reach the CaseSen parameter, is fine. There's 'too many' parameters whether it's no. 6 or no. 7.
- I find the StartPos parameter significantly easier to use, cf. the SubStr approach.

Code: Select all

q:: ;test StrReplaceNew
;replace last 2 occurrences of 'DEF'
vIsV1 := InStr(1, 1,, 0)
vText := "ABCDEF_ABCDEF_ABCDEF"
vNeedle := "DEF"
vPos := InStr(vText, vNeedle, 1, vIsV1?0:-1, 2)
MsgBox, % StrReplaceNew(vText, vNeedle,,,, vPos)

;replace 4th-to-last and 3rd-to-last occurrences of 'DEF'
vIsV1 := InStr(1, 1,, 0)
vText := "ABCDEF_ABCDEF_ABCDEF_ABCDEF_ABCDEF_ABCDEF_ABCDEF_ABCDEF"
vNeedle := "DEF"
vPos := InStr(vText, vNeedle, 1, vIsV1?0:-1, 4)
MsgBox, % StrReplaceNew(vText, vNeedle,,, 2, vPos)

vText := "ABCabc"
vNeedle := "a"
MsgBox, % StrReplaceNew(vText, vNeedle,,,,, 1)
MsgBox, % StrReplaceNew(vText, vNeedle,,,,, 0)
return

StrReplaceNew(vText, vNeedle, vReplaceText:="", ByRef vCount:=0, vLimit:=-1, vStartPos:=1, vCaseSen:="")
{
	local
	if !(vCaseSen = "")
	{
		vSCS := A_StringCaseSense
		if (vCaseSen = 1) || (vCaseSen = 0)
			StringCaseSense, % vCaseSen ? "On" : "Off"
		else
			StringCaseSense, % vCaseSen
	}
	if (vStartPos = 1)
		vRet := StrReplace(vText, vNeedle, vReplaceText, vCount, vLimit)
	else
		vRet := SubStr(vText, 1, vStartPos-1) StrReplace(SubStr(vText, vStartPos), vNeedle, vReplaceText, vCount, vLimit)
	if !(vCaseSen = "")
		StringCaseSense, % vSCS
	return vRet
}

Code: Select all

;handy templates:

;common
vText := StrReplace(vText, vNeedle, vReplaceText)

;all
vText := StrReplace(vText, vNeedle, vReplaceText, vCount, vLimit, vStartPos, vCaseSen)

;common + 1
vText := StrReplace(vText, vNeedle, vReplaceText, vCount)
vText := StrReplace(vText, vNeedle, vReplaceText,, vLimit)
vText := StrReplace(vText, vNeedle, vReplaceText,,, vStartPos)
vText := StrReplace(vText, vNeedle, vReplaceText,,,, vCaseSen)
- [EDIT:] I since used the StartPos parameter in a script, to replace the 4th occurrence (the 1st occurrence, starting at StartPos), and it was very convenient.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA

Return to “Wish List”

Who is online

Users browsing this forum: No registered users and 33 guests