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

From Rosetta Code
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


C++[edit]

#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[edit]

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[edit]

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

Go[edit]

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

J[edit]

   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

Julia[edit]

 
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[edit]

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)
 

Perl[edit]

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[edit]

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"

Python[edit]

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


Raku[edit]

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

Vlang[edit]

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[edit]

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[edit]

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