Can Obj C or Shell Script replace text item delimiter substitution?

What Objective C or Do Shell Script method could I use to replace the word substitution allowed by the following text item delimiter method?

set WordsToDelete to {"two", "three", "four", "five"}
set PriorText to "I have two apples, three bananas, four pears, and five apricots"
set text item delimiters to WordsToDelete
set TargetItems to text items of PriorText
set text item delimiters to "several"
set NewText to TargetItems as string

One shell method would be to use the Awk language with the sub or gsub command for first or all-instance substitution, respectively. I’ve posted searchable examples on this forum.

Edit: It’s unlikely significant performance improvements over the existing text item delimiter method will be made via the shell, as TIDs are already crazy fast, even with many changes.

This will do it:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"

set aString to current application's NSString's stringWithString:"I have two apples, three bananas, four pears, and five apricots"
set wordList to {"two", "three", "four", "five"}
set thePattern to (current application's NSArray's arrayWithArray:wordList)'s componentsJoinedByString:"|"
set aString to current application's NSString's stringWithString:"I have two apples, three bananas, four pears, and five apricots"
set aString to (aString's stringByReplacingOccurrencesOfString:thePattern withString:"several" options:(current application's NSRegularExpressionSearch) range:{0, aString's |length|()}) as text

This assumes your list of strings doesn’t contain any regex reserved characters, like |.

To make it case insensitive:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"

set aString to current application's NSString's stringWithString:"I have two apples, three bananas, four pears, and five apricots"
set wordList to {"two", "three", "four", "five"}
set thePattern to (current application's NSArray's arrayWithArray:wordList)'s componentsJoinedByString:"|"
set thePattern to "(?i)" & thePattern as text
set aString to current application's NSString's stringWithString:"I have two apples, three bananas, four pears, and five apricots"
set aString to (aString's stringByReplacingOccurrencesOfString:thePattern withString:"several" options:(current application's NSRegularExpressionSearch) range:{0, aString's |length|()}) as text

I did this as a brain-training exercise for myself and I’m still not sure it’s exactly what’s required.

Given the syntax of the original input string, it will replace one-word cardinal numbers greater than “one” with “several”.

It uses sed to find any number of alphabetic characters, followed by a space, followed by any number of alphabetic characters followed by “s” - the plural. It then globally substitutes the first sequence of characters (the number) with “several”.

do shell script "echo \"I have one grape, two apples, three bananas, four pears, and sixty gooseberries.\" | sed -E 's/[a-z]+ ([a-z]+s)/several \\1/'g"

--"I have one grape, several apples, several bananas, several pears, and several gooseberries."

It won’t work with “ten thousand”, or “twenty-five”. It ignores “one” because the following word doesn’t end in “s”. Although that’s more by accident than design. It seems to work with ‘s’ in the middle of the fruit name (can’t yet understand why…)

Another ASObjC suggestion:

use framework "Foundation"
use scripting additions

set PriorText to "I have two apples, three bananas, four pears, and five apricots"
set WordsToDelete to {"two", "three", "four", "five"}

set theString to current application's NSString's stringWithString:PriorText
repeat with i from 1 to (count WordsToDelete)
	set theString to (theString's stringByReplacingOccurrencesOfString:(item i of WordsToDelete) withString:"several")
end repeat
set newText to theString as text --> "I have several apples, several bananas, several pears, and several apricots"

The above script edited to be case insensitive:

use framework "Foundation"
use scripting additions

set PriorText to "I have two apples, Three bananas, four pears, and Five apricots"
set WordsToDelete to {"two", "three", "four", "five"}

set theString to current application's NSString's stringWithString:PriorText
repeat with i from 1 to (count WordsToDelete)
	set theString to (theString's stringByReplacingOccurrencesOfString:(item i of WordsToDelete) withString:"several" options:(current application's NSCaseInsensitiveSearch) range:{0, theString's |length|()})
end repeat
set newText to theString as text --> "I have several apples, several bananas, several pears, and several apricots"

Neither suggestion is very efficient, but a string with 384 words was processed by the second script in less than a millisecond.

That’s a safer approach than mine, in that you don’t have to worry about reserved regex characters.

Is there any difference if you use a mutable string?

Shane. Thanks for the suggestion.

I modified my scripts to use NSMutableString and I’ve included the case-insensitive version below. To make it case sensitive, one only needs to set ‘options’ to 0. I ran timing tests for both the NSString and NSMutableString scripts and there was not a significant difference, even when priorText was increased to 11,264 words (which took 4 milliseconds to run). The version that uses NSMutableString has the native ability to return the number of replacements and might be preferred for that reason.

use framework "Foundation"
use scripting additions

set PriorText to "I have two apples, Three bananas, four pears, and Five apricots"
set WordsToDelete to {"two", "three", "four", "five"}

set theString to current application's NSMutableString's stringWithString:PriorText
repeat with i from 1 to (count WordsToDelete)
	(theString's replaceOccurrencesOfString:(item i of WordsToDelete) withString:"several" options:(current application's NSCaseInsensitiveSearch) range:{0, theString's |length|()})
end repeat
set newText to theString as text --> "I have several apples, several bananas, several pears, and several apricots"