1

I have an input string like this

I want to go to {places} where {things} are happening.

The values for {places} and {things} are computed lazily (i.e. first i find out what all keys needs to be replaced and then compute the values for them, and then replace them in the original string).

I am able to find out all the keys and removing them using the below code.

public class Temp {
    private static final Pattern betweenCurlyBracesMatcher = Pattern.compile("\\{(.*?)\\}");

    public static void main(String args[]) {
        System.out.println(resolve2("hello {world} from {here}"));
    }

    public static String resolve2(String input) {
        Map<String, String> keyValueMap = new HashMap<>();
        Matcher matcher = betweenCurlyBracesMatcher.matcher(input);
        while (matcher.find()) {
            String key = matcher.group(1);
            if (!keyValueMap.containsKey(key)) {
                keyValueMap.put(key, computeValueForKey(key));
            }
        }
        for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
            input = input.replace("{" + entry.getKey() + "}", entry.getValue());  // << ugly code here
        }
        return input;
    }

    private static String computeValueForKey(String key) {
        return "new" + key;
    }
}

but I am not happy with

input = input.replace("{" + entry.getKey() + "}", entry.getValue());

because it means whenever i change my regex i will have to update this logic. Is there a more elegant solution to this problem.


input hello {world} from {here}

output hello newworld from newhere


input I want to go to {places} where {things} are happening.

output I want to go to newplaces where newthings are happening.

3
  • What is your actual expected output here? Commented Jan 16, 2020 at 4:38
  • @TimBiegeleisen updated the question. Commented Jan 16, 2020 at 4:40
  • A short suggestion: the Spring ecosystem and other frameworks provide methods for property placeholders that do the same you are achieving with your regex. Commented Jan 16, 2020 at 4:59

2 Answers 2

1

You should make use of the Matcher#appendReplacement and Matcher#appendTail APIs here:

Map<String, String> keyValueMap = new HashMap<>();
keyValueMap.put("places", "to America");
keyValueMap.put("things", "events");
String input = "I want to go to {places} where {things} are happening.";
Pattern pattern = Pattern.compile("\\{(.*?)\\}");
Matcher matcher = pattern.matcher(input);
StringBuffer buffer = new StringBuffer();

while(matcher.find()) {
    matcher.appendReplacement(buffer, keyValueMap.get(matcher.group(1)));
}
matcher.appendTail(buffer);
System.out.println(buffer.toString());

This prints:

I want to go to to America where events are happening.
Sign up to request clarification or add additional context in comments.

Comments

0

We can use matcher.group(0) with returns entire matched string

public class Temp {
    private static final Pattern betweenCurlyBracesMatcher = Pattern.compile("\\{(.*?)\\}");

    public static void main(String args[]) {
        System.out.println(resolve2("hello {world} from {here}"));
    }

    public static String resolve2(String input) {
        Map<String, String> keyValueMap = new HashMap<>();
        Matcher matcher = betweenCurlyBracesMatcher.matcher(input);
        while (matcher.find()) {
            String keyBetweenBraces = matcher.group(1);
            String keyWithBraces = matcher.group(0);
            if (!keyValueMap.containsKey(keyWithBraces)) {
                keyValueMap.put(keyWithBraces, computeValueForKey(keyBetweenBraces));
            }
        }
        for (Map.Entry<String, String> entry : keyValueMap.entrySet()) {
            input = input.replace(entry.getKey(), entry.getValue());
        }
        return input;
    }

    private static String computeValueForKey(String key) {
        return "new" + key;
    }
}

3 Comments

This is not the ideal way to solve your problem. You are iterating the original string once to find all matches, and then you are iterating a second time to make the replacement, for each match. This is N^2 behavior.
yes, i am looking into StrSubstitutor.replace("heelo hi ok {ok}", keyValueMap) from apache to do it in single step.
You don't an Apache library here; core Java will do just fine, see my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.