I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Selectively replace multiple instances of a character within a string

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.

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'.

## 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'COBEGIN    [,]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"
```

## 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 <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
```

## 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
```

## 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 SEnd Function Dim As String SS = "abracadabra"Print S; " -> "; replaceChar(S)S = "caarabadrab"Print S; " -> "; replaceChar(S)Sleep`
Output:
```abracadabra -> 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
```

As a map-accumulation:

`import Data.List (mapAccumL)import qualified Data.Map.Strict as Mimport Data.Maybe (fromMaybe) ---------- POSITIONAL CHARACTER-REPLACEMENT RULES -------- nthCharsReplaced :: M.Map Char [Maybe Char] -> String -> StringnthCharsReplaced 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`

## 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`

## 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

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) `

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) `

## 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

-- 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"}}
```
Output:
```"AErBcadCbFD"
```

## 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 Iterablefrom typing import Tuple  @functools.cachedef 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 -> Stringdef 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`

## 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

## 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
```

## 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 Lstimport "./str" for Str var s = "abracadabra"var sl = s.toListvar 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