2

Let's say I have a String text = "abc" and I want to replace a map of values, eg:

a->b
b->c
c->a

How would you go for it?

Because obviously:

map.entrySet().forEach(el -> text = text.replaceAll(el.getKey(), el.getValue()))

won't work, since the second replacement will overwrite also the first replacement (and at the end you won't get bca)

So how would you avoid this "replacement of the previous replacement"?

I saw this answer but I hope in a more concise and naive solution (and hopefully without the use of Apache external packages)

By the way the string can be also more than one character

Alberto Sinigaglia
  • 12,097
  • 2
  • 20
  • 48

3 Answers3

0

I came up with this solution with java streams.

String text = "abc";
Map<String, String> replaceMap = new HashMap<>();
replaceMap.put("a", "b");
replaceMap.put("b", "c");
replaceMap.put("c", "a");

System.out.println("Text = " + text);

text = Arrays.stream(text.split("")).map(x -> {
    String replacement = replaceMap.get(x);
    if (replacement != null) {
        return x.replace(x, replacement);
    } else {
        return x;
    }
}).collect(Collectors.joining(""));

System.out.println("Processed Text = " + text);

Output

Text = abc
Processed Text = bca
0

This is a problem I'd normal handle with regex replacement. The code for that in Java is a bit verbose, but this should work:

String text = "abc";
Map<String, String> map = new HashMap<>();
map.put("a", "b");
map.put("b", "c");
map.put("c", "a");

String regex = map.keySet()
                  .stream()
                  .map(s -> Pattern.quote(s))
                  .collect(Collectors.joining("|"));

String output = Pattern.compile(regex)
                       .matcher(text)
                       .replaceAll((m) -> {
                           String s = m.group();
                           String r = map.get(s);
                           return r != null ? r : s;
                       });
System.out.println(output); 
// bca

It's relatively straightforward, if a little verbose because Java. First, create a regex expression that will accept any of the keys in the map (using Pattern.quote() to sanitize them), and then use lambda replacement to pluck the appropriate replacement from the map whenever an instance is found.

The performance-intensive part is just compiling the regex in the first place; the replacement itself should make only one pass through the string.

Should be compatible with Java 1.9+

Green Cloak Guy
  • 23,793
  • 4
  • 33
  • 53
0

Java 8 onwards, there is a method called chars that returns an IntStream from which you can get a character corresponding to integer represented by the character and map it using your map.

If your map is String to String map then you could use:

text = text.chars().mapToObj(el -> map.get(String.valueOf((char)el))).
                                                 collect(Collectors.joining(""));

if your map is Character to Character then just remove String.valueOf()

text = text.chars().mapToObj(el -> map.get((char)el)).collect(Collectors.joining(""));
SomeDude
  • 13,876
  • 5
  • 21
  • 44