Check if char is in () block - java

I would like to check if a semicolon (;) is in the brackets of an AND or OR block within a string.
For example:
IF(AND(ROUND($GX18-SUM(0)/$M$12;2)<=0;$AK$7=1);0;OR(1;A2)+O2)
If it's not within an AND or OR then I replace it with #:
IF(AND(ROUND($GX18-SUM(0)/$M$12;2)<=0$AK$7=1)#0#OR(1;A2)+O2)
I know how to do the substitution, but how do I detect whether the ; is inside such a block?
UPDATE
Using regex possibly seems quite complex. However, to break down the problem:
How to detect if a certain char(;) is within an AND(...) or OR(...)? This would help me a lot!

Hope following java code helps to resolve your problem,
String str = "IF(AND(ROUND($GX18-SUM(0)/$M$12;2)<=0$AK$7=1);0;OR(1;A2)+O2)";
char[] ch = str.toCharArray();
int count = 0;
String temp = "";
for (int i = 0; i < ch.length; i++) {
temp = temp + ch[i];
if ("AND(".equals(temp) || "OR(".equals(temp)) {
count++;
}
if ("(".equals(temp) && count > 0) {
count++;
}
if (")".equals(temp) && count > 0) {
count--;
}
if (";".equals(temp) && count == 0) {
ch[i] = '#';
}
if ((!"AND(".startsWith(temp) && !"OR(".startsWith(temp)) || temp.length() > 4) {
temp = "";
}
}
System.out.println("Expected Data >> " + String.valueOf(ch));

Related

Java Array nested for loops, not clearing second for to go back to the first

The for loops in the code below are first looping through the user entries, then checking the number of spaces " " in the string. After checking through them a question is asked if they want to display strings with no spaces, one, or more. I don't think the loop is making it past the first instance of the for loop as the second should be looking for a 0 value on the count variable. Below is the nested for loops:
if(answer.equals("No")){
for (int i = 0; i < array.length;i++) {
if (array[i] != null) {
for (int count = 0; array[i].charAt(i) != ' ';count++) {
if(count == 0)
System.out.println(array[i]+" ");
}
}
}
}
Primary code for reference:
import java.util.*;
import java.util.Scanner;
public class StringsWithSpaces {
public static void main(String[] args)
{
Scanner s = new Scanner(System.in);
String[] array = new String[20];
System.out.println("Please enter anything..., or type QUIT to quit.");
for (int i = 0; i < array.length; i++) {
array[i] = s.nextLine();
boolean result = Arrays.stream(array).anyMatch("QUIT"::equals);
if(result)
{
break;
}
}
String str = null;
int len = -1;
System.out.println("Would you like to display strings with No Spaces, One Space or More? Type No, One, More to see the results: ");
String answer = s.nextLine();
if(answer.equals("No")){
for (int i = 0; i < array.length;i++) {
if (array[i] != null) {
for (int count = 0; array[i].charAt(i) != ' ';count++) {
if(count == 0)
System.out.println(array[i]+" ");
}
}
}
}
else if(answer.equals("One"))
{
for (int i = 0; i < array.length;i++) {
int count = 0;
if (array[i] != null) {
if (array[i].charAt(i) != ' ') {
count++;
System.out.println(count);
}
//System.out.print(array[i] + " ");
}
}
}
else
System.out.println("No values to show");
System.out.println();
}
}
I have looked for something similar, but could only find other languages. Thanks for all the help.
In the first set of nested loops you're basically checking the same character of each word each time. You should use count as the parameter for the charAt method instead of whatever index is in i;
Also you should only print out the word when count equals the length of the word minus one.
Here it is:
if(answer.equals("No")){
for (int i = 0; i < array.length;i++) {
if (array[i] != null) {
for (int count = 0; array[i].charAt(i) != ' ' ;count++) {
if(count == array[i].length()-1) {
System.out.println(array[i]);
break;
}
}
}
}
}
I also rewrote the second set of nested loops, it should explain itself:
else if(answer.equals("One"))
{
for (int i = 0; i < array.length;i++) {
int count = 0;
for (int j = 0; j < array[i].length(); j++) {
if (Character.isSpaceChar(array[i].charAt(j)))
count++;
}
if (count == 1) {
System.out.println(array[i]);
}
}
}
EDIT: I forgot to mention that I added the break statement that you forgot. It ensures that the for loop is exited when you reach the last character in the string. Without this statement your application would crash each time it hits the end of a string because of an IndexOutOfBoundsException.

Eliminating duplicate characters in a String

I am currently solving the following hackerrank problem https://www.hackerrank.com/challenges/reduced-string/problem, in where given a string I have to eliminate pairs of characters that are the same.
My code is as follows:
static String super_reduced_string(String s){
for (int i = 0; i < s.length()-1; i++) {
if (s.charAt(i) == s.charAt(i+1)) {
s = s.substring(0, i) + s.substring(i+2);
i = 0;
}
}
if (s.length() == 0) {
return("Empty String");
} else {
return(s);
}
}
It works in most cases, however with certain testcases such as if the string is "baab", the code outputs "bb" (baab should be simplified to bb and then to an empty string) instead of an empty string, however I don't see why this is the case.
At the end of the for-loop i is incremented. So if you want the loop to begin at the start again after a match you need to set i to -1 so the next loop run starts with i==0.
static String super_reduced_string(String s){
for (int i = 0; i < s.length()-1; i++) {
if (s.charAt(i) == s.charAt(i+1)) {
s = s.substring(0, i) + s.substring(i+2);
i = -1; // so after the ++ at the end of the loop, the next loop-run will have i==0.
}
}
if (s.length() == 0) {
return("Empty String");
} else {
return(s);
}
}
Tampering with a loop counter is always a bit error prone. So I recommend avoiding it.

Confusion while trying to comprehend the solution to a codingbat assignment

Here is the problem from codingbat.com that I am having trouble understanding the solution to.
Given a string, return a version where all the "x" have been removed.
Except an "x" at the very start or end should not be removed.
stringX("xxHxix") → "xHix"
stringX("abxxxcd") → "abcd"
stringX("xabxxxcdx") → "xabcdx"
Here is the solution they provided:
public String stringX(String str) {
String result = "";
for (int i=0; i<str.length(); i++) {
// Only append the char if it is not the "x" case
if (!(i > 0 && i < (str.length()-1) && str.substring(i, i+1).equals("x"))) {
result = result + str.substring(i, i+1); // Could use str.charAt(i) here
}
}
return result;
}
If someone could break down the complex logic in the if statement, it would be really helpful. Does the negation operator(!) apply to the entire if statement or only to what's inside of the first set of parenthesis? That if statement is what's really confusing me.
Thank you in advance.
For clarity, let us reformat the if statement:
if (
!(
i > 0
&& i < (str.length()-1)
&& str.substring(i, i+1).equals("x")
)
)
i > 0 && i < (str.length()-1) checks that the element is between the two end indexes of the String.
str.substring(i, i+1).equals("x") checks if the current element has value 'x'.
Finally, the negation applies to the logical AND of the above 2 statements.
In plain English this would be, "Append the current letter to your string, either if it is at one of the ends, or if it is between the extremes and not equal to x".
The above code would be the same as negating each part as in
if (i != 0 && i != str.length() -1 && !str.substring(i, i+1).equals("x")) {
result = result + str.substring(i, i+1); // Could use str.charAt(i) here
}
public String stringX(String str) {
int len = str.length();
String word = "";
for (int i=0; i<len; i++){
if (!(i>0 && i < len-1 && str.charAt(i)=='x')){
word += str.charAt(i);
}
}
return word;
}
i hope u will like the below code:
public static String stringX(String str) {
if(str.length()<2){
return str;
}
String sub_first = str.substring(0, 1);
String sub_last = str.substring(str.length() - 1);
String sub_mid = str.substring(1, str.length() - 1);
for (int i = 0; i < sub_mid.length(); i++) {
if (sub_mid.charAt(i) == 'x') {
sub_mid = sub_mid.replace("x", "");
}
}
return sub_first + sub_mid + sub_last;
}
Better Consider this Solution.
The Below Solution is same as given by you with more clarity.
StrHasChanged - Check whether String contains 'X'.
Then iterate from second till last before character.
if StrHasChanged is false return Original string OR return modified String.
public String stringX(String str) {
String newStr = "";
Boolean StrHasChanged = false;
if (str.length() > 1) {
for (int itr = 1; itr < str.length()-1; itr ++) {
if (str.charAt(itr) != 'x') {
StrHasChanged = true;
newStr = newStr + Character.toString(str.charAt(itr));
}
}
}
return ((str.length() == 1) || (!(StrHasChanged))) ? str : Character.toString(str.charAt(0)) + newStr + Character.toString( str.charAt(str.length()-1) );
}
public String stringX(String str) {
if (str.length() <= 3)
return str;
else {
String c = str.substring(0, 1);
String d = str.substring(str.length() - 1, str.length());
String a = str.replaceAll("x", "");
if ((c.equals("x")) && (d.equals("x")))
return c + a + d;
else if (c.equals("x"))
return c + a;
else if (d.equals("x"))
return a + d;
else
return a;
}
}

Tracking brackets in a string

I am having the following string and want to track the closing bracket of ROUND( ) in my string.
"=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10)/$M$9;CA18*CB18)))/$M$12;$M$11)";
public class RoundParser {
public static String parseRound(String text) {
text = text.toUpperCase();
String result;
char[] ch = text.toCharArray();
int count = -1;
String temp = "";
for (int i = 0; i < ch.length; i++) {
temp = temp + ch[i];
System.out.println(count);
if ("ROUND(".equals(temp)) {
count++;
}
if ("(".equals(temp)) {
count++;
}
if (")".equals(temp) && count > 0) {
count--;
}
if (")".equals(temp) && count == 0) {
ch[i] = '#';
}
if (!"ROUND(".startsWith(temp) || temp.length() > 5) {
temp = "";
}
}
text = String.valueOf(ch);
result = text;
return result;
}
public static void main(String[] args) {
String text = "=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10)/$M$9;CA18*CB18)))/$M$12;$M$11)";
System.out.println(parseRound(text));
}
}
However, using my parser at the moment I am getting:
=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10)/$M$9;CA18*CB18))#/$M$12;$M$11#
The output I want to get is:
=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10#/$M$9;CA18*CB18)))/$M$12;$M$11#
As you can see the not the right ) are replaced, as ;$M$11)"; and ;$M$10). I really appreciate if you have any idea how to repalce these two cases.
there are 2 approaches to this
1) if the number of opening and closing brackets are always going to be equal, then you can just track the last closing bracket by using a for loop.
2) if you are not sure about opening and closing brackets to be equal, then you can so the following-->
public class RoundParser {
public static String parseRound(String text) {
text = text.toUpperCase();
String result;
char[] ch = text.toCharArray();
int count=0,pos=0;
int c[10];
for(int i=0;i<ch.length;i++){
if(ch[i].equals("(")){
count++;
if(ch[i-1].equals("D")){
c[pos]=count; //will store the count value at every opening round
pos++;
}
}
if(ch[i].equals(")")){
count--;
for(int j=0;j<10;j++){
if(c[j]==count) //if the closing of round had been encountered
ch[i]="#";
}
}
}
text = String.valueOf(ch);
result = text;
return result;
}
public static void main(String[] args) {
String text = "=ROUND(IF(AND($BY18=2);CA18*CB18/$M$11;IF($BY18=3;CA18*CB18/$M$10;IF($BY18=4;ROUND(CA18*CB18;$M$10)/$M$9;CA18*CB18)))/$M$12;$M$11)";
System.out.println(parseRound(text));
}
}
there you go.
i think this should work.
hope this helps.
You forgot an else:
else if (")".equals(temp) && count == 0) {
That will decrement count and if then count==0, it will decrement twice.
This problem can be done recursively.
First, you use method .indexOf("ROUND(") to detect the first occurrence of round().
Then, we need to determine which is the end ')' of this round(). A simple algo will be enough :
int start = text.indexOf("ROUND(") + "ROUND(".length();
int count = 1;
int end = -1;
for(int i = start; i < text.length; i++){
if(text.charAt(i) == '('){
count++;
}else if(text.charAt(i) == ')'){
count--;
}
if(count == 0){
end = i;
break;
}
}
After you detect the start and end of the outer round(), you can use text.substring(start, end) to remove the outer round(), and continue the above function recursively, until you find all round()
For recognition of multiple ROUND(X), I suggest
TreeMap<Integer,Pair<Integer,Integer>> map = new TreeMap<>();
int count = 0;
Where we store <start_index, <init_count, end_index>>
if ("ROUND(".equals(temp))
{
map.put(i, new Pair<Integer,Integer>(count, -1));
count++;
}
if ("(".equals(temp)) count++;
if (")".equals(temp))
{
if (count <= 0)
{
count = 0;
// Error: extra closing bracket
}
else
{
count--;
}
int max_i = -1;
for (Integer index : map.keySet())
{
if (index > max_i
&& map.get(index).second() == -1
&& map.get(index).first() == count)
{
max_i = index;
}
}
if (max_i > -1) map.get(max_i).setSecond(i);
}
Here's an algorithm..
If you are not sure that the last ")" would be the one you are looking for.
Start from index 0 of the String, for each "(" you encounter, increment the count, and for each ")" decrement the count, replace the ")" with "#".

How could I solve this error, with my string to equation convertin calculator?

I'm writing a calculator code that solves the input whats given in string. All is good, except when it gets a negative result in the parentheses it fails badly because two operations get next to each other:
1+2*(10-11) >> 1+2*(-1) >> 1+2*-1
So where *- is, it gets "" (nothing) in the BigDecimal's constructor.
I know what's the problem, but how can I solve it?
import java.math.BigDecimal;
import java.util.ArrayList;
public class DoMath {
public static void main(String[] args) {
// Test equation goes here.
String number = "95.3+43.23*(10-11.1)";
System.out.println(doMath(number));
}
public static BigDecimal doMath(String input) {
StringBuilder builtInput = new StringBuilder(input);
StringBuilder help = new StringBuilder();
// Check if there are parenthesis in the equation.
boolean noParenthesis = true;
for (int i = 0; i < builtInput.length(); i++) {
if (builtInput.charAt(i) == 40) {
noParenthesis = false;
break;
}
}
if (noParenthesis) { // If there are no parenthesis, calculate the equation!
return calculateAndConvert(builtInput);
} else { // If there are parenthesis, breakdown to simple equations!
int parenthesePair = 0;
// Start extracting characters from the builtInput variable.
for (int i = 0; i < builtInput.length(); i++) {
// Start where we find a parentheses opener.
if (builtInput.charAt(i) == 40) {
parenthesePair = 1;
builtInput.deleteCharAt(i);
for (int j = i; j < builtInput.length(); j++) {
// If we find another opener, add one to parenthesePair variable.
if (builtInput.charAt(j) == 40) {
parenthesePair++;
}
// If we find a closer, subtract one from the given variable.
if (builtInput.charAt(j) == 41) {
parenthesePair--;
}
// If we have found the matching pair, delete it and break the for loop.
if (parenthesePair == 0) {
builtInput.deleteCharAt(j);
builtInput.insert(j, doMath(help.toString()));
break;
}
help.append(builtInput.charAt(j));
builtInput.deleteCharAt(j);
j--;
}
break;
}
}
}
System.out.println(builtInput);
return doMath(builtInput.toString());
}
public static BigDecimal calculateAndConvert(StringBuilder input) {
ArrayList<BigDecimal> listOfNumbers = new ArrayList<BigDecimal>();
StringBuilder numBay = new StringBuilder();
StringBuilder operations = new StringBuilder();
// If the first character is -, the first number is negative.
boolean firstIsNegative = false;
if (input.charAt(0) == 45) {
firstIsNegative = true;
input.deleteCharAt(0);
}
// Converting to numbers.
while (input.length() != 0) {
// If the character is a number or a dot, put it in the numBay variable and delete the char.
if (input.charAt(0) >= 48 && input.charAt(0) <= 57 || input.charAt(0) == 46) {
numBay.append(input.charAt(0));
// If the character is not a number, put it in the operations variable
// and save the number in the list (not operator characters are filtered)
} else {
listOfNumbers.add(new BigDecimal(numBay.toString()));
numBay.setLength(0);
operations.append(input.charAt(0));
}
// Delete the character.
input.deleteCharAt(0);
}
listOfNumbers.add(new BigDecimal(numBay.toString()));
// Setting first number to negative if it's needed.
if (firstIsNegative) {
listOfNumbers.set(0, listOfNumbers.get(0).negate());
}
// Calculate the result from the list and operations and return it.
return calculate(listOfNumbers, operations);
}
public static BigDecimal calculate(ArrayList<BigDecimal> list, StringBuilder ops) {
BigDecimal momentaryResult;
// Check for a multiply operation - if there is one, solve it.
for (int i = 0; i < ops.length(); i++) {
if (ops.charAt(i) == 42) {
momentaryResult = list.get(i).multiply(list.get(i + 1));
list.remove(i);
list.set(i, momentaryResult);
ops.deleteCharAt(i);
i--;
}
}
// Check for a divide operation - if there is one, solve it.
for (int i = 0; i < ops.length(); i++) {
if (ops.charAt(i) == 47) {
momentaryResult = list.get(i).divide(list.get(i + 1));
list.remove(i);
list.set(i, momentaryResult);
ops.deleteCharAt(i);
i--;
}
}
// Check for a subtract operation - if there is one, solve it.
for (int i = 0; i < ops.length(); i++) {
if (ops.charAt(i) == 45) {
momentaryResult = list.get(i).subtract(list.get(i + 1));
list.remove(i);
list.set(i, momentaryResult);
ops.deleteCharAt(i);
i--;
}
}
// Check for a plus operation - if there is one, solve it.
for (int i = 0; i < ops.length(); i++) {
if (ops.charAt(i) == 43) {
momentaryResult = list.get(i).add(list.get(i + 1));
list.remove(i);
list.set(i, momentaryResult);
ops.deleteCharAt(i);
i--;
}
}
// Return with the one remaining number that represents the result.
return list.get(0);
}
}
Edit: or would it be easier to write a new code with a different algorithm...?
I would post this as a comment to your question, but I do not have the required reputation to do so.
Anyway, since you have already recognized that the bug is the "operator" *- couldn't you make a method that would fix this problem by replacing the plus operator immediately before by a minus? Like this:
1+2*-1 >>> 1-2*1
If you want I can write you the code. But maybe it will be easier for you to adapt a solution like this in your code that is already working.
Edit - 1:
Obviously, the code should also treat the following cases:
1-2*-1 >>> 1+2*1
2*-1 >>> -2*1
Edit - 2:
Here is the code I managed to make. Let me know if you find any errors.
public int countChar(String str, char chr) {
int count = 0;
for (int k = 0; k < str.length(); k++) {
if (str.charAt(k) == chr)
count++;
}
return count;
}
public String fixBug(String eq) {
boolean hasBug = eq.contains("*-");
if (hasBug) {
String subeq;
int indbug, indp, indm;
eq = eq.replace("*-", "#");
int N = countChar(eq, '#');
for (int k = N; k > 0; k--) {
indbug = eq.indexOf('#');
subeq = eq.substring(0, indbug);
indp = subeq.lastIndexOf('+');
indm = subeq.lastIndexOf('-');
if (indp == -1 && indm == -1) {
eq = "-" + eq;
} else if (indp > indm) {
eq = eq.substring(0, indp) + '-' + eq.substring(indp + 1);
} else {
eq = eq.substring(0, indm) + '+' + eq.substring(indm + 1);
}
}
eq = eq.replace("#", "*");
}
return eq;
}

Categories