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/
Related
I have given a string "my1kiran4name2is3" and my expected output is "my name is kiran"
Explanation1
my - 1
kiran - 4
name - 2
is - 3
I have to arrange the words based on the numbers.
the string only contains numbers from 1 to 9.
So my output is "my name is kiran"
been trying to solve this problem from past two days but not finding any way just started learning java, any kind of help would be appreciated.
As a heads-up that as a reviewer, I wouldn't approve the next code because it's hard to read and maintain.
But in case there is someone like me and likes regexes and streams:
String string = "my1kiran4name2is3";
Map<Integer, String> map =
Arrays.asList(string
.split("(?<=\\d)"))
.stream()
.map(s -> s.split("(?=\\d)"))
.collect(Collectors.toMap((e -> Integer.parseInt(e[1])), e -> e[0]));
string = map
.values()
.stream()
.collect((Collectors.joining(" ")));
System.out.println(string);
A little explanation:
(?<=\d) is positive lookbehind. We split the String if the value before the match is a digit. So as a result we have (my1, kiran4, name2, is3)
(?=\d) is positive lookahead we map the String if the Value ahead is a number. So as a result we have (my 1, kiran 4, name 2, is 3)
Another solution, just for fun:
Pattern re = Pattern.compile("([^0-9]+)([0-9]+)");
String input = "my1kiran4name2is3";
Map<Integer,String> words = new TreeMap<>();
Matcher matcher = re.matcher(input);
while (matcher.find()) {
words.put(Integer.valueOf(matcher.group(2)), matcher.group(1));
}
String output = String.join(" ", words.values());
System.out.println(output);
You can use StringBuilder and Map to solve your problem:
String str = "my1kiran4name2is3";
Map<Integer, String> map = new HashMap<>();
StringBuilder key = new StringBuilder();
StringBuilder value = new StringBuilder();
for (char c : str.toCharArray()) {
if (Character.isDigit(c)) {
key.append(c);
} else {
if (key.length() > 0) {
map.put(Integer.valueOf(key.toString()), value.toString());
key.setLength(0);
value.setLength(0);
}
value.append(c);
}
}
map.put(Integer.valueOf(key.toString()), value.toString());
String response = String.join(" ", map.values()); // my name is kiran
The idea here is to loop over all the characters and check if digit or not, then in the end store the number as a key in the map and the string as a value.
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 :)
I Have Java program that invokes a Python script and takes the result which is a Python dictionary as a string. A result would be something like:
{'27': [], '10864': [u'8344', u'7769', u'7207', u'3735']}
I want to parse the result into two String tables the keys and the values as returned. In this example I want:
String[] keys >>>> ["27",'10864'] and
String[] values >>>> ["[]","[u'8344', u'7769', u'7207', u'3735']"]
I am trying to do it with regular expressions but I don't have much success. Can anyone give an efficient solution?
You can see the regex I have used here on regex101.com.
Essentially the Matcher will run through your input and detect all matches of the regex. Then you use the .find method in a while loop to go through each match individually and extract the information.
You can use a regular array however in my opinion using an ArrayList is way more efficient if you are not sure of the size of the incoming input.
This code works for me:
public static void main(String[] args)
{
String input = "{'27': [], '10864': [u'8344', u'7769', u'7207', u'3735']}";
String pattern = "('([\w]*)': (\\[[u'0-9, ]*])?)";
List<String> keys = new ArrayList<>();
List<String> values = new ArrayList<>();
//Find multiple matches in your input.
Matcher m = Pattern.compile(pattern).matcher(input);
//Goes through each match individually.
while (m.find())
{
keys.add(m.group(2));
values.add(m.group(3));
}
//To display your keys and values.
System.out.println("Keys: " + keys.toString());
System.out.println("Values: " + values.toString());
}
Since you want to use regexes, here is one approach:
import java.util.*;
class Main {
public static void main(String[] args) {
String strExample = "{'27': [], '10864': [u'8344', u'7769', u'7207', u'3735']}";
// Initialize
List<String> keys = new ArrayList<String>();
List<String> values = new ArrayList<String>();
// Hacky regex parsing -- happy now???
for (String component : strExample.replace("{","").split("][,}]")) {
String kv[] = component.split(": ");
keys.add(kv[0]);
values.add(kv[1] + "]");
}
// Print
System.out.println("Keys: ");
for (String key : keys) {
System.out.println(key);
}
System.out.println("\nValues: ");
for (String value : values) {
System.out.println(value);
}
}
}
This first splits on the closing bracket ] and then the colon :
The output is as you desired (run in repl):
Keys:
'27'
'10864'
Values:
[]
[u'8344', u'7769', u'7207', u'3735']
Note, you really should not:
use Strings for numeric keys
use regexes to parse JSONs
use anything like this code in a real system
How should I get the total no. of substrings in a string.
For all substrings in a string.
Ex:
str="This is this my book is This"
O/p should like below:
This-3
Is=2
my=1
book=1
If I understood you correctly this is a solution for your problem:
String str="This is this my book is This";
Map<String, Integer> counts = new HashMap<String, Integer>();
String[] words = str.toLowerCase().split("[\\s\\.,;!\\?]");
for (String word: words) {
int count = counts.containsKey(word) ? counts.get(word).intValue() : 0;
counts.put(word, Integer.valueOf(count + 1));
}
You just split the string by the delimiters you want to consider and collect the occurrences in a map.
If I'm right you want to search for the occurrences of all words, not all possible substrings. A very small, easy to understand, code would be the following:
// Split at space
String[] words = input.split(" ");
HashMap<String, Integer> countingMap = new HashMap<>();
for (String word : words) {
Integer counter = countingMap.get(word);
if (counter == null)) {
counter = 0;
}
countingMap.put(word, counter + 1);
}
However, this approach is limited as it assumes each word is surrounded by a space.
Regex is a more powerful tool, it provides a special character for a word boundary (this also matches ,.!? and so on). Consider the following Pattern:
\b(.+?)\b
You can see an example here: regex101.com/r/hO8kA0/1
How to do this in Java?
Pattern pattern = Pattern.compile("\\b(.+?)\\b");
Matcher matcher = pattern.matcher(input);
while(matcher.find()) {
String word = matcher.group(1);
// Here is your word, count the occurrences like above
}
String str="This is this my book is This";
String[] words = str.split(" ");
Map<String,Integer> unitwords = new HashMap<String,Integer>;
for(String word: words){
if(unitwords.containsKey(word)){
unitwords[word]++;
}else{
unitwords.add(word,1);
}
And print the map unitwords.
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