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

From Rosetta Code
Content added Content deleted
(Added Vlang)
Line 124: Line 124:


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

However, here's a more compact approach:

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


=={{header|Julia}}==
=={{header|Julia}}==

Revision as of 18:22, 3 June 2022

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:

<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

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