Selectively replace multiple instances of a character within a string

From Rosetta Code
Revision as of 07:12, 10 June 2022 by rosettacode>Alainmarty (add lambdatalk code)
Selectively replace multiple instances of a character within a string is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
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++

<lang cpp>#include <map>

  1. include <iostream>
  2. 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";

}</lang>

Output:
AErBcadCbFD

Factor

Works with: Factor version 0.99 2022-04-03

<lang factor>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</lang>

Output:
abracadabra -> AErBcadCbFD
caaarrbabad -> cABarFECbDd

Go

Translation of: Wren

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

}</lang>

Output:
AErBcadCbFD

J

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

AErBcadCbFD</lang>

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

<lang J> chg=: {{ (}.x) (I.y={.x)} y}}

  'aABaCD' chg 'bEb' chg 'rrF' chg 'abracadabra'

AErBcadCbFD</lang>

Julia

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

</lang>

Output:

Same as Perl.

Perl

<lang 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';</lang>

Output:
abracadabra -> AErBcadCbFD

Lambdatalk

<lang Scheme> 1) Using regular expressions

{def multrepl_rex

{def multrepl_rex.rec 
 {lambda {:word :rules}
  {if {A.empty? :rules}
   then :word
   else {multrepl_rex.rec 
         {S.replace_one {W.first {A.first :rules}}
                     by {W.last {A.first :rules}}
                     in :word }
         {A.rest :rules}} }}}
{lambda {:word :rules}
 {multrepl_rex.rec :word {A.new :rules}}}}

-> multrepl_rex

{multrepl_rex

abracadabra
  aA aB a_ aC aD   // first "a", second "a", fourth "a", fifth "a"
  bE               // first "b"
  r_ rF            // second "r"
  _a _r            // restore third "a" and first "r"

} -> AEaBcrdCbFD

 (AErBcadCbFD)

</lang>

For a more general algorithm see http://lambdaway.free.fr/lambdawalks/?view=replace .

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"

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.

<lang perl6>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;</lang>

Output:
abracadabra -> AErBcadCbFD
caarabadrab -> cABraECdFDb

Vlang

A similar approach to the C++ entry. <lang ruby>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')
   }

}</lang>

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 :) <lang ecmascript>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)</lang>

Output:
AErBcadCbFD

Alternatively, using regular expressions (embedded script) producing output as before. <lang ecmascript>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)</lang>

XPL0

<lang 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); ]</lang>

Output:
abracadabra -> AErBcadCbFD
caarabadrab -> cABraECdFDb