Selectively replace multiple instances of a character within a string: Difference between revisions

From Rosetta Code
Content added Content deleted
(Selectively replace multiple instances of a character within a string in FreeBASIC)
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(43 intermediate revisions by 21 users not shown)
Line 21: Line 21:
{{Template:Strings}}
{{Template:Strings}}
<br>
<br>

=={{header|11l}}==
{{trans|Python}}

<syntaxhighlight lang="11l">V rep = [‘a’ = [1 = ‘A’, 2 = ‘B’, 4 = ‘C’, 5 = ‘D’], ‘b’ = [1 = ‘E’], ‘r’ = [2 = ‘F’]]

F trstring(oldstring, repdict)
DefaultDict[Char, Int] seen
V newchars = ‘’
L(c) oldstring
V i = ++seen[c]
newchars ‘’= I c C repdict & i C repdict[c] {repdict[c][i]} E c
R newchars

print(‘abracadabra -> ’trstring(‘abracadabra’, rep))</syntaxhighlight>

{{out}}
<pre>
abracadabra -> AErBcadCbFD
</pre>

=={{header|ALGOL 68}}==
<syntaxhighlight lang="algol68">CO in the string "abracadabra", replace the first 'a' with 'A', the second 'a' with 'B'
, the fourth 'a' with 'C', the fifth 'a' with 'D'
the first 'b' with 'E', the second 'r' with 'F'
CO
BEGIN
[,]STRING replacements = ( ( "a", "ABaCD" ), ( "b", "E" ), ( "r", "rF" ) );
[ 1 LWB replacements : 1 UPB replacements ]INT position;
STRING input = "abracadabra";
[ LWB input : UPB input ]CHAR output;
FOR i FROM LWB position TO UPB position DO position[ i ] := LWB replacements[ i, 2 ] OD;
FOR c pos FROM LWB input TO UPB input DO
CHAR c = input[ c pos ];
output[ c pos ] := c;
BOOL found := FALSE;
FOR r pos FROM 1 LWB replacements TO 1 UPB replacements WHILE NOT found DO
STRING r = replacements[ r pos, 1 ];
IF c = r[ LWB r ] THEN
found := TRUE;
IF position[ r pos ] <= UPB replacements[ r pos, 2 ] THEN
output[ c pos ] := replacements[ r pos, 2 ][ position[ r pos ] ];
position[ r pos ] +:= 1;
found := TRUE
FI
FI
OD
OD;
print( ( """", input, """ -> """, output, """" ) );
IF output /= "AErBcadCbFD" THEN print( ( " ** UNEXPECTED RESULT" ) ) FI;
print( ( newline ) )
END</syntaxhighlight>
{{out}}
<pre>
"abracadabra" -> "AErBcadCbFD"
</pre>

=={{header|Arturo}}==
<syntaxhighlight lang="arturo">replacement: function [rule,ch,order][
loop rule 'r ->
if r\[0] = order -> return r\[1]
return ch
]
multiReplace: function [str, rules][
cntr: #.raw flatten couple keys rules repeat 0 size rules

join map str 'ch [
(key? cntr ch)? [
cntr\[ch]: cntr\[ch] + 1
replacement rules\[ch] ch dec cntr\[ch]
] -> ch
]
]

print multiReplace "abracadabra" #[
a: [[0 `A`][1 `B`][3 `C`][4 `D`]]
b: [[0 `E`]]
r: [[1 `F`]]
]</syntaxhighlight>

{{out}}

<pre>AErBcadCbFD</pre>

=={{header|AutoHotkey}}==
<syntaxhighlight lang="autohotkey">str := "abracadabra"
steps := [[1, "a", "A"]
, [2, "a", "B"]
, [4, "a", "C"]
, [5, "a", "D"]
, [1, "b", "E"]
, [2, "r", "F"]]

MsgBox % result := Selectively_replace(str, steps)
return

Selectively_replace(str, steps){
Res := [], x := StrSplit(str)
for i, step in steps {
n := step.1, L := step.2, R := step.3, k := 0
for j, v in x
if (v=L) && (++k = n) {
Res[j] := R
break
}
}
for j, v in x
result .= Res[j] = "" ? x[j] : Res[j]
return result
}</syntaxhighlight>
{{out}}
<pre>AErBcadCbFD</pre>

=={{header|C}}==
<syntaxhighlight lang="c">
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
const char string[] = "abracadabra";

char *replaced = malloc(sizeof(string));
strcpy(replaced, string);

// Null terminated replacement character arrays
const char *aRep = "ABaCD";
const char *bRep = "E";
const char *rRep = "rF";

for (char *c = replaced; *c; ++c) {
switch (*c) {
case 'a':
if (*aRep)
*c = *aRep++;
break;
case 'b':
if (*bRep)
*c = *bRep++;
break;
case 'r':
if (*rRep)
*c = *rRep++;
break;
}
}

printf("%s\n", replaced);

free(replaced);
return 0;
}
</syntaxhighlight>
{{out}}
<pre>
AErBcadCbFD
</pre>


=={{header|C++}}==
=={{header|C++}}==
<lang cpp>#include <map>
<syntaxhighlight lang="cpp">#include <map>
#include <iostream>
#include <iostream>
#include <string>
#include <string>
Line 46: Line 203:


std::cout << magic << "\n";
std::cout << magic << "\n";
}</lang>
}</syntaxhighlight>
{{out}}
<pre>
AErBcadCbFD
</pre>

=={{header|Delphi}}==
{{works with|Delphi|6.0}}
{{libheader|SysUtils,StdCtrls}}


<syntaxhighlight lang="Delphi">
var TestStr: string = 'abracadabra';


function FindNthChar(C: char; S: string; N: integer): integer;
{Routine to find the Nth version of C, string S}
begin
for Result:=1 to Length(S) do
if S[Result]=C then
begin
Dec(N);
if N<=0 then exit;
end;
Result:=-1;
end;


procedure ReplaceNthChar(COld,CNew: char; var S: string; N: integer);
{Find and replace the Nth version COld with CNew}
var Inx: integer;
begin
Inx:=FindNthChar(COld,S,N);
if Inx<1 then exit;
S[Inx]:=CNew;
end;


procedure SelectivelyReplaceChars(Memo: TMemo);
var I: integer;
begin
Memo.Lines.Add('Before: '+TestStr);
{Do the replacement toward the end of string first}
ReplaceNthChar('a','D',TestStr,5);
ReplaceNthChar('a','C',TestStr,4);
ReplaceNthChar('a','B',TestStr,2);
ReplaceNthChar('r','F',TestStr,2);
ReplaceNthChar('a','A',TestStr,1);
ReplaceNthChar('b','E',TestStr,1);
Memo.Lines.Add('After: '+TestStr);
end;


</syntaxhighlight>
{{out}}
<pre>
Before: AErBcadCbFD
After: AErBcAdCEFD
Elapsed Time: 1.749 ms.

</pre>


=={{header|EMal}}==
<syntaxhighlight lang="emal">
fun transmogrify = text by text input, Map replacements
Map indexes = text%int[]
text result = ""
for each text ch in input
result.append(when(replacements.has(++indexes[ch] + ch), replacements[indexes[ch] + ch], ch))
end
return result
end
writeLine(transmogrify("abracadabra",
text%text["1a" => "A", "2a" => "B", "4a" => "C", "5a" => "D", "1b" => "E", "2r" => "F"]))
</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 54: Line 286:
=={{header|Factor}}==
=={{header|Factor}}==
{{works with|Factor|0.99 2022-04-03}}
{{works with|Factor|0.99 2022-04-03}}
<lang factor>USING: assocs formatting grouping kernel random sequences ;
<syntaxhighlight lang="factor">USING: assocs formatting grouping kernel random sequences ;


CONSTANT: instrs {
CONSTANT: instrs {
Line 76: Line 308:


"abracadabra" test
"abracadabra" test
"abracadabra" randomize test</lang>
"abracadabra" randomize test</syntaxhighlight>
{{out}}
{{out}}
<pre>
<pre>
Line 84: Line 316:


=={{header|FreeBASIC}}==
=={{header|FreeBASIC}}==
<lang freebasic>Function replaceChar(Byref S As String) As String
<syntaxhighlight lang="freebasic">Function replaceChar(Byref S As String) As String
Dim As String A = "ABaCD", B = "Eb", R = "rF"
Dim As String A = "ABaCD", B = "Eb", R = "rF"
Dim As Byte pA = 1, pB = 1, pR = 1
Dim As Byte pA = 1, pB = 1, pR = 1
Line 108: Line 340:
S = "caarabadrab"
S = "caarabadrab"
Print S; " -> "; replaceChar(S)
Print S; " -> "; replaceChar(S)
Sleep</lang>
Sleep</syntaxhighlight>
{{out}}
{{out}}
<pre>abracadabra -> AErBcadCbFD
<pre>abracadabra -> AErBcadCbFD
caaarrbabad -> cABarFECbDd</pre>
caaarrbabad -> cABarFECbDd</pre>

=={{header|FutureBasic}}==
<syntaxhighlight lang="futurebasic">
include "NSLog.incl"

void local fn DoIt
long a = 0, b = 0, r = 0, length, i
CFMutableStringRef string = fn MutableStringWithString( @"abracadabra" )
CFStringRef s
length = len(string)
for i = 0 to length - 1
s = NULL
select ( mid(string,i,1) )
case @"a"
a++
select ( a )
case 1 : s = @"A"
case 2 : s = @"B"
case 4 : s = @"C"
case 5 : s = @"D"
end select
case @"b"
b++
if ( b == 1 ) then s = @"E"
case @"r"
r++
if ( r == 2 ) then s = @"F"
end select
if ( s ) then mid(string,i,1) = s
next
NSLog(@"%@",string)
end fn

fn DoIt

HandleEvents
</syntaxhighlight>

{{out}}
<pre>
AErBcadCbFD
</pre>


=={{header|Go}}==
=={{header|Go}}==
{{trans|Wren}}
{{trans|Wren}}
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
Line 140: Line 416:
s = strings.Replace(s, "F", "r", 1)
s = strings.Replace(s, "F", "r", 1)
fmt.Println(s)
fmt.Println(s)
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 146: Line 422:
AErBcadCbFD
AErBcadCbFD
</pre>
</pre>

=={{header|Haskell}}==
As a map-accumulation:
<syntaxhighlight lang="haskell">import Data.List (mapAccumL)
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)

---------- POSITIONAL CHARACTER-REPLACEMENT RULES --------

nthCharsReplaced :: M.Map Char [Maybe Char] -> String -> String
nthCharsReplaced ruleMap = snd . mapAccumL go ruleMap
where
go a c =
case M.lookup c a of
Nothing -> (a, c)
Just [] -> (a, c)
Just (d : ds) ->
( M.insert c ds a,
fromMaybe c d
)

--------------------------- TEST -------------------------
main :: IO ()
main = putStrLn $ nthCharsReplaced rules "abracadabra"

rules :: M.Map Char [Maybe Char]
rules =
M.fromList
[ ('a', (Just <$> "AB") <> [Nothing] <> (Just <$> "CD")),
('b', [Just 'E']),
('r', [Nothing, Just 'F'])
]</syntaxhighlight>
{{Out}}
<pre>AErBcadCbFD</pre>


=={{header|J}}==
=={{header|J}}==


<lang J> upd=: {{ x (n{I.y=m)} y }}
<syntaxhighlight lang="j"> upd=: {{ x (n{I.y=m)} y }}
'ABCD' 'a' upd 0 1 3 4 'E' 'b' upd 0 'F' 'r' upd 1 'abracadabra'
'ABCD' 'a' upd 0 1 3 4 'E' 'b' upd 0 'F' 'r' upd 1 'abracadabra'
AErBcadCbFD</lang>
AErBcadCbFD</syntaxhighlight>


<tt>upd</tt> here takes four arguments -- two on the left (replacement characters, original character) and two on the right(index values for which instances to replace, and the original string).
<tt>upd</tt> here takes four arguments -- two on the left (replacement characters, original character) and two on the right(index values for which instances to replace, and the original string).
Line 157: Line 467:
However, here's a more compact approach (the first item in the left argument is the target, and the rest of the left argument explicitly provides values for every instance of that item in the right argument):
However, here's a more compact approach (the first item in the left argument is the target, and the rest of the left argument explicitly provides values for every instance of that item in the right argument):


<lang J> chg=: {{ (}.x) (I.y={.x)} y}}
<syntaxhighlight lang="j"> chg=: {{ (}.x) (I.y={.x)} y}}
'aABaCD' chg 'bEb' chg 'rrF' chg 'abracadabra'
'aABaCD' chg 'bEb' chg 'rrF' chg 'abracadabra'
AErBcadCbFD</lang>
AErBcadCbFD</syntaxhighlight>


=={{header|Java}}==
{{trans|JavaScript}}
Here's an example translated from JavaScript.
<syntaxhighlight lang="java">
int findNth(String s, char c, int n) {
if (n == 1) return s.indexOf(c);
return s.indexOf(c, findNth(s, c, n - 1) + 1);
}

String selectiveReplace(String s, Set... ops) {
char[] chars = s.toCharArray();
for (Set set : ops)
chars[findNth(s, set.old, set.n)] = set.rep;
return new String(chars);
}

record Set(int n, char old, char rep) { }
</syntaxhighlight>
<syntaxhighlight lang="java">
selectiveReplace("abracadabra",
new Set(1, 'a', 'A'),
new Set(2, 'a', 'B'),
new Set(4, 'a', 'C'),
new Set(5, 'a', 'D'),
new Set(1, 'b', 'E'),
new Set(2, 'r', 'F'));
</syntaxhighlight>
{{out}}
<pre>AErBcadCbFD</pre>


=={{header|JavaScript}}==
<syntaxhighlight lang="javascript">function findNth(s, c, n) {
if (n === 1) return s.indexOf(c);
return s.indexOf(c, findNth(s, c, n - 1) + 1);
}

function selectiveReplace(s, ops) {
const chars = Array.from(s);
for ([n, old, rep] of ops) {
chars[findNth(s, old, n)] = rep;
}
return chars.join("");
}

console.log(
selectiveReplace("abracadabra", [
[1, "a", "A"], // the first 'a' with 'A'
[2, "a", "B"], // the second 'a' with 'B'
[4, "a", "C"], // the fourth 'a' with 'C'
[5, "a", "D"], // the fifth 'a' with 'D'
[1, "b", "E"], // the first 'b' with 'E'
[2, "r", "F"], // the second 'r' with 'F'
])
);</syntaxhighlight>
{{out}}
<pre>AErBcadCbFD</pre>


Or, expressed as a map-accumulation:
<syntaxhighlight lang="javascript">(() => {
"use strict";

// -- INSTANCE-SPECIFIC CHARACTER REPLACEMENT RULES --

// # nthInstanceReplaced :: Dict Char [(Null | Char)] ->
const nthInstanceReplaced = ruleMap =>
// A string defined by replacements specified for
// the nth instances of various characters.
s => mapAccumL(
(a, c) => c in a ? (() => {
const ds = a[c];

return Boolean(ds.length) ? [
Object.assign(a, {[c]: ds.slice(1)}),
ds[0] || c
] : [a, c];
})() : [a, c]
)(Object.assign({}, ruleMap))(
[...s]
)[1].join("");


// ---------------------- TEST -----------------------
const main = () =>
// Instance-specific character replacement rules.
nthInstanceReplaced({
a: ["A", "B", null, "C", "D"],
b: ["E"],
r: [null, "F"]
})(
"abracadabra"
);


// --------------------- GENERIC ---------------------

// mapAccumL :: (acc -> x -> (acc, y)) -> acc ->
// [x] -> (acc, [y])
const mapAccumL = f =>
// A tuple of an accumulation and a list
// obtained by a combined map and fold,
// with accumulation from left to right.
acc => xs => [...xs].reduce(
([a, bs], x) => second(
v => [...bs, v]
)(
f(a, x)
),
[acc, []]
);


// second :: (a -> b) -> ((c, a) -> (c, b))
const second = f =>
// A function over a simple value lifted
// to a function over a tuple.
// f (a, b) -> (a, f(b))
([x, y]) => [x, f(y)];


// MAIN --
return main();
})();</syntaxhighlight>
{{Out}}
<pre>AErBcadCbFD</pre>

=={{header|jq}}==
In this section, array-indexing and occurrence-counting are both 0-based except for the transcription of the task in `steps`.

'''Generic functions'''
<syntaxhighlight lang="jq">
# Emit empty if the stream does not have an $n-th item
# Note: jq's nth/2 does not serve our purposes.
def n_th($n; stream):
if $n < 0 then empty
else foreach stream as $x (-1; .+1; if . == $n then $x else empty end)
end;

def positions(stream; $v):
foreach stream as $x (-1; .+1; if $v == $x then . else empty end);

# Input: an array or string.
# Output: the input with an occurrence of $old replaced by $new.
# . and $reference are assumed to be of the same type and length.
# The search occurs in $reference and the corresponding spot in . is modified.
def replace_nth($occurrence; $old; $new; $reference):
if type == "array"
then ($reference | n_th($occurrence; positions(.[]; $old)) // null) as $ix
| if $ix then .[:$ix] + [$new] + .[$ix + 1:] else . end
else explode
| replace_nth($occurrence; $old|explode|first; $new|explode|first; $reference|explode)
| implode
end;
</syntaxhighlight>

'''The task'''
<syntaxhighlight lang="jq">
def steps:
[1, "a", "A"],
[2, "a", "B"],
[4, "a", "C"],
[5, "a", "D"],
[1, "b", "E"],
[2, "r", "F"];

def task(steps):
. as $reference
| reduce steps as [$occurrence, $old, $new] (.;
replace_nth($occurrence - 1; $old; $new; $reference ));
"abracadabra" | task(steps)
</syntaxhighlight>
{{Output}}
<pre>
AErBcadCbFD
</pre>


=={{header|Julia}}==
=={{header|Julia}}==
<syntaxhighlight lang="julia">
<lang ruby>
rep = Dict('a' => Dict(1 => 'A', 2 => 'B', 4 => 'C', 5 => 'D'), 'b' => Dict(1 => 'E'), 'r' => Dict(2 => 'F'))
rep = Dict('a' => Dict(1 => 'A', 2 => 'B', 4 => 'C', 5 => 'D'), 'b' => Dict(1 => 'E'), 'r' => Dict(2 => 'F'))


Line 176: Line 665:


println("abracadabra -> ", trstring("abracadabra", rep))
println("abracadabra -> ", trstring("abracadabra", rep))
</lang>{{out}}Same as Perl.
</syntaxhighlight>{{out}}Same as Perl.


=={{header|Lambdatalk}}==
=={{header|Lambdatalk}}==
Line 189: Line 678:


Then we add to the existing set of array functions a new one finding the indexes of some value in a given array.
Then we add to the existing set of array functions a new one finding the indexes of some value in a given array.
<syntaxhighlight lang="scheme">
<lang Scheme>
{def A.findindexes
{def A.findindexes


Line 209: Line 698:
-> [0,3,5,7,10]
-> [0,3,5,7,10]
... and so on
... and so on
</syntaxhighlight>
</lang>


Using findindexes we can translate the aA1 aB2 aC4 aD5 bE1 rF2 sequence into a new one where numbers are replaced by indexes in the given string, here abracadabra.
Using findindexes we can translate the aA1 aB2 aC4 aD5 bE1 rF2 sequence into a new one where numbers are replaced by indexes in the given string, here abracadabra.


<syntaxhighlight lang="scheme">
<lang Scheme>
{def replacements.rules
{def replacements.rules
{lambda {:w :r}
{lambda {:w :r}
Line 226: Line 715:
-> aA0
-> aA0
... and so on
... and so on
</syntaxhighlight>
</lang>


Finally the replacements function will apply this sequence of rules to the word.
Finally the replacements function will apply this sequence of rules to the word.


<syntaxhighlight lang="scheme">
<lang Scheme>
{def replacements
{def replacements


Line 255: Line 744:
-> cABarFECbDd
-> cABarFECbDd
(cABarFECbDd)
(cABarFECbDd)
</syntaxhighlight>
</lang>


2) second answer using regexps
2) second answer using regexps
Line 261: Line 750:
Here is a quick & dirty answer using the S.replace_once primitive.
Here is a quick & dirty answer using the S.replace_once primitive.


<syntaxhighlight lang="scheme">
<lang Scheme>
{def multrepl_rex
{def multrepl_rex
{lambda {:word :rules}
{lambda {:word :rules}
Line 282: Line 771:
-> AErBcadCbFD
-> AErBcadCbFD
(AErBcadCbFD)
(AErBcadCbFD)
</syntaxhighlight>
</lang>

=={{header|Nim}}==
<syntaxhighlight lang="Nim">import std/tables

type
# Table of replacements for a character.
Replacements = Table[int, char]
# Table mapping characters to their replacement table.
ReplacementTable = Table[char, Replacements]

const ReplTable = {'a': {1: 'A', 2: 'B', 4: 'C', 5: 'D'}.toTable,
'b': {1: 'E'}.toTable,
'r': {2: 'F'}.toTable
}.toTable

proc replace(text: string; replTable: ReplacementTable): string =
var counts: Table[char, int] # Follow count of characters.
for c in text:
if c in replTable:
counts.mgetOrPut(c, 0).inc # Update count for this char.
let pos = counts[c]
result.add replTable[c].getOrDefault(pos, c)
else:
result.add c

echo replace("abracadabra", ReplTable)
</syntaxhighlight>

{{out}}
<pre>AErBcadCbFD
</pre>


=={{header|Perl}}==
=={{header|Perl}}==
<lang perl>use strict;
<syntaxhighlight lang="perl">use strict;
use warnings;
use warnings;
use feature 'say';
use feature 'say';
Line 299: Line 819:


my $word = 'abracadabra';
my $word = 'abracadabra';
say "$word -> " . transmogrify $word, 'a' => 'AB_CD', 'r' => '_F', 'b' => 'E';</lang>
say "$word -> " . transmogrify $word, 'a' => 'AB_CD', 'r' => '_F', 'b' => 'E';</syntaxhighlight>
{{out}}
{{out}}
<pre>abracadabra -> AErBcadCbFD</pre>
<pre>abracadabra -> AErBcadCbFD</pre>
Line 307: Line 827:
=={{header|Phix}}==
=={{header|Phix}}==
Couldn't really decide which I prefer so posted both.
Couldn't really decide which I prefer so posted both.
<!--<lang Phix>(phixonline)-->
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">replace_nth</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">r</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">replace_nth</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">r</span><span style="color: #0000FF;">)</span>
Line 331: Line 851:
<span style="color: #0000FF;">{{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">},</span><span style="color: #008000;">'r'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"F"</span><span style="color: #0000FF;">}}</span>
<span style="color: #0000FF;">{{</span><span style="color: #000000;">2</span><span style="color: #0000FF;">},</span><span style="color: #008000;">'r'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"F"</span><span style="color: #0000FF;">}}</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">replace_nths</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"abracadabra"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">r</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">replace_nths</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"abracadabra"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">r</span><span style="color: #0000FF;">)</span>
<!--</lang>-->
<!--</syntaxhighlight>-->
{{out}}
{{out}}
<pre>
<pre>
Line 337: Line 857:
"AErBcadCbFD"
"AErBcadCbFD"
</pre>
</pre>

=={{header|Phixmonti}}==
<syntaxhighlight lang="Phixmonti">/# Rosetta Code problem: https://rosettacode.org/wiki/Selectively_replace_multiple_instances_of_a_character_within_a_string
by Galileo, 11/2022 #/

include ..\Utilitys.pmt

"ABaCD" var A "Eb" var B "rF" var R

"abracadabra" len for >ps
tps get tochar
dup "a" == if drop A pop var A tps set else
dup "b" == if drop B pop var B tps set else
"r" == if R pop var R tps set
endif endif endif
ps> drop
endfor

pstack</syntaxhighlight>
{{out}}
<pre>
["AErBcadCbFD"]

=== Press any key to exit ===</pre>


=={{header|Python}}==
=={{header|Python}}==
{{trans|Julia}}
{{trans|Julia}}
<lang python>from collections import defaultdict
<syntaxhighlight lang="python">from collections import defaultdict


rep = {'a' : {1 : 'A', 2 : 'B', 4 : 'C', 5 : 'D'}, 'b' : {1 : 'E'}, 'r' : {2 : 'F'}}
rep = {'a' : {1 : 'A', 2 : 'B', 4 : 'C', 5 : 'D'}, 'b' : {1 : 'E'}, 'r' : {2 : 'F'}}
Line 353: Line 897:


print('abracadabra ->', trstring('abracadabra', rep))
print('abracadabra ->', trstring('abracadabra', rep))
</syntaxhighlight>
</lang>


===Alternative===
<syntaxhighlight lang="python">import functools

from typing import Iterable
from typing import Tuple


@functools.cache
def find_nth(s: str, sub: str, n: int) -> int:
assert n >= 1
if n == 1:
return s.find(sub)
return s.find(sub, find_nth(s, sub, n - 1) + 1)


def selective_replace(s: str, ops: Iterable[Tuple[int, str, str]]) -> str:
chars = list(s)
for n, old, new in ops:
chars[find_nth(s, old, n)] = new
return "".join(chars)


print(
selective_replace(
"abracadabra",
[
(1, "a", "A"), # the first 'a' with 'A'
(2, "a", "B"), # the second 'a' with 'B'
(4, "a", "C"), # the fourth 'a' with 'C'
(5, "a", "D"), # the fifth 'a' with 'D'
(1, "b", "E"), # the first 'b' with 'E'
(2, "r", "F"), # the second 'r' with 'F'
],
)
)</syntaxhighlight>
{{out}}
<pre>AErBcadCbFD</pre>


Or, as a map-accumulation:
<syntaxhighlight lang="python">'''Instance-specific character replacement rules'''

from functools import reduce


# nthInstanceReplaced :: Dict Char [(None | Char)] ->
# String -> String
def nthInstanceReplaced(ruleMap):
def go(a, c):
ds = a.get(c, None)
return (
dict(a, **{c: ds[1:]}),
ds[0] or c
) if ds else (a, c)

return lambda s: ''.join(
mapAccumL(go)(ruleMap)(s)[1]
)


# ------------------------- TEST -------------------------
def main():
'''Rule-set applied to a given string.'''

print(
nthInstanceReplaced({
'a': ['A', 'B', None, 'C', 'D'],
'b': ['E'],
'r': [None, 'F']
})(
"abracadabra"
)
)


# ----------------------- GENERIC ------------------------

# mapAccumL :: (acc -> x -> (acc, y)) ->
# acc -> [x] -> (acc, [y])
def mapAccumL(f):
'''A tuple of an accumulation and a map
with accumulation from left to right.
'''
def go(a, x):
return second(lambda v: a[1] + [v])(
f(a[0], x)
)
return lambda acc: lambda xs: reduce(
go, xs, (acc, [])
)


# second :: (a -> b) -> ((c, a) -> (c, b))
def second(f):
'''A simple function lifted to a function over a tuple,
with f applied only to the second of two values.
'''
return lambda xy: (xy[0], f(xy[1]))


# MAIN ---
if __name__ == '__main__':
main()</syntaxhighlight>
{{Out}}
<pre>AErBcadCbFD</pre>

=={{header|Quackery}}==

If there is no nth instance of a particular character in the string, <code>selectivereplace</code> will disregard it. For example, if the nest of replacements in the example included <code>[ 1 char z char ! ]</code> the output would be the same, as there is no z in "abracadabra".

<syntaxhighlight lang="Quackery"> [ dup size temp put
witheach
[ over = if
[ swap 1 -
tuck 0 = if
[ i^ temp replace
conclude ] ] ]
2drop
temp take ] is nfind ( n c $ --> n )

[ temp put
[] [] rot witheach
[ 2 pluck
dip rot join unrot
nested join ]
[] swap witheach
[ do
temp share nfind
join ]
temp take 0 join unrot
witheach
[ dip [ behead rot ]
poke swap ]
drop
-1 split drop ] is selectivereplace ( [ $ --> $ )


' [ [ 1 char a char A ]
[ 2 char a char B ]
[ 4 char a char C ]
[ 5 char a char D ]
[ 1 char b char E ]
[ 2 char r char F ] ]
$ "abracadabra"
selectivereplace echo$</syntaxhighlight>

{{out}}

<pre>AErBcadCbFD</pre>


=={{header|Raku}}==
=={{header|Raku}}==
Set up to not particularly rely on absolute structure of the word. Demonstrate with both the original 'abracadabra' and with a random shuffled instance.
Set up to not particularly rely on absolute structure of the word. Demonstrate with both the original 'abracadabra' and with a random shuffled instance.


<lang perl6>sub mangle ($str is copy) {
<syntaxhighlight lang="raku" line>sub mangle ($str is copy) {
$str.match(:ex, 'a')».from.map: { $str.substr-rw($_, 1) = 'ABaCD'.comb[$++] };
$str.match(:ex, 'a')».from.map: { $str.substr-rw($_, 1) = 'ABaCD'.comb[$++] };
$str.=subst('b', 'E');
$str.=subst('b', 'E');
Line 368: Line 1,061:
say $_, ' -> ', .&mangle given 'abracadabra';
say $_, ' -> ', .&mangle given 'abracadabra';


say $_, ' -> ', .&mangle given 'abracadabra'.comb.pick(*).join;</lang>
say $_, ' -> ', .&mangle given 'abracadabra'.comb.pick(*).join;</syntaxhighlight>


{{out}}
{{out}}
Line 374: Line 1,067:
caarabadrab -> cABraECdFDb</pre>
caarabadrab -> cABraECdFDb</pre>


=={{header|Vlang}}==
=={{header|RPL}}==
The character "-" in the rule string means that no replacement should be made for the occurrence concerned. Any other character can be chosen by modifying the code appropriately.

Due to the use of <code>INCR</code> and <code>REPL</code> instructions, this program will only work directly on HP48 compatible RPL versions. HP28 users must have programmed their own version of these instructions.
{| class="wikitable"
! RPL code
! Comment
|-
|
≪ 0 → car rule occ
≪ 1 OVER SIZE '''FOR''' j
'''IF''' DUP j DUP SUB car == '''THEN'''
rule 'occ' INCR DUP SUB
'''IF''' DUP "-" == '''THEN''' DROP '''ELSE''' j SWAP REPL '''END'''
'''END NEXT'''
≫ ≫ ''''REPLR'''' STO
|
'''REPLR''' ''( "string" "character" "rule" -- "string" ) ''
loop for j = 1 to string length
if string[j] == character
get todo = rule[++occ]
replace if todo is different from "-"
end if end loop
return string
|}
{{in}}
<pre>
"abracadabra"
≪ "a" "AB-CD" REPLR "b" "E" REPLR "r" "-F" REPLR ≫ EVAL
</pre>
{{out}}
<pre>
1: "AErBcadCbFD"
</pre>

=={{header|Ruby}}==
<syntaxhighlight lang="ruby">str = "abracadabra"
rules = [
["a", 1, "A"],
["a", 2, "B"],
["a", 4, "C"],
["a", 5, "D"],
["b", 1, "E"],
["r", 2, "F"]]

indices = Hash.new{[]}
str.each_char.with_index{|c, i| indices[c] <<= i}

rules.each{|char, i, to| str[indices[char][i-1]] = to}

p str</syntaxhighlight>
{{out}}
<pre>"AErBcadCbFD"
</pre>
=={{header|sed}}==
<syntaxhighlight lang="sed">s/a\([^a]*\)a\([^a]*a[^a]*\)a\([^a]*\)a/A\1B\2C\3D/
s/b/E/
s/\(r[^r]*\)r/\1F/</syntaxhighlight>

=={{header|V (Vlang)}}==
A similar approach to the C++ entry.
A similar approach to the C++ entry.
<lang ruby>fn selectively_replace_chars(s string, char_map map[string]string) string {
<syntaxhighlight lang="ruby">fn selectively_replace_chars(s string, char_map map[string]string) string {
mut bytes := s.bytes()
mut bytes := s.bytes()
mut counts := {
mut counts := {
Line 403: Line 1,155:
println('$old -> $new')
println('$old -> $new')
}
}
}</lang>
}</syntaxhighlight>


{{out}}
{{out}}
Line 416: Line 1,168:
{{libheader|Wren-regex}}
{{libheader|Wren-regex}}
Not particularly succinct but, thanks to a recently added library method, better than it would have been :)
Not particularly succinct but, thanks to a recently added library method, better than it would have been :)
<lang ecmascript>import "./seq" for Lst
<syntaxhighlight lang="wren">import "./seq" for Lst
import "./str" for Str
import "./str" for Str


Line 427: Line 1,179:
s = Str.replace(s, "b", "E", 1)
s = Str.replace(s, "b", "E", 1)
s = Str.replace(s, "r", "F", 2, 1)
s = Str.replace(s, "r", "F", 2, 1)
System.print(s)</lang>
System.print(s)</syntaxhighlight>


{{out}}
{{out}}
Line 435: Line 1,187:


Alternatively, using regular expressions (embedded script) producing output as before.
Alternatively, using regular expressions (embedded script) producing output as before.
<lang ecmascript>import "./regex" for Regex
<syntaxhighlight lang="wren">import "./regex" for Regex


var s = "abracadabra"
var s = "abracadabra"
Line 445: Line 1,197:
s = Regex.compile("b").replace(s, "E")
s = Regex.compile("b").replace(s, "E")
s = Regex.compile("r").replaceAll(s, "F", 2, 1)
s = Regex.compile("r").replaceAll(s, "F", 2, 1)
System.print(s)</lang>
System.print(s)</syntaxhighlight>


=={{header|XPL0}}==
=={{header|XPL0}}==
<lang XPL0>string 0;
<syntaxhighlight lang="xpl0">string 0;
proc Mangle(S);
proc Mangle(S);
char S, A, B, R;
char S, A, B, R;
Line 467: Line 1,219:
S:= "caarabadrab";
S:= "caarabadrab";
Text(0, S); Text(0, " -> "); Mangle(S); Text(0, S); CrLf(0);
Text(0, S); Text(0, " -> "); Mangle(S); Text(0, S); CrLf(0);
]</lang>
]</syntaxhighlight>


{{out}}
{{out}}

Latest revision as of 10:51, 4 February 2024

Task
Selectively replace multiple instances of a character within a string
You are encouraged to solve this task according to the task description, using any language you may know.
Task

This is admittedly a trivial task but I thought it would be interesting to see how succinctly (or otherwise) different languages can handle it.

Given the string: "abracadabra", replace programatically:

  • the first 'a' with 'A'
  • the second 'a' with 'B'
  • the fourth 'a' with 'C'
  • the fifth 'a' with 'D'
  • the first 'b' with 'E'
  • the second 'r' with 'F'


Note that there is no replacement for the third 'a', second 'b' or first 'r'.

The answer should, of course, be : "AErBcadCbFD".

Other tasks related to string operations:
Metrics
Counting
Remove/replace
Anagrams/Derangements/shuffling
Find/Search/Determine
Formatting
Song lyrics/poems/Mad Libs/phrases
Tokenize
Sequences


11l

Translation of: Python
V rep = [‘a’ = [1 = ‘A’, 2 = ‘B’, 4 = ‘C’, 5 = ‘D’], ‘b’ = [1 = ‘E’], ‘r’ = [2 = ‘F’]]

F trstring(oldstring, repdict)
   DefaultDict[Char, Int] seen
   V newchars = ‘’
   L(c) oldstring
      V i = ++seen[c]
      newchars ‘’= I c C repdict & i C repdict[c] {repdict[c][i]} E c
   R newchars

print(‘abracadabra -> ’trstring(‘abracadabra’, rep))
Output:
abracadabra -> AErBcadCbFD

ALGOL 68

CO in the string "abracadabra", replace the first  'a' with 'A', the second 'a' with 'B'
                                      , the fourth 'a' with 'C', the fifth  'a' with 'D'
                                        the first  'b' with 'E', the second 'r' with 'F'
CO
BEGIN
    [,]STRING replacements = ( ( "a", "ABaCD" ), ( "b", "E" ), ( "r", "rF" ) );
    [ 1 LWB replacements : 1 UPB replacements ]INT position;
    STRING input = "abracadabra";
    [ LWB input : UPB input ]CHAR output;
    FOR i FROM LWB position TO UPB position DO position[ i ] := LWB replacements[ i, 2 ] OD;
    FOR c pos FROM LWB input TO UPB input DO
        CHAR c           = input[ c pos ];
        output[ c pos ] := c;
        BOOL found      := FALSE;
        FOR r pos FROM 1 LWB replacements TO 1 UPB replacements WHILE NOT found DO
            STRING r = replacements[ r pos, 1 ];
            IF c = r[ LWB r ] THEN
                found := TRUE;
                IF position[ r pos ] <= UPB replacements[ r pos, 2 ] THEN
                    output[   c pos ]  := replacements[ r pos, 2 ][ position[ r pos ] ];
                    position[ r pos ] +:= 1;
                    found              := TRUE
                FI
            FI
        OD
    OD;
    print( ( """", input, """ -> """, output, """" ) );
    IF output /= "AErBcadCbFD" THEN print( ( " ** UNEXPECTED RESULT" ) ) FI;
    print( ( newline ) )
END
Output:
"abracadabra" -> "AErBcadCbFD"

Arturo

replacement: function [rule,ch,order][
    loop rule 'r ->
        if r\[0] = order -> return r\[1]
    return ch
]
multiReplace: function [str, rules][
    cntr: #.raw flatten couple keys rules repeat 0 size rules

    join map str 'ch [
        (key? cntr ch)? [
            cntr\[ch]: cntr\[ch] + 1
            replacement rules\[ch] ch dec cntr\[ch]
        ] -> ch
    ]
]

print multiReplace "abracadabra" #[
    a: [[0 `A`][1 `B`][3 `C`][4 `D`]]
    b: [[0 `E`]]
    r: [[1 `F`]]
]
Output:
AErBcadCbFD

AutoHotkey

str := "abracadabra"
steps := [[1, "a", "A"]
        , [2, "a", "B"]
        , [4, "a", "C"]
        , [5, "a", "D"]
        , [1, "b", "E"]
        , [2, "r", "F"]]

MsgBox % result := Selectively_replace(str, steps)
return

Selectively_replace(str, steps){
    Res := [], x := StrSplit(str)
    for i, step in steps {
        n := step.1, L := step.2, R := step.3, k := 0
        for j, v in x
            if (v=L) && (++k = n) {
                Res[j] := R
                break
            }
    }
    for j, v in x
        result .= Res[j] = "" ? x[j] : Res[j]
    return result
}
Output:
AErBcadCbFD

C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  const char string[] = "abracadabra";

  char *replaced = malloc(sizeof(string));
  strcpy(replaced, string);

  // Null terminated replacement character arrays
  const char *aRep = "ABaCD";
  const char *bRep = "E";
  const char *rRep = "rF";

  for (char *c = replaced; *c; ++c) {
    switch (*c) {
    case 'a':
      if (*aRep)
        *c = *aRep++;
      break;
    case 'b':
      if (*bRep)
        *c = *bRep++;
      break;
    case 'r':
      if (*rRep)
        *c = *rRep++;
      break;
    }
  }

  printf("%s\n", replaced);

  free(replaced);
  return 0;
}
Output:
AErBcadCbFD

C++

#include <map>
#include <iostream>
#include <string>

int main()
{
  std::map<char, std::string> rep = 
    {{'a', "DCaBA"}, // replacement string is reversed
     {'b', "E"},
     {'r', "Fr"}};

  std::string magic = "abracadabra";

  for(auto it = magic.begin(); it != magic.end(); ++it)
  {
    if(auto f = rep.find(*it); f != rep.end() && !f->second.empty())
    {
      *it = f->second.back();
      f->second.pop_back();
    }
  }

  std::cout << magic << "\n";
}
Output:
AErBcadCbFD

Delphi

Works with: Delphi version 6.0


var TestStr: string = 'abracadabra';


function FindNthChar(C: char; S: string; N: integer): integer;
{Routine to find the Nth version of C, string S}
begin
for Result:=1 to Length(S) do
 if S[Result]=C then
	begin
	Dec(N);
	if N<=0 then exit;
	end;
Result:=-1;
end;


procedure ReplaceNthChar(COld,CNew: char; var S: string; N: integer);
{Find and replace the Nth version COld with CNew}
var Inx: integer;
begin
Inx:=FindNthChar(COld,S,N);
if Inx<1 then exit;
S[Inx]:=CNew;
end;


procedure SelectivelyReplaceChars(Memo: TMemo);
var I: integer;
begin
Memo.Lines.Add('Before: '+TestStr);
{Do the replacement toward the end of string first}
ReplaceNthChar('a','D',TestStr,5);
ReplaceNthChar('a','C',TestStr,4);
ReplaceNthChar('a','B',TestStr,2);
ReplaceNthChar('r','F',TestStr,2);
ReplaceNthChar('a','A',TestStr,1);
ReplaceNthChar('b','E',TestStr,1);
Memo.Lines.Add('After: '+TestStr);
end;
Output:
Before: AErBcadCbFD
After: AErBcAdCEFD
Elapsed Time: 1.749 ms.


EMal

fun transmogrify  = text by text input, Map replacements
  Map indexes = text%int[]
  text result = ""
  for each text ch in input
    result.append(when(replacements.has(++indexes[ch] + ch), replacements[indexes[ch] + ch], ch))
  end
  return result
end
writeLine(transmogrify("abracadabra",
  text%text["1a" => "A", "2a" => "B", "4a" => "C", "5a" => "D", "1b" => "E", "2r" => "F"]))
Output:
AErBcadCbFD

Factor

Works with: Factor version 0.99 2022-04-03
USING: assocs formatting grouping kernel random sequences ;

CONSTANT: instrs {
    CHAR: a 1 CHAR: A
    CHAR: a 2 CHAR: B
    CHAR: a 4 CHAR: C
    CHAR: a 5 CHAR: D
    CHAR: b 1 CHAR: E
    CHAR: r 2 CHAR: F
}

: counts ( seq -- assoc )
    H{ } clone swap [ 2dup swap inc-at dupd of ] zip-with nip ;

: replace-nths ( seq instrs -- seq' )
    [ counts ] dip 3 group [ f suffix 2 group ] map substitute keys ;

: test ( str -- )
    dup instrs replace-nths "" like "%s -> %s\n" printf ;


"abracadabra" test
"abracadabra" randomize test
Output:
abracadabra -> AErBcadCbFD
caaarrbabad -> cABarFECbDd

FreeBASIC

Function replaceChar(Byref S As String) As String
    Dim As String A = "ABaCD", B = "Eb", R = "rF"
    Dim As Byte pA = 1, pB = 1, pR = 1
    For i As Byte = 0 To Len(S)
        Select Case Mid(S,i,1)
        Case "a"
            Mid(S,i,1) = Mid(A,pA,1)
            pA += 1
        Case "b"
            Mid(S,i,1) = Mid(B,pB,1)
            pB += 1
        Case "r"
            Mid(S,i,1) = Mid(R,pR,1)
            pR += 1
        End Select
    Next i
    Return S
End Function

Dim As String S
S = "abracadabra"
Print S; " -> "; replaceChar(S)
S = "caarabadrab"
Print S; " -> "; replaceChar(S)
Sleep
Output:
abracadabra -> AErBcadCbFD
caaarrbabad -> cABarFECbDd

FutureBasic

include "NSLog.incl"

void local fn DoIt
  long               a = 0, b = 0, r = 0, length, i
  CFMutableStringRef string = fn MutableStringWithString( @"abracadabra" )
  CFStringRef        s
  
  length = len(string)
  for i = 0 to length - 1
    s = NULL
    select ( mid(string,i,1) )
      case @"a"
        a++
        select ( a )
          case 1 : s = @"A"
          case 2 : s = @"B"
          case 4 : s = @"C"
          case 5 : s = @"D"
        end select
      case @"b"
        b++
        if ( b == 1 ) then s = @"E"
      case @"r"
        r++
        if ( r == 2 ) then s = @"F"
    end select
    if ( s ) then mid(string,i,1) = s
  next
  
  NSLog(@"%@",string)
end fn

fn DoIt

HandleEvents
Output:
AErBcadCbFD

Go

Translation of: Wren
package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "abracadabra"
    ss := []byte(s)
    var ixs []int
    for ix, c := range s {
        if c == 'a' {
            ixs = append(ixs, ix)
        }
    }
    repl := "ABaCD"
    for i := 0; i < 5; i++ {
        ss[ixs[i]] = repl[i]
    }
    s = string(ss)
    s = strings.Replace(s, "b", "E", 1)
    s = strings.Replace(s, "r", "F", 2)
    s = strings.Replace(s, "F", "r", 1)
    fmt.Println(s)
}
Output:
AErBcadCbFD

Haskell

As a map-accumulation:

import Data.List (mapAccumL)
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)

---------- POSITIONAL CHARACTER-REPLACEMENT RULES --------

nthCharsReplaced :: M.Map Char [Maybe Char] -> String -> String
nthCharsReplaced ruleMap = snd . mapAccumL go ruleMap
  where
    go a c =
      case M.lookup c a of
        Nothing -> (a, c)
        Just [] -> (a, c)
        Just (d : ds) ->
          ( M.insert c ds a,
            fromMaybe c d
          )

--------------------------- TEST -------------------------
main :: IO ()
main = putStrLn $ nthCharsReplaced rules "abracadabra"

rules :: M.Map Char [Maybe Char]
rules =
  M.fromList
    [ ('a', (Just <$> "AB") <> [Nothing] <> (Just <$> "CD")),
      ('b', [Just 'E']),
      ('r', [Nothing, Just 'F'])
    ]
Output:
AErBcadCbFD

J

   upd=: {{ x (n{I.y=m)} y }}
   'ABCD' 'a' upd 0 1 3 4 'E' 'b' upd 0 'F' 'r' upd 1 'abracadabra'
AErBcadCbFD

upd here takes four arguments -- two on the left (replacement characters, original character) and two on the right(index values for which instances to replace, and the original string).

However, here's a more compact approach (the first item in the left argument is the target, and the rest of the left argument explicitly provides values for every instance of that item in the right argument):

   chg=: {{ (}.x) (I.y={.x)} y}}
   'aABaCD' chg 'bEb' chg 'rrF' chg 'abracadabra'
AErBcadCbFD


Java

Translation of: JavaScript

Here's an example translated from JavaScript.

int findNth(String s, char c, int n) {
    if (n == 1) return s.indexOf(c);
    return s.indexOf(c, findNth(s, c, n - 1) + 1);
}

String selectiveReplace(String s, Set... ops) {
    char[] chars = s.toCharArray();
    for (Set set : ops)
        chars[findNth(s, set.old, set.n)] = set.rep;
    return new String(chars);
}

record Set(int n, char old, char rep) { }
selectiveReplace("abracadabra",
    new Set(1, 'a', 'A'),
    new Set(2, 'a', 'B'),
    new Set(4, 'a', 'C'),
    new Set(5, 'a', 'D'),
    new Set(1, 'b', 'E'),
    new Set(2, 'r', 'F'));
Output:
AErBcadCbFD


JavaScript

function findNth(s, c, n) {
  if (n === 1) return s.indexOf(c);
  return s.indexOf(c, findNth(s, c, n - 1) + 1);
}

function selectiveReplace(s, ops) {
  const chars = Array.from(s);
  for ([n, old, rep] of ops) {
    chars[findNth(s, old, n)] = rep;
  }
  return chars.join("");
}

console.log(
  selectiveReplace("abracadabra", [
    [1, "a", "A"], // the first 'a' with 'A'
    [2, "a", "B"], // the second 'a' with 'B'
    [4, "a", "C"], // the fourth 'a' with 'C'
    [5, "a", "D"], // the fifth 'a' with 'D'
    [1, "b", "E"], // the first 'b' with 'E'
    [2, "r", "F"], // the second 'r' with 'F'
  ])
);
Output:
AErBcadCbFD


Or, expressed as a map-accumulation:

(() => {
    "use strict";

    // -- INSTANCE-SPECIFIC CHARACTER REPLACEMENT RULES --

    // # nthInstanceReplaced :: Dict Char [(Null | Char)] ->
    const nthInstanceReplaced = ruleMap =>
        // A string defined by replacements specified for
        // the nth instances of various characters.
        s => mapAccumL(
            (a, c) => c in a ? (() => {
                const ds = a[c];

                return Boolean(ds.length) ? [
                    Object.assign(a, {[c]: ds.slice(1)}),
                    ds[0] || c
                ] : [a, c];
            })() : [a, c]
        )(Object.assign({}, ruleMap))(
            [...s]
        )[1].join("");


    // ---------------------- TEST -----------------------
    const main = () =>
    // Instance-specific character replacement rules.
        nthInstanceReplaced({
            a: ["A", "B", null, "C", "D"],
            b: ["E"],
            r: [null, "F"]
        })(
            "abracadabra"
        );


    // --------------------- GENERIC ---------------------

    // mapAccumL :: (acc -> x -> (acc, y)) -> acc ->
    // [x] -> (acc, [y])
    const mapAccumL = f =>
    // A tuple of an accumulation and a list
    // obtained by a combined map and fold,
    // with accumulation from left to right.
        acc => xs => [...xs].reduce(
            ([a, bs], x) => second(
                v => [...bs, v]
            )(
                f(a, x)
            ),
            [acc, []]
        );


    // second :: (a -> b) -> ((c, a) -> (c, b))
    const second = f =>
    // A function over a simple value lifted
    // to a function over a tuple.
    // f (a, b) -> (a, f(b))
        ([x, y]) => [x, f(y)];


    // MAIN --
    return main();
})();
Output:
AErBcadCbFD

jq

In this section, array-indexing and occurrence-counting are both 0-based except for the transcription of the task in `steps`.

Generic functions

# Emit empty if the stream does not have an $n-th item
# Note: jq's nth/2 does not serve our purposes.
def n_th($n; stream):
  if $n < 0 then empty
  else foreach stream as $x (-1; .+1; if . == $n then $x else empty end)
  end;

def positions(stream; $v):
  foreach stream as $x (-1; .+1; if $v == $x then . else empty end);

# Input: an array or string.
# Output: the input with an occurrence of $old replaced by $new.
# . and $reference are assumed to be of the same type and length.
# The search occurs in $reference and the corresponding spot in . is modified.
def replace_nth($occurrence; $old; $new; $reference):
  if type == "array"
  then ($reference | n_th($occurrence; positions(.[]; $old)) // null) as $ix
  | if $ix then .[:$ix] + [$new] + .[$ix + 1:] else . end
  else explode
  | replace_nth($occurrence; $old|explode|first; $new|explode|first; $reference|explode)
  | implode
  end;

The task

def steps:
  [1, "a", "A"],
  [2, "a", "B"],
  [4, "a", "C"],
  [5, "a", "D"],
  [1, "b", "E"],
  [2, "r", "F"];

def task(steps):
  . as $reference
  | reduce steps as [$occurrence, $old, $new] (.;
      replace_nth($occurrence - 1; $old; $new; $reference ));
  
"abracadabra" | task(steps)
Output:
AErBcadCbFD

Julia

rep = Dict('a' => Dict(1 => 'A', 2 => 'B', 4 => 'C', 5 => 'D'), 'b' => Dict(1 => 'E'), 'r' => Dict(2 => 'F'))

function trstring(oldstring, repdict)
    seen, newchars = Dict{Char, Int}(), Char[]
    for c in oldstring
        i = get!(seen, c, 1)
        push!(newchars, haskey(repdict, c) && haskey(repdict[c], i) ? repdict[c][i] : c)
        seen[c] += 1
    end
    return String(newchars)
end

println("abracadabra -> ", trstring("abracadabra", rep))
Output:

Same as Perl.

Lambdatalk

1) first answer

We first translate the replacements program into a sequence of rules

the first  'a' with 'A'   -> aA1
...and so on

Then we add to the existing set of array functions a new one finding the indexes of some value in a given array.

{def A.findindexes

 {def A.findindexes.rec
  {lambda {:v :a :b :i}
   {if {A.empty? :a}
    then :b
    else {A.findindexes.rec :v {A.rest :a} 
                            {if {W.equal? {A.first :a} :v}
                             then {A.addlast! :i :b}
                             else :b} 
                            {+ :i 1}} }}}

 {lambda {:v :a}
  {A.findindexes.rec :v :a {A.new} 0} }}
-> A.findindexes
 
{A.findindexes a {A.split abracadabra}}
-> [0,3,5,7,10]
... and so on

Using findindexes we can translate the aA1 aB2 aC4 aD5 bE1 rF2 sequence into a new one where numbers are replaced by indexes in the given string, here abracadabra.

{def replacements.rules
  {lambda {:w :r}
   {A.new 
    {W.get 0 :r}
    {W.get 1 :r}
    {A.get {- {W.get 2 :r} 1}  // arrays begin at 0
           {A.findindexes {W.get 0 :r} {A.split :w}}}}}}
->  replacements.rules

{A.join {replacements.rules abracadabra aA1}}
-> aA0
... and so on

Finally the replacements function will apply this sequence of rules to the word.

{def replacements 

 {def replacements.rec
  {lambda {:word :rules}
   {if {A.empty? :rules}
    then {A.join :word}
    else {replacements.rec {A.set! {A.get 2 {A.first :rules}}
                                   {A.get 1 {A.first :rules}}
                                   :word} 
                           {A.rest :rules}} }}}

 {lambda {:word :rules}
  {replacements.rec
   {A.split :word} 
   {A.map {replacements.rules :word} {A.new :rules}} }}}
-> replacements

{replacements abracadabra aA1 aB2 aC4 aD5 bE1 rF2}
-> AErBcadCbFD
  (AErBcadCbFD)

{replacements caaarrbabad aA1 aB2 aC4 aD5 bE1 rF2} 
-> cABarFECbDd
  (cABarFECbDd)

2) second answer using regexps

Here is a quick & dirty answer using the S.replace_once primitive.

{def multrepl_rex
 {lambda {:word :rules}
  {if {A.empty? :rules}
   then :word
   else {multrepl_rex 
         {S.replace_once {W.first {A.first :rules}}
                     by {W.last {A.first :rules}}
                     in :word }
         {A.rest :rules}} }}}
-> multrepl_rex

{multrepl_rex
 abracadabra
  {A.new
   aA aB a3 aC aD 3a   // save third "a" as "3" and restore it
   bE                  // first "b"
   r1 rF 1r            // save first "r" as "1" and restore it
}}
-> AErBcadCbFD 
  (AErBcadCbFD)

Nim

import std/tables

type
  # Table of replacements for a character.
  Replacements = Table[int, char]
  # Table mapping characters to their replacement table.
  ReplacementTable = Table[char, Replacements]

const ReplTable = {'a': {1: 'A', 2: 'B', 4: 'C', 5: 'D'}.toTable,
                   'b': {1: 'E'}.toTable,
                   'r': {2: 'F'}.toTable
                  }.toTable

proc replace(text: string; replTable: ReplacementTable): string =
  var counts: Table[char, int]  # Follow count of characters.
  for c in text:
    if c in replTable:
      counts.mgetOrPut(c, 0).inc  # Update count for this char.
      let pos = counts[c]
      result.add replTable[c].getOrDefault(pos, c)
    else:
      result.add c

echo replace("abracadabra", ReplTable)
Output:
AErBcadCbFD

Perl

use strict;
use warnings;
use feature 'say';

sub transmogrify {
    my($str, %sub) = @_;
    for my $l (keys %sub) {
        $str =~ s/$l/$_/ for split '', $sub{$l};
        $str =~ s/_/$l/g;
    }
    $str
}

my $word = 'abracadabra';
say "$word -> " . transmogrify $word, 'a' => 'AB_CD', 'r' => '_F', 'b' => 'E';
Output:
abracadabra -> AErBcadCbFD


Phix

Couldn't really decide which I prefer so posted both.

with javascript_semantics
function replace_nth(string s, r)
    string res = s
    for i=1 to length(r) by 3 do
        res[find_all(r[i],s)[r[i+1]-'0']] = r[i+2]
    end for
    return res
end function
?replace_nth("abracadabra","a1Aa2Ba4Ca5Db1Er2F")

-- Alternative version
function replace_nths(string s, sequence r)
    for icr in r do
        {sequence idx, integer ch, string reps} = icr
        s = reinstate(s,extract(find_all(ch,s),idx),reps)
    end for
    return s
end function

constant r = {{{1,2,4,5},'a',"ABCD"},
              {{1},'b',"E"},
              {{2},'r',"F"}}
?replace_nths("abracadabra",r)
Output:
"AErBcadCbFD"
"AErBcadCbFD"

Phixmonti

/# Rosetta Code problem: https://rosettacode.org/wiki/Selectively_replace_multiple_instances_of_a_character_within_a_string
by Galileo, 11/2022 #/

include ..\Utilitys.pmt

"ABaCD" var A "Eb" var B "rF" var R

"abracadabra" len for >ps
    tps get tochar
    dup "a" == if drop A pop var A tps set else
    dup "b" == if drop B pop var B tps set else
    "r" == if R pop var R tps set
    endif endif endif
    ps> drop
endfor

pstack
Output:
["AErBcadCbFD"]

=== Press any key to exit ===

Python

Translation of: Julia
from collections import defaultdict

rep = {'a' : {1 : 'A', 2 : 'B', 4 : 'C', 5 : 'D'}, 'b' : {1 : 'E'}, 'r' : {2 : 'F'}}
 
def trstring(oldstring, repdict):
    seen, newchars = defaultdict(lambda:1, {}), []
    for c in oldstring:
        i = seen[c]
        newchars.append(repdict[c][i] if c in repdict and i in repdict[c] else c)
        seen[c] += 1
    return ''.join(newchars)

print('abracadabra ->', trstring('abracadabra', rep))

Alternative

import functools

from typing import Iterable
from typing import Tuple


@functools.cache
def find_nth(s: str, sub: str, n: int) -> int:
    assert n >= 1
    if n == 1:
        return s.find(sub)
    return s.find(sub, find_nth(s, sub, n - 1) + 1)


def selective_replace(s: str, ops: Iterable[Tuple[int, str, str]]) -> str:
    chars = list(s)
    for n, old, new in ops:
        chars[find_nth(s, old, n)] = new
    return "".join(chars)


print(
    selective_replace(
        "abracadabra",
        [
            (1, "a", "A"),  # the first 'a' with 'A'
            (2, "a", "B"),  # the second 'a' with 'B'
            (4, "a", "C"),  # the fourth 'a' with 'C'
            (5, "a", "D"),  # the fifth 'a' with 'D'
            (1, "b", "E"),  # the first 'b' with 'E'
            (2, "r", "F"),  # the second 'r' with 'F'
        ],
    )
)
Output:
AErBcadCbFD


Or, as a map-accumulation:

'''Instance-specific character replacement rules'''

from functools import reduce


# nthInstanceReplaced :: Dict Char [(None | Char)] ->
# String -> String
def nthInstanceReplaced(ruleMap):
    def go(a, c):
        ds = a.get(c, None)
        return (
            dict(a, **{c: ds[1:]}),
            ds[0] or c
        ) if ds else (a, c)

    return lambda s: ''.join(
        mapAccumL(go)(ruleMap)(s)[1]
    )


# ------------------------- TEST -------------------------
def main():
    '''Rule-set applied to a given string.'''

    print(
        nthInstanceReplaced({
            'a': ['A', 'B', None, 'C', 'D'],
            'b': ['E'],
            'r': [None, 'F']
        })(
            "abracadabra"
        )
    )


# ----------------------- GENERIC ------------------------

# mapAccumL :: (acc -> x -> (acc, y)) ->
# acc -> [x] -> (acc, [y])
def mapAccumL(f):
    '''A tuple of an accumulation and a map
       with accumulation from left to right.
    '''
    def go(a, x):
        return second(lambda v: a[1] + [v])(
            f(a[0], x)
        )
    return lambda acc: lambda xs: reduce(
        go, xs, (acc, [])
    )


# second :: (a -> b) -> ((c, a) -> (c, b))
def second(f):
    '''A simple function lifted to a function over a tuple,
       with f applied only to the second of two values.
    '''
    return lambda xy: (xy[0], f(xy[1]))


# MAIN ---
if __name__ == '__main__':
    main()
Output:
AErBcadCbFD

Quackery

If there is no nth instance of a particular character in the string, selectivereplace will disregard it. For example, if the nest of replacements in the example included [ 1 char z char ! ] the output would be the same, as there is no z in "abracadabra".

 [ dup size temp put
   witheach
     [ over = if
        [ swap 1 -
          tuck 0 = if
            [ i^ temp replace
              conclude ] ] ]
    2drop
    temp take ]               is nfind            ( n c $ --> n )

  [ temp put
    [] [] rot witheach
      [ 2 pluck
        dip rot join unrot
        nested join ]
    [] swap witheach
      [ do
        temp share nfind
        join ]
    temp take 0 join unrot
    witheach
      [ dip [ behead rot ]
        poke swap ]
    drop
    -1 split drop ]           is selectivereplace (   [ $ --> $ )


  ' [ [ 1 char a char A ]
      [ 2 char a char B ]
      [ 4 char a char C ]
      [ 5 char a char D ]
      [ 1 char b char E ]
      [ 2 char r char F ] ]
  $ "abracadabra"
  selectivereplace echo$
Output:
AErBcadCbFD

Raku

Set up to not particularly rely on absolute structure of the word. Demonstrate with both the original 'abracadabra' and with a random shuffled instance.

sub mangle ($str is copy) {
    $str.match(:ex, 'a')».from.map: { $str.substr-rw($_, 1) = 'ABaCD'.comb[$++] };
    $str.=subst('b', 'E');
    $str.substr-rw($_, 1) = 'F' given $str.match(:ex, 'r')».from[1];
    $str
}

say $_, ' -> ', .&mangle given 'abracadabra';

say $_, ' -> ', .&mangle given 'abracadabra'.comb.pick(*).join;
Output:
abracadabra -> AErBcadCbFD
caarabadrab -> cABraECdFDb

RPL

The character "-" in the rule string means that no replacement should be made for the occurrence concerned. Any other character can be chosen by modifying the code appropriately.

Due to the use of INCR and REPL instructions, this program will only work directly on HP48 compatible RPL versions. HP28 users must have programmed their own version of these instructions.

RPL code Comment
 ≪ 0 → car rule occ
  ≪ 1 OVER SIZE FOR j
      IF DUP j DUP SUB car == THEN
        rule 'occ' INCR DUP SUB
        IF DUP "-" == THEN DROP ELSE j SWAP REPL END
     END NEXT
≫ ≫ 'REPLR' STO
REPLR ( "string" "character" "rule" -- "string" ) 
loop for j = 1 to string length
   if string[j] == character
      get todo = rule[++occ]
      replace if todo is different from "-"
   end if end loop
return string
Input:
"abracadabra"
≪ "a" "AB-CD" REPLR "b" "E" REPLR "r" "-F" REPLR ≫ EVAL
Output:
1: "AErBcadCbFD"

Ruby

str = "abracadabra"
rules = [
  ["a", 1, "A"],
  ["a", 2, "B"],
  ["a", 4, "C"],
  ["a", 5, "D"], 
  ["b", 1, "E"], 
  ["r", 2, "F"]]

indices = Hash.new{[]}
str.each_char.with_index{|c, i| indices[c] <<= i}

rules.each{|char, i, to| str[indices[char][i-1]] = to}

p str
Output:
"AErBcadCbFD"

sed

s/a\([^a]*\)a\([^a]*a[^a]*\)a\([^a]*\)a/A\1B\2C\3D/
s/b/E/
s/\(r[^r]*\)r/\1F/

V (Vlang)

A similar approach to the C++ entry.

fn selectively_replace_chars(s string, char_map map[string]string) string {
    mut bytes := s.bytes()
    mut counts := {
        'a': 0
        'b': 0
        'r': 0
    }
    for i := s.len - 1; i >= 0; i-- {
        c := s[i].ascii_str()
        if c in ['a', 'b', 'r'] {
            bytes[i] = char_map[c][counts[c]]
            counts[c]++
        }
    }
    return bytes.bytestr()
}

fn main() {
    char_map := {
        'a': 'DCaBA'
        'b': 'bE'
        'r': 'Fr'
    }
    for old in ['abracadabra', 'caaarrbabad'] {
        new := selectively_replace_chars(old, char_map)
        println('$old -> $new')
    }
}
Output:
abracadabra -> AErBcadCbFD
caaarrbabad -> cABarFECbDd

Wren

Library: Wren-seq
Library: Wren-str
Library: Wren-regex

Not particularly succinct but, thanks to a recently added library method, better than it would have been :)

import "./seq" for Lst
import "./str" for Str

var s = "abracadabra"
var sl = s.toList
var ixs = Lst.indicesOf(sl, "a")[2]
var repl = "ABaCD"
for (i in 0..4) sl[ixs[i]] = repl[i]
s = sl.join()
s = Str.replace(s, "b", "E", 1)
s = Str.replace(s, "r", "F", 2, 1)
System.print(s)
Output:
AErBcadCbFD

Alternatively, using regular expressions (embedded script) producing output as before.

import "./regex" for Regex

var s = "abracadabra"
var split = Regex.compile("a").split(s)
var repl = "ABaCD"
var res = ""
for (i in 0...split.count-1) res = res + split[i] + repl[i]
s = res + split[-1]
s = Regex.compile("b").replace(s, "E")
s = Regex.compile("r").replaceAll(s, "F", 2, 1)
System.print(s)

XPL0

string 0;
proc Mangle(S);
char S, A, B, R;
[A:= "ABaCD";  B:= "Eb";  R:= "rF";
while S(0) do
    [case S(0) of
      ^a: [S(0):= A(0);  A:= A+1];
      ^b: [S(0):= B(0);  B:= B+1];
      ^r: [S(0):= R(0);  R:= R+1]
    other [];
    S:= S+1;
    ];
];

char S;
[S:= "abracadabra"; 
Text(0, S);  Text(0, " -> ");  Mangle(S);  Text(0, S);  CrLf(0);
S:= "caarabadrab";
Text(0, S);  Text(0, " -> ");  Mangle(S);  Text(0, S);  CrLf(0);
]
Output:
abracadabra -> AErBcadCbFD
caarabadrab -> cABraECdFDb