I'm working on a fraction calculator using String.split() to get the terms split. The inputs are separated by spaces( 1/2 / 1/2)
String[] toReturn = new String[6];
result = isInputValid(expression);
toReturn = splitExpression(expression, placeToSplit[0]);
int indexOfUnderscore = toReturn[0].indexOf("_");
result = isInputValid(toReturn[0]);
if(toReturn[5] != null){
getOperator2(toReturn);
}
The error is in the if statement. toReturn[5] is out of bounds, because when two terms or less were answered split expression, which uses String.split() to split it at the spaces, doesn't create toReturn[5], even when I set values to toReturn[5]. If there is a way to tell if a field in an array exists, that could solve it, or if there is a way to tell how many terms are being put in. My program works for 1/2 + 1/2 * 1/2, but I haven't figured out how to tell if toReturn[5] exists.
Correctly:
result = isInputValid(expression);
String[] toReturn = splitExpression(expression, placeToSplit[0]);
int indexOfUnderscore = toReturn[0].indexOf("_");
result = isInputValid(toReturn[0]);
if(toReturn.length>5 && !"".equals(toReturn[5]) ){
getOperator2(toReturn);
}
the toReturn.length>5 part verifies that the array itself is at least 6 items long. Then you can check if that element is empty or not...
This is what it should be like.
Remove first line , String[] toReturn = new String[6];
update your third line,
String[] toReturn = splitExpression(expression, placeToSplit[0]);
And check this condition:
if(toReturn.length>5 ){ // use !toReturn[5].isEmpty() to check the empty string
getOperator2(toReturn);
}
Related
I'm trying to develop a function that reads an ArrayList of string and is capable to find if there exist at least two tuples that have the same values from a set of indices but differ for a supplementary index. I've developed a version of this function by using a RegEx comparison as follow:
public boolean checkMatching(){
ArrayList<String> rows = new ArrayList<String>();
rows.add("7,2,2,1,1");
rows.add("7,3,2,1,1");
rows.add("7,8,1,1,1");
rows.add("8,2,1,3,1");
rows.add("8,2,1,4,1");
rows.add("8,4,5,1,1");
int[] indices = new int[] {2,3};
int supplementaryIndex = 1;
String regex = "";
for(String r : rows){
String[] rt = r.split(",");
regex = "[a-zA-Z0-9,-.]*[,][a-zA-Z0-9,-.]*[,][" + rt[indices[0]] + "][,][" + rt[indices[1]] + "][,][a-zA-Z0-9,-.]*";
for(String r2 : rows){
if(r.equals(r2) == false){
if(Pattern.matches(regex, r2)){
String[] rt2 = r.split(",");
if(rt[supplementaryIndex].equals(rt2[supplementaryIndex]) == false){
return true;
}
}
}
}
}
return false;
}
However, it is very expensive, especially if there are many rows. I've thought to create a more complex RegEx that considers multiple choices (with '|' condition), as follow:
public boolean checkMatching(){
ArrayList<String> rows = new ArrayList<String>();
rows.add("7,2,2,1,1");
rows.add("7,3,2,1,1");
rows.add("7,8,1,1,1");
rows.add("8,2,1,3,1");
rows.add("8,2,1,4,1");
rows.add("8,4,5,1,1");
int[] indices = new int[] {2,3};
int supplementaryIndex = 1;
String regex = "";
for(String r : rows){
String[] rt = r.split(",");
regex += "[a-zA-Z0-9,-.]*[,][a-zA-Z0-9,-.]*[,][" + rt[indices[0]] + "][,][" + rt[indices[1]] + "][,][a-zA-Z0-9,-.]*";
regex += "|"; //or
}
for(String r2 : rows){
if(Pattern.matches(regex, r2)){
//String rt2 = r.split(",");
//if(rt[supplementaryIndex].equals(rt2[supplementaryIndex]) == false){
return true;
//}
}
}
return false;
}
But the problem is that this way I can't compare the supplementary index values. Do you have any suggestions on how to define a regex that can directly satisfy this condition? Or, is it possible to leverage java streams to do this efficiently?
The main problem of your first approach is that you have two nested loops over the same list, which gets you a quadratic time complexity. To recall, that implies that the inner loop’s body gets executed 10,000 times for a list with 100 elements and 1,000,000 times for a list of 1,000 elements, and so on.
It doesn’t help calling Pattern.matches(regex, r2) in the inner loop’s body. That method exist only to support (as delegation target) the String operation r2.matches(r2), a convenience method, to do Pattern.compile(regex).matcher(input).matches() in one go. If you have to apply the same regex multiple times, you should keep and re-use the result of Pattern.compile(regex).
But here, there is no point in using a regex at all. You have already decomposed the string using split and can access each component via a plain array access. Using this starting point to compose a regex to be applied on the string again, is complicated and expensive at the same time.
Just use something like
// return true when at least one string has the same values for indices
// but different value for supplementaryIndex
Map<List<String>,String> map = new HashMap<>();
for(String r : rows) {
String[] rt = r.split(",");
List<String> key = List.of(rt[indices[0]], rt[indices[1]]);
String old = map.putIfAbsent(key, rt[supplementaryIndex]);
if(old != null && !old.equals(rt[supplementaryIndex])) return true;
}
return false;
This loops over the list a single time, extracts the key elements from the array and composes a key for a HashMap. There are various ways to do this. But while it’s tempting to just concatenate these elements like rt[indices[0]] + "," + rt[indices[1]], which would work, using a List is preferable, as it avoids expensive string concatenation.
The code puts the value to check into the map which will return a previous value if this key has been encountered before. If so, the old and new values can be compared and the method can return immediately if they don’t match.
When you are using Java 8, you have to use Arrays.asList(rt[indices[0]], rt[indices[1]]) instead of List.of(rt[indices[0]], rt[indices[1]]).
This can be easily expanded to support variable lengths for indices, by changing
List<String> key = List.of(rt[indices[0]], rt[indices[1]]);
to
List<String> key = Arrays.stream(indices).mapToObj(i -> rt[i]).toList();
or, if you are using a Java version older than 16:
List<String> key
= Arrays.stream(indices).mapToObj(i -> rt[i]).collect(Collectors.toList());
Given the following sets of strings:
are yo
you u
how nhoware
alan arala
dear de
I need to find a sequence that can be constructed by concatenating the strings in either columnm, and it must use the same number of elements in both cases.
For example, "dearalanhowareyou" can be constructed from both sets of strings, using 5 elements each time.
A invalid choice would be "dearalanhoware" since it would use 4 elements from the left column but only 3 from the right
The problem is taken from here:
https://open.kattis.com/problems/correspondence
I'm using this site to improve for future job interviews and I just can't seem to figure this one out at all.
My only working implementation is a brute force approach taking every possible combination of each set which is not a very good solution due to time complexity.
My code right now:
list1 = getPermutations("",send1);
list2 = getPermutations("",send2);
ArrayList<String> duplicateValues = new ArrayList<String>();
for (int i = 0; i < list1.size(); i++) {
if (list2.contains(list1.get(i))) {
duplicateValues.add(list1.get(i));
}
private static ArrayList<String> getPermutations(String currentResult, ArrayList<String> possibleChars) {
ArrayList<String> result = new ArrayList<>(possibleChars.size());
for (String append : possibleChars) {
String permutation = currentResult + append;
result.add(permutation);
if (possibleChars.size() > 0) {
ArrayList<String> possibleCharsUpdated = (ArrayList) possibleChars.clone();
possibleCharsUpdated.remove(new String(append));
result.addAll(getPermutations(permutation, possibleCharsUpdated));
}
}
return result;
}
You can significantly narrow down the amount of permutations that you need to check by finding which words from each set could possibly begin the constructed String. In this case, the only two choices are dear and de because de is a substring of dear. Once you get the String started you can take a substring of the longer String, in this case "dear".substring("de".length()) returns ar which tells you that the next element from the right side needs to start with ar. So basically you have two cases :
String stringLeft = "", stringRight = "";
if(stringLeft.length() == stringRight.length())
{
//find two matching Strings here (one is substring of another)
String[] matching = getMatching(); //returns 1d array of size 2(if only two strings match)
stringLeft += matching[0];
stringRight += matching[1];
}
else
{
if(stringLeft.length() > stringRight.length())
{
String start = stringLeft.substring(stringRight.length());
//find string on right that starts with start
stringRight += getStringStartingWith(start);
}
else
{
String start = stringRight.substring(stringLeft.length());
//find string on left that starts with start
stringLeft += getStringStartingWith(start);
}
}
The only thing you need to look out for is if there are multiple matching Strings, or Strings starting with the substring you're looking for.
I have 3 String fields per line within my text file. There are 4 lines in total. The first 2 fields (field[0] and field[1]) are already filled in but field 3 (field[2]) is yet to be generated so it shall remain empty. Is there any way I can read in this text file line by line without getting a java.lang.ArrayIndexOutOfBoundsException: 1 error? I have included my code used for reading in the file.
import java.io.*;
public class PassGen {
public static void main(String args[]) throws Exception{
BufferedReader inKb = new BufferedReader(new InputStreamReader(System.in));
BufferedReader inF = new BufferedReader(new FileReader(new File("students.txt")));
String line = inF.readLine();
int cnt = 0;
Student pupil[] = new Student[6];
while(line != null) {
String field[] = line.split("//s");
pupil[cnt] = new Student(field[0], field[1], field[2]);
cnt++;
inF.readLine();
}
}
}
You can simply add a check on the number of fields:
if(field.length > 2) {
pupil[cnt] = new Student(field[0], field[1], field[2]);
} else {
pupil[cnt] = new Student(field[0], field[1], null);
}
Alternatively, you can use the overloaded split method that takes a limit parameter and set that to -1 to include the empty field. From the documentation of String#split(String regex, int limit):
The limit parameter controls the number of times the pattern is applied and therefore affects the length of the resulting array. If the limit n is greater than zero then the pattern will be applied at most n - 1 times, the array's length will be no greater than n, and the array's last entry will contain all input beyond the last matched delimiter. If n is non-positive then the pattern will be applied as many times as possible and the array can have any length. If n is zero then the pattern will be applied as many times as possible, the array can have any length, and trailing empty strings will be discarded.
Note that you need to use \\s instead of //s for the whitespace regex (this needs to be corrected either way).
String field[] = line.split("\\s", -1);
I think you problem lies in the way you are managing your data, but you can have something like this to read from any array and not getting any exceptions:
public static String getIfExists(final String[] values, final int position) {
return (values != null) && (values.length > position) ? values[position] : null;
}
Then you can fill every field like new Student(getIfExists(field, 0), getIfExists(field, 1), getIfExists(field, 2));
Of course you can optimize this a little bit more...but that would make the trick without having to think on how many fields you might get in the future or having a lot of if/case conditions.
I've got some text files I need to extract data from. The file itself contains around a hundred lines and the interesting part for me is:
AA====== test==== ====================================================/
AA normal low max max2 max3 /
AD .45000E+01 .22490E+01 .77550E+01 .90000E+01 .47330E+00 /
Say I need to extract the double values under "normal", "low" and "max". Is there any efficient and not-too-error-prone solution other than regexing the hell out of the text file?
If you really want to avoid regexes, and assuming you'll always have this same basic format, you could do something like:
HashMap<String, Double> map = new HashMap<>();
Scanner scan = new Scanner(filePath); //or your preferred input mechanism
assert (scan.nextLine().startsWith("AA====:); //remove the top line, ensure it is the top line
while (scan.hasNextLine()){
String[] headings = scan.nextLine().split("\\s+"); //("\t") can be used if you're sure the delimiters will always be tabs
String[] vals = scan.nextLine().split("\\s+");
assert headings[0].equals("AA"); //ensure
assert vals[0].equals("AD");
for (int i = 1; i< headings.length; i++){ //start with 1
map.put(headings[i], Double.parseDouble(vals[i]);
}
}
//to make sure a certain value is contained in the map:
assert map.containsKey("normal");
//use it:
double normalValue = map.get("normal");
}
Code is untested as I don't have access to an IDE at the moment. Also, I obviously don't know what's variable and what will remain constant here (read: the "AD", "AA", etc.), but hopefully you get the gist and can modify as needed.
If each line will always have this exact form you can use String.split()
String line; // Fill with one line from the file
String[] cols = line.split(".")
String normal = "."+cols[0]
String low = "."+cols[1]
String max = "."+cols[2]
If you know what index each value will start, you can just do substrings of the row. (The split method technically does a regex).
i.e.
String normal = line.substring(x, y).trim();
String low = line.substring(z, w).trim();
etc.
I'm wondering how I could grab each nth lines from a String, say each 100, with the lines in the String being seperated with a '\n'.
This is probably a simple thing to do but I really can't think of how to do it, so does anybody have a solution?
Thanks much,
Alex.
UPDATE:
Sorry I didn't explain my question very well.
Basically, imagine there's a 350 line file. I want to grab the start and end of each 100 line chunk. Pretending each line is 10 characters long, I'd finish with a 2 seperate arrays (containing start and end indexes) like this:
(Lines 0-100) 0-1000
(Lines 100-200) 1000-2000
(Lines 200-300) 2000-3000
(Lines 300-350) 3000-3500
So then if I wanted to mess around with say the second set of 100 lines (100-200) I have the regions for them.
You can split the string into an array using split() and then just get the indexes you want, like so:
String[] strings = myString.split("\n");
int nth = 100;
for(int i = nth; i < strings.length; i + nth) {
System.out.println(strings[i]);
}
String newLine = System.getProperty("line.separator");
String lines[] = text.split(newLine);
Where text is string with your whole text.
Now to get nth line, do e.g.:
System.out.println(lines[nth - 1]); // Minus one, because arrays in Java are zero-indexed
One approach is to create a StringReader from the string, wrap it in a BufferedReader and use that to read lines. Alternatively, you could just split on \n to get the lines, of course...
String[] allLines = text.split("\n");
List<String> selectedLines = new ArrayList<String>();
for (int i = 0; i < allLines.length; i += 100)
{
selectedLines.add(allLines[i]);
}
This is simpler code than using a BufferedReader, but it does mean having the complete split string in memory (as well as the original, at least temporarily, of course). It's also less flexible in terms of being adapted to reading lines from other sources such as a file. But if it's all you need, it's pretty straightforward :)
EDIT: If the start indexes are needed too, it becomes slightly more complicated... but not too bad. You probably want to encapsulate the "start and line" in a single class, but for the sake of brevity:
String[] allLines = text.split("\n");
List<String> selectedLines = new ArrayList<String>();
List<Integer> selectedIndexes = new ArrayList<Integer>();
int index = 0;
for (int i = 0; i < allLines.length; i++)
{
if (i % 100 == 0)
{
selectedLines.add(allLines[i]);
selectedIndexes.add(index);
}
index += allLines[i].length + 1; // Add 1 for the trailing "\n"
}
Of course given the start index and the line, you can get the end index just by adding the line length :)