I have a HashMap<Integer, Double> which looks something similar like this:
{260=223.118,50, 261=1889,00, 262=305,70, 270=308,00}
From database I take a string that could look something like this:
String result = "(260+261)-(262+270)";
I want to change the string's values of 260, 261, 262... (which are always the same with the HashMap's keys) with the values so I could get a string like:
String finRes = "(223.118,50+1889,00)-(305,70+308,00)";
Also the string result can contain multiplication and division characters.
A simple regex solution here would be to match your input string against the pattern (\d+). This should yield all the integers in the arithmetic string. Then, we can lookup each match, converted to an integer, in the map to obtain the corresponding double value. Since the desired output is again a string, we have to convert the double back to a string.
Map<Integer, Double> map = new HashMap<>();
map.put(260, 223.118);
map.put(261, 1889.00);
map.put(262, 305.70);
map.put(270, 308.00);
String input = "(260+261)-(262+270)";
String result = input;
String pattern = "(\\d+)";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(input);
StringBuffer sb = new StringBuffer();
while (m.find()) {
m.appendReplacement(sb, String.valueOf(map.get(Integer.parseInt(m.group(1)))));
}
m.appendTail(sb);
System.out.println(sb.toString());
Output:
(223.118+1889.0)-(305.7+308.0)
Demo here:
Rextester
here is an explained solution:
// your hashmap that contains data
HashMap<Integer,Double> myHashMap = new HashMap<Integer,Double>();
// fill your hashmap with data ..
..
// the string coming from the Database
String result = "(260+261)-(262+270)";
// u will iterate all the keys of your map and replace each key by its value
for(Integer n : myHashMap.keySet()) {
result = result.replace(n,Double.toString(myHashMap.get(n)));
}
// the String variable 'result' will contains the new String
Hope it helps :)
Related
I have a number of large strings looking like this:
"text(24), text_2(5), text_4(822)..."
I'm trying to check if a specific text exists and get the corresponding value.
Is there a quick way to do this?
Edit:
I have an array with all possible text values. At the moment I use foreach to check for text values.
I have the string text_2 and what I need is the corresponding 5 as an integer.
You can use regex to extract all the text element from the String and store them into a map, e.g:
String s = "text(24), text_2(5), text_4(822)";
Pattern pattern = Pattern.compile("([a-zA-Z]*(_)?[0-9]*\\([0-9]+\\))");
Matcher matcher = pattern.matcher(s);
Map<String, Integer> valuesMap = new HashMap<>();
while(matcher.find()){
String[] tokens = matcher.group().split("(?=\\([0-9]+\\),?)");
String key = tokens[0];
Integer value = Integer.parseInt(tokens[1].substring(1, tokens[1].length() - 1));
valuesMap.put(key, value);
}
System.out.println(valuesMap);
Once done, you can call valuesMap.get("test_2"); to get the corresponding value. This is how the above example works:
It splits text into tokens containing <text>(<Value)
It then splits each token again, into text and value and places these into a Map.
Since you need to do this a number of times. I suggest you split the string and build a map from the text to its value, this is O(n). After that, your lookups are only O(1) if you use HashMap.
String text = "text(24), text_2(5), text_4(822)";
Map<String, Integer> map = new HashMap<>();
String[] split = text.split(", ");
for(String s:split){
//search for the position of "(" and ")"
int start = 0;
int end = s.length()-1;
while(s.charAt(start) != '(')
start++;
while(s.charAt(end) != ')')
end--;
//put string and matching value in the map
map.put(s.substring(0, start), Integer.parseInt(s.substring(start+1, end)));
}
System.out.println(map);
I also ran some benchmarks for a string containing 10000 entries. And this approach was about 4 times faster than a regex approach. (38 ms vs 163 ms)
Very new to android java and I have a simple question. I have string for example like this :
P:38,AS:31,DT:231,AR:21
I want to split this into 4 different lists in the form :
P(list) = 38
AS(list) = 31
DT(list) = 231
AR(list) = 21
I tried split but it didnt get the job done ...
As long as the keys are always letters and the values are always integers, you can use regular expressions to parse these strings:
Hashtable<String, int[]> result = new Hashtable<String, int[]>();
Pattern pattern = Pattern.compile("([A-Z]+):(\\d+(?:,\\d+)*)");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
String key = matcher.group(1);
String[] fields = matcher.group(2).split(",");
int[] values = new int[fields.length];
for (int i=0; i<values.length; i++)
values[i] = Integer.parseInt(fields[i]);
result.put(key, values);
}
Edit
"([A-Z]+):(\\d+(?:,\\d+)*)" is a regular expression that matches at least one uppercase letter ([A-Z]+) followed by a colon (:) followed by one more numbers separated by commas (\\d+(?:,\\d+)*). A single number is composed of one more digits (\\d+). The additional parentheses allow us to later access the individual parts of the input string using the group(int) method calls.
The java.util.regex.Matcher class allows us to iterate through the individual parts of the input string that match our regular expression. The find() method returns true as long as there is another substring in our input string that matches the regular expression. So with the input string "P:38,45,AS:31,DT:231,345,678,AR:21" the while loop would execute four times and the matcher variable would point to the following four substrings of the input string:
P:38,45
AS:31
DT:231,345,678
AR:21
We can then use the matcher's group(int) method to access the individual parts of each substring. matcher.group(1) accesses the text that was captured by the first parentheses of our regular expression (([A-Z]+)) which corresponds to "P", "AS", "DT", and "AR" in the individual loop iterations. Analogously, matcher.group(2) corresponds to the second parentheses of the regular expression ((\\d+(?:,\\d+)*)) which would return "38,45", "31", "231,345,678", and "21". So in the first iteration of the while loop key would hold "P" and fields would hold an array of strings ["38", "45"]. We can then parse the fields into actual integer values using Integer.parseInt(String) and store the key and the values in a Hashtable so that we can later retrieve the values for the individual keys. For example, result.get("DT") would return an array of integers with the values [231, 345, 678].
As #pobybolek said, you can use his method which he wrote to take the string and convert it into a hashtable, which uses the key and then the int values after it. The int values are stored in an array and the key is a string.
String input = master_string;
Hashtable<String, int[]> result = new Hashtable<String, int[]>();
Pattern pattern = Pattern.compile("([A-Z]+):(\\d+(?:,\\d+)*)");
Matcher matcher = pattern.matcher(input);
while (matcher.find())
{
String key = matcher.group(1);
String[] fields = matcher.group(2).split(",");
int[] values = new int[fields.length];
for (int pqr=0; pqr<values.length; pqr++)
{
values[pqr] = Integer.parseInt(fields[pqr]);
// values[pqr] = fields[pqr];
}
result.put(key, values);
}
the above code splits the given string into its keys and the values after the key into an integer array, this can also be changed into a String key, String[] array as seen in the second line of my code.
I have a search String which contains the format below:
Search String
111651311
111651303
4111650024
4360280062
20167400
It needs to be matched with sequence of numbers below
001111651311000
001111651303000
054111650024000
054360280062000
201674000000000
Please note the search strings have been added with additional numbers either on each sides.
I have tried the regex below in java to match the search strings but it only works for some.
Pattern pattern = Pattern.compile("([0-9])\1*"+c4MIDVal+"([0-9])\1*");
Any advice ?
Update
Added the code I used below might provide some clarity on what am trying to do
Code Snippet
public void compare(String fileNameAdded, String fileNameToBeAdded){
List<String> midListAdded = readMID.readMIDAdded(fileNameAdded);
HashMap<String, String> midPairsToBeAdded = readMID.readMIDToBeAdded(fileNameToBeAdded);
List <String []> midCaptured = new ArrayList<String[]>();
for (Map.Entry<String, String> entry: midPairsToBeAdded.entrySet()){
String c4StoreKey = entry.getKey();
String c4MIDVal = entry.getValue();
Pattern pattern = Pattern.compile("([0-9]?)\\1*"+c4MIDVal+"([0-9]?)\\2*");
for (String mid : midListAdded){
Matcher match = pattern.matcher(mid);
// logger.info("Match Configured MID :: "+ mid+ " with Pattern "+"\\*"+match.toString()+"\\*");
if (match.find()){
midCaptured.add(new String []{ c4StoreKey +"-"+c4MIDVal, mid});
}
}
}
logger.info(midCaptured.size()+ " List of Configured MIDs ");
for (String [] entry: midCaptured){
logger.info(entry[0]+ "- "+entry[1] );
}
}
You need to refer the second capturing group in the second part and also you need to make both the patterns inside the capturing group as optional.
Pattern pattern = Pattern.compile("([0-9]?)\\1*"+c4MIDVal+"([0-9]?)\\2*");
DEMO
What is the problem by using the String.contains() method?
"001111651311000".contains("111651311"); // true
"201674000000000".contains("111651311"); // false
In my below code, I am making a json string using gson:
private String generateData(Map<String, Map<Integer, Set<Integer>>> nodeTable, int i) {
JsonObject jsonObject = new JsonObject();
Set<Integer> pp = nodeTable.get("TEXTER").get(i);
Set<Integer> sp = nodeTable.get("PETER").get(i);
// my above pp and sp variables shows correct values with one space between numbers.
jsonObject.addProperty("description", "Hello. World");
jsonObject.add("data1", gson.toJsonTree(pp));
jsonObject.add("data2", gson.toJsonTree(sp));
System.out.println(jsonObject.toString());
return jsonObject.toString();
}
When I get my json string, I get it like this. As you can see after comma everything is next to each other without any spaces. I don't want like that.
{"description":"Hello. World.","data1":[0,273,546,819,1092,559],"data2":[816,1644,1368,276]}
I want to have one space between numbers in data1 and data2 variable as shown below:
{"description":"Hello. World.","data1":[0, 273, 546, 819, 1092, 559],"data2":[816, 1644, 1368, 276]}
Is this possible to do by any chance using gson or some other way?
This seems rather a cosmetic request with potential for bugs. Easiest way is probably using a regex:
jsonString = jsonString
.replaceAll("(\\d),(\\d)", "$1, $2")
.replaceAll("(\\d),(\\d)", "$1, $2")
The line above simply catches any digit + comma + digit sequence and adds a space between the two captured and restored digits. Since java catches matches only once, thus not allowing intersections, we're doing it two times here.
Alternative that is not so strict but doesn't require double replacing:
jsonString = jsonString.replaceAll(",(\\d)", ", $1")
The bug potential here is that this applies to the whole JSON string, not only encoded sets.
The right way™ would be to use a custom set formatter for GSON.
BTW, a map of maps cries for classes.
The easiest way to get space by using following methods like below
For 1:
jsonObject.add("data1", gson.toJsonTree(addSpaces(pp)));
jsonObject.add("data2", gson.toJsonTree(addSpaces(sp)))
For 2:
String string = addSpaceByRegEx(jsonObject.toString());
Add following methods in you class :
//1. if you need String value based spaces in json string
public Set<String> addSpaces(Set<Integer> integers) {
Set<String> strings = new HashSet<>();
for (Integer i : integers) {
strings.add(i.toString() + " ");
}
return strings;
}
//2. if you need space as in integer value in json string as your example
//This method add space by using regex
//Tested for String s = "{'description':'Hello. World.','data1':[0,273,546,819,1092,559],'data2':[816,1644,1368,276]}";
//In you example just replace following line
//System.out.println(jsonObject.toString()); by System.out.println(addSpaceByRegEx(jsonObject.toString()));
//See result, i think it work
public String addSpaceByRegEx(String jsonString) {
Pattern pattern = Pattern.compile(",[0-9]");
Matcher matcher = pattern.matcher(jsonString);
StringBuilder sb = new StringBuilder();
int prevIndex = 0;
while (matcher.find()) {
int startIndex = matcher.start();
int endIndex = matcher.end();
sb.append(jsonString.substring(prevIndex, startIndex + 1)).append(" ").append(jsonString.substring(startIndex + 1, endIndex));
prevIndex = endIndex;
}
sb.append(jsonString.substring(prevIndex, jsonString.length()));
return sb.toString();
}
My requirement is that I have a given string like
String originalString = "delhi to goa";
And I have a string
String regStr = "%{orgCity} to %{destCity}";
The given text can be in any format. (Not just "delhi to goa", it can be "delhi goa to")
Now, given these two strings, I want to get a HashMap with key value pair as
orgCity -> delhi
destCity -> goa
Here key is the string between %{ and } . And value is the corresponding string in the originalString.
This needs to be implemented using some regex / pattern.
I could not find the solution for this.
Can someone help on this??
Thanks
Updated
Solution:
public static void main(String[] args) {
// Original requirement
System.out.println(getValueMap("%{orgCity} to %{destCity}", "delhi to goa"));
// A variation with two words values
System.out.println(getValueMap("%{orgCity} to %{destCity}", "New York to Mexico"));
// Another variation
System.out.println(getValueMap("%{orgCity} to %{destCity} and more", "delhi to goa and more"));
// order of words doesn't matter
System.out.println(getValueMap("%{orgCity} %{destCity} to", "delhi goa to"));
// different strings than the original requirement
System.out.println(getValueMap("I'm going to %{firstCity} and then to %{secondCity}", "I'm going to Nauru and then to Seattle"));
// more than two values, with more than one word
System.out.println(getValueMap("I am %{age} years old, I have %{eyesColour} eyes and %{pocketContent} in my pocket",
"I am 20 years old, I have dark blue eyes and two coins in my pocket"));
// etc ...
}
public static Map<String, String> getValueMap(String format, String text) {
Map<String, String> map = new HashMap<String, String>();
String pattern = format;
String[] keyList = StringUtils.substringsBetween(format, "%{", "}");
for (String str : keyList) {
pattern = pattern.replaceAll("\\%\\{" + str + "\\}", ("(.+)"));
}
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(text);
if(!m.find()) {
throw new RuntimeException("regStr and originalString don't match");
}
for (int i = 0; i < m.groupCount(); i++) {
map.put(keyList[i], m.group(i+1));
}
return map;
}
I know I shouldn't do your work for you, but that exercise you propose was so interesting to me that I just couldn't resist:
This method receives your two strings and gives you back a Map (ok, just change the return type if you REALLY want a HashMap instead):
public static Map<String, String> getMap(String regStr, String originalString) {
Pattern searchPattern = Pattern.compile("%\\{([^}]+)\\}");
Matcher matcher = searchPattern.matcher(regStr);
StringBuilder builder = new StringBuilder();
List<String> keys = new LinkedList<>();
Map<String, String> map = new HashMap<>();
int start = 0;
while(matcher.find()) {
builder.append(Pattern.quote(regStr.substring(start, matcher.start())))
.append("(.+)");
start = matcher.end();
keys.add(matcher.group(1));
}
builder.append(Pattern.quote(regStr.substring(start)));
Pattern finalPattern = Pattern.compile(builder.toString());
matcher = finalPattern.matcher(originalString);
int pos = 0;
if(!matcher.find()) {
throw new RuntimeException("regStr and originalString don't match");
}
for(String key: keys) {
map.put(key, matcher.group(++pos));
}
return map;
}
Some light testing:
public static void main(String[] args) {
// Original requirement
System.out.println(getMap("%{orgCity} to %{destCity}", "delhi to goa"));
// A variation with two words values
System.out.println(getMap("%{orgCity} to %{destCity}", "New York to Mexico"));
// Another variation
System.out.println(getMap("%{orgCity} to %{destCity} and more", "delhi to goa and more"));
// order of words doesn't matter
System.out.println(getMap("%{orgCity} %{destCity} to", "delhi goa to"));
// different strings than the original requirement
System.out.println(getMap("I'm going to %{firstCity} and then to %{secondCity}", "I'm going to Nauru and then to Seattle"));
// more than two values, with more than one word
System.out.println(getMap("I am %{age} years old, I have %{eyesColour} eyes and %{pocketContent} in my pocket",
"I am 20 years old, I have dark blue eyes and two coins in my pocket"));
// etc ...
}
Time to practice your regex skills http://www.regexr.com/
Create two capture groups and regex each string.
If it is always going to be in the format: someCity to someCity you can use the String.split method.
String input = "delhi to goa";
String[] arr = input.split(" to ");
Map<String, String> map = new HashMap<>();
map.put(arr[0], arr[1]);
You can use regex (\w+)\sto\s(\w+) and can find two groups.
Group 1 for key and Group 2 for value.
You can test this regular expression on http://rubular.com/