Is there a pre-written method to replace dollar-sign name variables in a string with a predefined constant?
For example, the following code :
Map<String, Object> myVars = new TreeMap<String, Object>();
String str = "The current year is ${currentYear}.";
myVars.put("currentYear", "2014");
System.out.println(Replacer.replaceVars(str, myVars));
... would have this output:
The current year is 2014.
Spring does this too if you need to support more advanced use cases. I was able to utilize code from the following class for my use cases. See the parseStringValue method.
https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/util/PropertyPlaceholderHelper.java
In your case, you need to pass in a PlaceholderResolver that uses your Map to resolve the placeholder.
Have a look at the MessageFormat class. Your example would look like this:
int currentYear = 2014;
String str = "The current year is {0}.";
str = MessageFormat.format(str, currentYear);
This is probably the best version, but you could always use a regex as well:
public String format (String input, Map<String, String> replacement)
{
for (String key : replacement.keySet())
input = input.replaceAll("\\${"+replacement+"}", replacement.get(key));
return input;
}
I am not aware of any standard Java class which would support this behaviour but writing your tool wouldn't be so hard. Here is example of such solution:
class Replacer {
private static Pattern pattern = Pattern.compile("\\$\\{(?<key>[^}]*)\\}");
public static String replaceVars(String format, Map<String, ?> map) {
StringBuffer sb = new StringBuffer();
Matcher m = pattern.matcher(format);
while (m.find()) {
String key = m.group("key");
if (map.containsKey(key)) {//replace if founded key exists in map
m.appendReplacement(sb, map.get(key).toString());
} else {//do not replace, or to be precise replace with same value
m.appendReplacement(sb, m.group());
}
}
m.appendTail(sb);
return sb.toString();
}
}
We could take advantage of default method getOrDefault introduced in Java 8 to Map interface and replace
while (m.find()) {
String key = m.group("key");
if (map.containsKey(key)) {//replace if founded key exists in map
m.appendReplacement(sb, map.get(key).toString());
} else {//do not replace, or to be precise replace with same value
m.appendReplacement(sb, m.group());
}
}
m.appendTail(sb);
with
while (m.find())
m.appendReplacement(sb, map.getOrDefault(m.group("key"), m.group()));
m.appendTail(sb);
But to be able to use this method we first would need to specify type of value in map - in other words we would need to change type of accepted Map from Map<String, ?> map to type like Map<String, String> map.
Related
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
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
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+
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(""));
Let's say we have a string: string text = "Hello my name is $$name; and my surname is $$surname;"
It references 2 variables, identified by double dollar sign: name and surname
We also have a hashmap:
HashMap<String, String> variables = new HashMap<String, String>();
variables.put("name", "John");
variables.put("surname", "Doe");
How do I replace/interpolate variables in a string with their matched Regex values as hashmap keys? (perhaps there's no need to use Regex and Java has this implementation)
In the end, I want variable string text to equal "Hello my name is John and my surname is Doe"
EDIT: What I meant is replacing the key/variable with the hashmap value without knowing the key. For example, we have a string and we must replace all $$variable; values inside in with the map[variable].
What would be the fastest way of replacing this string?
You would have to use some regex, parse the input and lookup every key in the Map, like that:
import java.util.Map;
import java.util.regex.Pattern;
public class T2 {
/** Pattern for the variables syntax */
public static final Pattern PATTERN = Pattern.compile("\\$\\$([a-zA-Z]+);");
public static void main(String[] args) {
String s = "Hello my name is $$name; and my surname is $$surname;";
Map<String, String> variables = Map.of("name", "John", "surname", "Doe");
String ret = replaceVariables(s, variables);
System.out.println(ret);
}
private static String replaceVariables(final CharSequence s, final Map<? super String, String> variables) {
return PATTERN.matcher(s).replaceAll(mr -> variables.getOrDefault(mr.group(1), ""));
}
}
Output:
Hello my name is John and my surname is Doe
You can iterate over variables and replace the matched keys with the corresponding values without knowing the key using a foreach loop.
for(Map.Entry<String, String> e: variables.entrySet()) {
text = text.replace("$$"+e.getKey(), e.getValue());
}
Try this:
String text = "Hello my name is $$name; and my surname is $$surname;";
Map<String, String> variables = new HashMap<>();
variables.put("name", "John");
variables.put("surname", "Doe");
for(Map.Entry<String, String> e: variables.entrySet()) {
text = text.replace("$$"+e.getKey(), e.getValue());
}
System.out.println(text);
Output:
Hello my name is John; and my surname is Doe;
I am trying to replace the short form words to normal from a string in java but don't know how to do it in a good way because I can have multiple ('ve 're 'nt) and such. Is it good to use array list and if so how do I achieve that?
What I have tried so far:
public class main {
public static void main(String[] args) {
String s = "We've been doing this for ages. I'm having a difficulty doing this. Thats getting confusing.";
s = s.replaceAll("we've", "we have");
s = s.replaceAll("I'm", "I am");
s = s.replaceAll("that's", "that is");
}
}
Thanks!
You can do it more efficiently using regexes.
First, build a map containing your searches and replacements.
Map<String, String> replacements =
Map.of("we've", "we have", "I'm", "I am" /* etc */);
(or some pre-Java 9 equivalent)
Now, build a regex to match the things you want to replace:
Pattern p = Pattern.compile(
replacements.keySet()
.stream()
.map(Pattern::quote)
.collect(Collectors.joining("|")));
Now, create a Matcher and a StringBuilder in which to accumulate your new string:
Matcher m = p.matcher(s);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String replacement = replacements.get(m.group(0));
m.appendReplacement(sb, replacement);
}
m.appendTail(sb);
String newS = sb.toString();
Ideone demo
You could use a Map<String, String>, for example a HashMap where the keys would be the short form to replace and the value the replacement string. Then you could just iterate Map.entrySet() and call the replace method on the string.
Code could be (please note that I have omitted the initial letter - except for I which has to be upper case - to avoid the capitalized problem):
String str = s;
HashMap<String, String> replacements = new HashMap<>();
replacements.put("e've", "e have");
replacements.put("I'm", "I am");
replacements.put("hat's", "hat is");
for (Map.Entry<String, String> entry: replacements.entrySet()) {
str = str.replaceAll(entry.getKey(), entry.getValue());
}
It does not really make sense if it is intended to be used only once, but it could be the base of a method that could be re-used on many strings.
You can use a StringBuilder if you don't want to keep creating new strings all the time:
StringBuilder builder = new StringBuilder("We've been doing this for ages. I'm having a difficulty doing this. That's getting confusing.");
HashMap<String, String> replacements = new HashMap<>();
replacements.put("'ve", " have");
replacements.put("'m", " am");
replacements.put("'s", " is");
// others...
for (Map.Entry<String, String> entry: replacements.entrySet()) {
int index;
while ((index = builder.indexOf(entry.getKey())) != -1) {
builder.replace(index, index + entry.getKey().length(), entry.getValue());
}
}
System.out.println(builder);
Do note that if you are trying to replace all contractions like this, you are unlikely going to succeed 100% as some phrases contract to the same contraction, for example:
That has -> That's
That is -> That's
Also note that some 's don't indicate a contraction:
Mary's <-- how are you handling this?
You can kind of tackle the second problem by looking for more specific sequences like That's instead of just 's, but for the first problem, you will need to somehow understand the context.
I have a Treemap:
TreeMap<String, Integer> map = new TreeMap<String, Integer>();
It counts words that are put in, for example if I insert:
"Hi hello hi"
It prints:
{Hi=2, Hello=1}
I want to replace that "," with a "\n", but I did not understand the methods in Java library. Is this possible to do? And is it possible to convert the whole TreeMap to a String?
When printing the map to the System.out is uses the map's toString function to print the map to the console.
You could either string replace the comma with a newline like this:
String stringRepresentation = map.toString().replace(", ", "\n");
This might however poses problems when your key in the map contains commas.
Or you could create a function to produce the desired string format:
public String mapToMyString(Map<String, Integer> map) {
StringBuilder builder = new StringBuilder("{");
for (Map.Entry<String, Integer> entry : map.entrySet()) {
builder.append(entry.getKey()).append('=').append(entry.getValue()).append('\n');
}
builder.append('}');
return builder.toString();
}
String stringRepresentation = mapToMyString(map);
Guava has a lot of useful methods. Look at Joiner.MapJoiner
Joiner.MapJoiner joiner = Joiner.on('\n').withKeyValueSeparator("=");
System.out.println(joiner.join(map));
String input data is
{phone=333-333-3333, pr_specialist_email=null, sic_code=2391, status=ACTIVE, address1=E.BALL Drive, fax=333-888-3315, naics_code=325220, client_id=862222, bus_name=ENTERPRISES, address2=null, contact=BYRON BUEGE}
Key and values will increase in the array.
I want to get the value for each key ie myString.get("phone") should return 333-333-3333
I am using Java 1.7, is there any tools I can use this to parse the data and get the values.
Some of my input is having values like,
{phone=000000002,Desc="Light PROPERTITES, LCC", Address1="C/O ABC RICHARD",Address2="6508 THOUSAND OAKS BLVD.,",Adress3="SUITE 1120",city=MEMPHIS,state=TN,name=,dob=,DNE=,}
Comma separator doesn't work here
Here is a simple function that will do exacly what you want. It takes your string as an input and returns a Hashmap containing all the keys and values.
private HashMap<String, String> getKeyValueMap(String str) {
// Trim the curly ({}) brackets
str = str.trim().substring(1, str.length() - 1);
// Split all the key-values tuples
String[] split = str.split(",");
String[] keyValue;
HashMap<String, String> map = new HashMap<String, String>();
for (String tuple : split) {
// Seperate the key from the value and put them in the HashMap
keyValue = tuple.split("=");
map.put(keyValue[0].trim(), keyValue[1].trim());
}
// Return the HashMap with all the key-value combinations
return map;
}
Note: This will not work if there's ever a '=' or ',' character in any of the key names or values.
To get any value, all you have to do is:
HashMap<String, String> map = getKeyValueMap(...);
String value = map.get(key);
You can write a simple parser yourself. I'll exclude error checking in this code for brevity.
You should first remove the { and } characters, then split by ', ' and split each resulting string by =. At last add the results into a map.
String input = ...;
Map<String, String> map = new HashMap<>();
input = input.substring(1, input.length() - 1);
String elements[] = input.split(", ");
for(String elem : elements)
{
String values[] = elem.split("=");
map.put(values[0].trim(), values[1].trim());
}
Then, to retrieve a value, just do
String value = map.get("YOURKEY");
You can use "Google Core Libraries for Java API" MapSplitter to do your job.
First remove the curly braces using substring method and use the below code to do your job.
Map<String, String> splitKeyValues = Splitter.on(",")
.omitEmptyStrings()
.trimResults()
.withKeyValueSeparator("=")
.split(stringToSplit);