0

I want to modify two characters in the string, for example change each 'i' into 'e', and each 'e' into 'i' so text like "This is a test" will become "Thes es a tist".

I've made a solution that works, but it is boring and inelegant:

String input = "This is a test";
char a = 'i';
char b = 'e';

char[] chars = input.toCharArray();
for(int i = 0; i < chars.length; i++) {
    if(chars[i] == a) {
        chars[i] = b;
    }else if(chars[i] == b) {
        chars[i] = a;
    }
}

input = new String(chars);

How can this be accomplished with regex?

Pshemo
  • 122,468
  • 25
  • 185
  • 269
Muhammad Yojer
  • 161
  • 1
  • 10
  • @feelingunwelcome that duplicate is about swapping *two* characters (changing their positions), while this is about *changing* characters with different ones while *replacement* characters doesn't even need to exist in original string at all. – Pshemo Sep 22 '18 at 11:50

2 Answers2

5

Since Java 9 we can use Matcher#replaceAll(Function<MatchResult,String>). So you can create regex which will search for either i or e, and when it finds it let function pick replacement based on found value (like from map)

Demo

Map<String, String> replacements = Map.ofEntries(
        Map.entry("i", "e"), 
        Map.entry("e", "i")
);
String replaced = Pattern.compile("[ie]")
                         .matcher(yourString)
                         .replaceAll((match) -> replacements.get(match.group()));

But to be honest your solution doesn't look bad, especially if it is used for searching for single characters.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • That's a really cool solution, but I'm on Java 8, unfortunately. – Muhammad Yojer Sep 21 '18 at 14:56
  • 1
    I would like to remind that Java 11 will be released in this month. Anyway using map to pick replacement can be also applied in your own solution instead of `if`s. – Pshemo Sep 21 '18 at 14:58
2

A less elegant solution than Pschemo's but usable since Java 8:

static String swap(String source, String a, String b) {
    // TODO null/empty checks and length checks on a/b
    return Arrays
        // streams single characters as strings
        .stream(source.split(""))
        // maps characters to their replacement if applicable
        .map(s -> {
            if (s.equals(a)) {
                return b;
            }
            else if (s.equals(b)) {
                return a;
            }
            else {
                return s;
            }
        })
        // rejoins as single string
        .collect(Collectors.joining());
}

Invoked on "This is a test", it returns:

Thes es a tist

Note

As mentioned by others, your solution is fine as is for single characters.

Mena
  • 47,782
  • 11
  • 87
  • 106
  • 1
    We can also use instead of `if`s `Map replacements` and `map(s->replacements.getOrDefault(s,s))` to let `s` be returned in case when `replacement` doesn't have `s` key. – Pshemo Sep 21 '18 at 15:08