The idea is simple. You've to create a "visualised" letter-replacement, by providing 3 strings (input can be comma separated, separate inputs, or as an array). The first segment is the word you want to correct, and the second segment is the letters you want to replace, and the third segment is the replacement for the letters in segment 2.

For example:

|    | Input                       | Starting Word | Output      |
| #1 | Hello world -wo -ld +Ea +th | Hello world   | Hello Earth |
| #2 | Hello World -wo -ld +Ea +th | Hello World   | Hello Worth |
| #3 | Hello -llo +y               | Hello         | Hey         |
| #4 | Red -R -d +Gr +en           | Red           | Green       |
| #5 | mississippi -is -i +lz +p   | mississippi   | mlzslzspppp |
| #6 | Football -o -a +a +i        | Football      | Fiitbill    |
| #7 | mississippi -is -i +iz +p   | mississippi   | mpzspzspppp |


The replacements are to be done step-by-step with their respective pair. Here's an illustration with an input of mississippi -is -i +iz +p to give the output mpzspzsppp (see example #7 above)

| Step  | Input                         | Output        |
|------ |---------------------------    |-------------  |
| #1    | mississippi -is -i +iz +p     |               |
| #2    | mississippi -is +iz           | mizsizsippi   |
| #3    | mizsizsippi -i +p             | mpzspzspppp   |


  • Inputs are always in this order <starting_string> <list_of_letters_to_replace> <replacement_letters>.
  • Letters to replace and replacement groups will never be mixed (ie: there will never be -a +i -e +o).
  • Letters to replace are always prefixed with - and replacement letters are always prefixed with +. (The prefix is mandatory)
  • There may be more than one set of letters to replace, so you'd need to look at the prefix.
  • Assume the amount of letter groups to replace and the amount of replacement letter groups are always equal (ie: there will never be -a -e +i)
  • Replacements are case-sensitive (see example #1 and #2).
  • Replacements are done in the order they were given in the input.
  • Letter replacements can be replaced with other replacements. See example #6.
  • The first segment (starting word) will never include - or + characters.
  • This is code-golf so shortest bytes win.


Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

# Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the leaderboard snippet:

# [><>](http://esolangs.org/wiki/Fish), 121 bytes

var QUESTION_ID=96473,OVERRIDE_USER=38505;function answersUrl(e){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"http://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

  • 1
    \$\begingroup\$ Given rules 2 and 5, you really don't need to look at the prefix. With n inputs, input 0 is the base string, inputs 1 to int(n/2) are letter to replace (with prefix -) and input int(n/2)+1 to n-1 are replacement (with prefix +) \$\endgroup\$ – edc65 Oct 17 '16 at 8:51
  • \$\begingroup\$ @edc65 100% true, although the challenge was designed to have the prefix (and I could make up some weird explanation that I'm an alien who cannot process letter replacements without their prefix) but in reality, it's just another barrier to stop this being too trivial - though looking at the current answers (all are great by the way) it wasn't a complex barrier. Also fun fact, the idea behind this challenge was spawned from my friend in a Skype chat. He'd mis-spell a word (gello), and then send me the letter replacements (-g +h) because he wanted to be annoying instead of sending hello*. \$\endgroup\$ – ʰᵈˑ Oct 17 '16 at 8:57
  • 1
    \$\begingroup\$ Inputs are always in this order Why so restrictive? \$\endgroup\$ – Luis Mendo Oct 17 '16 at 9:16
  • \$\begingroup\$ @LuisMendo I guess it doesn't really matter - but it's the way my friend and I formatted it, but since answers have been posted to this requirement, I cannot really make a rule change. It wasn't questioned on the sandbox, so I didn't think of it as a negative. \$\endgroup\$ – ʰᵈˑ Oct 17 '16 at 9:23
  • 1
    \$\begingroup\$ @udioica is perfecly right and it in fact support the "Replacements are case-sensitive" rule. Run the snippet in the JavaScript answer to see it implemented. (#1 world` vs #2 World) \$\endgroup\$ – edc65 Oct 17 '16 at 12:31

15 Answers 15


05AB1E, 15 17 bytes


Try it online!


I                   # read starting string
 I                  # read letters to be replaced
  ð¡                # split on space
    ۇ              # and remove "-"
      I             # read replacement letters
       ð¡           # split on space
         ۇ         # and remove "+"
           ‚ø       # zip to produce pairs of [letters to replace, replacement letters]
             vy`:   # for each pair, replace in starting string

Or with a less strict input format


Try it online


JavaScript (ES6), 85 83 bytes

f=(s,n=1,l=s.split(/ \W/))=>(r=l[n+l.length/2|0])?f(s.split(l[n]).join(r),n+1):l[0]

Test cases

f=(s,n=1,l=s.split(/ \W/))=>(r=l[n+l.length/2|0])?f(s.split(l[n]).join(r),n+1):l[0]

console.log(f("Hello world -wo -ld +Ea +th"));
console.log(f("Hello World -wo -ld +Ea +th"));
console.log(f("Hello -llo +y"));
console.log(f("Red -R -d +Gr +en"));
console.log(f("mississippi -is -i +lz +p"));
console.log(f("Football -o -a +a +i"));
console.log(f("mississippi -is -i +iz +p"));


Pyke, 13 11 bytes


Try it here!

z           - input()
 [zdcmt)    - def func():
  zdc       -  input().split(" ")
     mt     -  map(>[1:], ^)
            - func()
        [   - func()
         .: - translate()

Or 2 bytes if in a different input format:


Try it here!

  • \$\begingroup\$ At work catbus.co.uk is blocked. Are you able to link an alternative test suite please? \$\endgroup\$ – ʰᵈˑ Oct 17 '16 at 9:24
  • 2
    \$\begingroup\$ @ʰᵈˑ I don't believe conforming to your (arbitrary) work firewall settings is reasonable. \$\endgroup\$ – orlp Oct 17 '16 at 10:12
  • 1
    \$\begingroup\$ @orlp - I agree, it's shit. But I don't set the firewall settings. I just wanted to test it out \$\endgroup\$ – ʰᵈˑ Oct 17 '16 at 10:25
  • 2
    \$\begingroup\$ @hd you can download Pyke at github.com/muddyfish/pyke \$\endgroup\$ – Blue Oct 17 '16 at 11:18

Perl, 58 bytes

57 bytes code + 1 for -p.

Requires first item on one line, then the replacements on the next. Big thanks to @Dada who came up with a different approach to help reduce by 4 bytes!



perl -pe '$a=<>;1while$a=~s%-(\S*)(.*?)\+(\S*)%"s/$1/$3/g;q{$2}"%ee' <<< 'Football
-o -a +a +i'
perl -pe '$a=<>;1while$a=~s%-(\S*)(.*?)\+(\S*)%"s/$1/$3/g;q{$2}"%ee' <<< 'mississippi
-is -i +iz +p'
perl -pe '$a=<>;1while$a=~s%-(\S*)(.*?)\+(\S*)%"s/$1/$3/g;q{$2}"%ee' <<< 'mississippi
-ippi -i -mess +ee +e +tenn'
  • \$\begingroup\$ 4 bytes longer, there is perl -pE 's/(.*?) -(\S*)(.*?)\+(\S*)/"(\$1=~s%$2%$4%gr).\"$3\""/ee&&redo'. I can't manage to get it any shorter, but maybe you can :) \$\endgroup\$ – Dada Oct 17 '16 at 15:51
  • 1
    \$\begingroup\$ Gotcha! 58 bytes : perl -pE '$a=<>;1while$a=~s%-(\S*)(.*?)\+(\S*)%"s/$1/$3/g;q{$2}"%ee'. (takes the string on one line, and the "flags" on the next line) \$\endgroup\$ – Dada Oct 17 '16 at 17:55
  • 1
    \$\begingroup\$ Awesome! I'm not at a computer but I'll update that tomorrow! Thanks! \$\endgroup\$ – Dom Hastings Oct 17 '16 at 20:47
  • \$\begingroup\$ Are you sure about removing the q{} surrounding $2 ? Wouldn't this fail when there are 3 - and 3 + switches? (I can't test it now, so maybe you were right so remove it ;) ) \$\endgroup\$ – Dada Oct 18 '16 at 11:13
  • \$\begingroup\$ @Dada ahhh, I did wonder why you'd added it, I tested all the cases in the test suite, but didn't think about a 3 for 3 replacement... \$\endgroup\$ – Dom Hastings Oct 18 '16 at 11:40

GNU sed 86 Bytes

Includes +1 for -r

:;s,^([^-]*)(\w+)([^-]*-)\2( [^+]*\+)(\w*),\1\5\3\2\4\5,

Try it online!


$ echo 'Hello world -wo -ld +Ea +th'|sed -rf replace.sed
Hello Earth

PHP, 98 97 bytes

for($s=$argv[$i=1];$v=$argv[++$i];)$r[$v[0]>'+'][]=substr($v,1);echo str_replace($r[1],$r[0],$s);

This challenge describes the exact behaviour of str_replace so for php it's all about making the arrays of replacements. I tried to do it using only one "substring" but that might not be the best solution. Use like:

php -r "for($s=$argv[$i=1];$v=$argv[++$i];)$r[$v[0]>'+'][]=substr($v,1);echo str_replace($r[1],$r[0],$s);" "mississippi" "-is" "-i" "+iz" "+p"

edit: 1 byte saved thanks to Titus

  • \$\begingroup\$ This is probably the shortest thing possible. But $v[0]>'+' saves one byte over $v[0]=='-'. You could also use ord($v)&4 instead. \$\endgroup\$ – Titus Oct 17 '16 at 19:06

Java 7, 153 133 bytes

String c(String[]a){String r=a[0],z[]=a[1].split(" ?-");for(int i=0;i<z.length;r=r.replace(z[i],a[2].split(" ?[+]")[i++]));return r;}

Ungolfed & test code:

Try it here.

class M{
  static String c(String[] a){
    String r = a[0],
           z[] = a[1].split(" ?-");
    for(int i = 0; i < z.length; r = r.replace(z[i], a[2].split(" ?[+]")[i++]));
    return r;

  public static void main(String[] a){
    System.out.println(c(new String[]{ "Hello world", "-wo -ld", "+Ea +th" }));
    System.out.println(c(new String[]{ "Hello World", "-wo -ld", "+Ea +th" }));
    System.out.println(c(new String[]{ "Hello", "-llo", "+y" }));
    System.out.println(c(new String[]{ "Red", "-R -d", "+Gr +en" }));
    System.out.println(c(new String[]{ "mississippi", "-is -i", "+lz +p" }));
    System.out.println(c(new String[]{ "Football", "-o -a", "+a +i" }));
    System.out.println(c(new String[]{ "mississippi", "-is -i", "+iz +p" }));


Hello Earth
Hello Worth
  • \$\begingroup\$ Does this work for the input new String[]{'Rom Ro. Rom", "-Ro." , "+No."}? Just writing something that (hopefully) matches a wrong regex. \$\endgroup\$ – Roman Gräf Oct 18 '16 at 5:09
  • \$\begingroup\$ @RomanGräf Yes, works and outputs Rom No. Rom. Btw, you can try it yourself by clicking the Try it here. link in the post, and then fork it. :) \$\endgroup\$ – Kevin Cruijssen Oct 18 '16 at 6:45
  • \$\begingroup\$ I know but I'm currently on my mobile. :( \$\endgroup\$ – Roman Gräf Oct 18 '16 at 7:29

PHP, 164 Bytes


Vim, 25 bytes


Assumes input in this format:

-is -i
+lz +p
  • +dE+r-PdiW: Combines - and + into single register, with the + turned into a -.
  • :1s<C-R>"-g: Uses the register as a code snippet, inserted directly into the :s command, with - as the separator.

Convex, 19 bytes


Try it online!


R, 98 94 bytes

Edit: saved 4 bytes thanks to @rturnbull

i=scan(,"");s=i[1];i=gsub("\\+|-","",i[-1]);l=length(i)/2;for(j in 1:l)s=gsub(i[j],i[l+j],s);s

Ungolfed and test cases

Because scan (reads input from stdin) doesn't work properly in R-fiddle I showcase the program by wrapping it in a function instead. Note that the function takes a vector as an input and can be run by e.g.: f(c("Hello world", "-wo", "-ld", "+Ea", "+th")). The gofled program above would prompt the user to input using stdin whereby typing "Hello world" -wo -ld -Ea +th in the console would yield the same result.

Run the code on R-fiddle

    s=i[1]                                   # Separate first element
    i=gsub("\\+|-","",i[-1])                 # Remove + and - from all elements except first, store as vector i
    l=length(i)/2                            # calculate the length of the vector i (should always be even)
    for(j in 1:l)s=gsub(i[j],i[l+j],s)       # iteratively match element j in i and substitute with element l+j in i
    s                                        # print to stdout
  • \$\begingroup\$ Can you provide a test suite link also, please? \$\endgroup\$ – ʰᵈˑ Oct 17 '16 at 9:23
  • \$\begingroup\$ @ʰᵈˑ added an R-fiddle test suite. Note that the test suite uses a function instead of reading input from stdin as explained in the edited answer. \$\endgroup\$ – Billywob Oct 17 '16 at 9:59
  • \$\begingroup\$ Is this answer valid, since you have to use " around the input string? \$\endgroup\$ – rturnbull Oct 17 '16 at 21:06
  • \$\begingroup\$ @rturnbull I don't see why not. Wrapping every entry with quotes and pressing enter would yield the equivalent result (e.g.: "Hello world" => enter => "-wo" => enter => "-ld" => enter => "+Ea" => enter =>"+th") which is usually how strings are read anyways. \$\endgroup\$ – Billywob Oct 17 '16 at 21:16
  • 1
    \$\begingroup\$ Yeah it's really up to the OP! I personally like your answer as-is, but I was worried that it might be invalid. Looking at answers for other languages it seems like quotes are pretty accepted. While I have your attention, I think you can golf off 4 bytes by changing l=length(i) to l=length(i)/2 and updating the later references to l. \$\endgroup\$ – rturnbull Oct 17 '16 at 21:57

Haskell, 85 78 bytes

import Data.Lists
g=map tail.words
a#b=foldl(flip$uncurry replace)a.zip(g b).g

Usage example: ("mississippi" # "-is -i") "+lz +p" -> "mlzslzspppp".

How it works:

g=map tail.words              -- helper function that splits a string into a
                              -- list of words (at spaces) and drops the first
                              -- char of each word

                zip(g b).g    -- make pairs of strings to be replaced and its
                              -- replacement
foldl(flip$uncurry replace)a  -- execute each replacement, starting with the
                              -- original string
                              -- -> "flip" flips the arguments of "uncurry replace"
                              --           i.e. string before pair of replacements
                              -- "uncurry" turns a function that expects two
                              --           lists into one that expects a list of pairs

Edit: @BlackCap found 6 bytes to save and I myself another one.

  • \$\begingroup\$ 6 bytes: import Data.Lists;a#b=foldl(uncurry replaceflip)a.zip(g b).g;g=map tail.words \$\endgroup\$ – BlackCap Oct 31 '16 at 10:52
  • \$\begingroup\$ @BlackCap: Nice, thanks! No need to make flip infix. Standard prefix is one byte shorter. \$\endgroup\$ – nimi Oct 31 '16 at 17:58

Python 3, 93 byte

def f(s):
  for n,o in zip(m.split(),p.split()):s=s.replace(n[1:],o[1:])
  return s

Try it online!

Input is a list with strings, replacement strings are space separated.

Example input: ['mississippi','-is -i','+iz +p']

  • \$\begingroup\$ Are you able to add a test suite link, please? \$\endgroup\$ – ʰᵈˑ Oct 17 '16 at 10:08
  • \$\begingroup\$ Link provided and also reduced size a little. \$\endgroup\$ – Gábor Fekete Oct 17 '16 at 10:14

PowerShell v2+, 90 bytes


Takes input as three arguments, with the - and + strings space-separated. Performs a -split on $b (the -split when acting in a unary fashion splits on whitespace), then loops |%{...} through each of those. Every iteration we're removing the -, finding the next [$i++] replacement string and removing the + from it, and using the -creplace (case-sensitive replacement) to slice and dice $a and store it back into $a. Then, $a is left on the pipeline and output is implicit.

PS C:\Tools\Scripts\golfing> .\letter-replacement-challenge.ps1 'mississippi' '-is -i' '+iz +p'

PS C:\Tools\Scripts\golfing> .\letter-replacement-challenge.ps1 'Hello world' '-wo -ld' '+Ea +th'
Hello Earth

PS C:\Tools\Scripts\golfing> .\letter-replacement-challenge.ps1 'Hello World' '-wo -ld' '+Ea +th'
Hello Worth

PHP, 106 bytes


straight forward approach. Run with php -r '<code> <arguments>.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.