I have been thinking about a cleaner way to represent combinations of if-statements. For example, the cases described by the code below could potentially be combined in some manner. I could certainly write out all of those combinations, but that would result in sacrificing code-readability for exhaustiveness.
if (foo == null) {
System.out.println("Null input");
return 1;
}
if (foo.isEmpty()){
System.out.println("Missing input");
return 2;
}
if (Character.isWhitespace(foo.charAt(0)) || Character.isWhitespace(foo.charAt(foo.length() - 1))){
System.out.println("var cannot begin or end with spaces");
return 3;
}
String[] words = foo.split(" ");
if (words.length > 2){
System.out.println("var cannot be more than two words or intermediate whitespaces");
return 4;
}
if (Pattern.compile("[0-9]").matcher(foo).find()){
System.out.println("var cannot contain digits");
return 5;
}
if (foo.split("\\t+").length > 1){
System.out.println("var cannot contain tabs");
return 6;
}
if (this.var.contains(foo)){
System.out.println("var already exists");
return 7;
}
I have seen pythonic approaches where each case is condensed into a map. Is there a viable java data structure or approach that would make the code below cleaner while still enabling me to represent all possible combinations of if-statements?
You can create a List of errors to check for, each with a Predicate to evaluate on the input and an error message.
record ErrorCondition(Predicate<String> condition, String message){}
List<ErrorCondition> errors = List.of(new ErrorCondition(s -> s == null, "Null input"), // or Objects::isNull
new ErrorCondition(String::isEmpty, "Missing input") /* other checks... */);
// ...
int code = 0;
String foo = "";
for (ErrorCondition error: errors) {
++code;
if (error.condition().test(foo)) {
System.out.println(error.message());
return code;
}
}
Related
Pretty new user here, we are learning about methods in Java and are trying to test them using an ArrayList but it is not giving the desired output.
Here are the two methods that I created:
// initialize the ArrayList declared in the class block and add some test data to it
private static void addTestData()
{
listOfNumbers = new ArrayList<>();
listOfNumbers.add(23);
listOfNumbers.add(34);
listOfNumbers.add(45);
listOfNumbers.add(56);
}
// returns a true or a false value indicating if a number provided to it is present in an ArrayList
private static boolean listContains(int param1)
{
boolean found = false;
for (int x: listOfNumbers)
{
if (param1 == x)
{
found = true;
}
else
break;
}
return found;
}
And my goal is to test in the main method if "4" and "56" are a part of the ArrayList, so here is my code for that:
boolean numberFound = false;
addTestData();
System.out.println("*** Testing ArrayList Search ***");
System.out.println("I am going to test if the number 4 is in the ArrayList.");
listContains(4);
numberFound = false;
if (numberFound = true)
System.out.println("The number 4 was found.");
else
System.out.println("The number 4 was not found.");
System.out.println("");
numberFound = false;
addTestData();
System.out.println("I am going to test if the number 56 is in the ArrayList");
listContains(56);
if (numberFound = true)
System.out.println("The number 56 was found.");
else
System.out.println("The number 56 was not found.");
My output tells me that 4 is a part of the ArrayList, when it is not, in fact. Any idea what to do?
Directions for main method
Directions for my method
My output
You are assigning rather than comparing if (numberFound = true). It should be if (numberFound == true), or rather simply if (numberFound).
Your function listContains() returns a boolean whicih you need to assign to numberFound.
numberFound = listContains(4);
Or you can straight up use it in if condition,
if(listContains(4)) { }
Your function listContains() has issue. It should return true if found and false if not after iterating over all elements, which you are not. Right on 1st iteration, if comparison is false, you are breaking the loop, which return false
for (int x: listOfNumbers)
{
if (param1 == x)
{
return true;
}
}
return false;
if according to your class instruction
boolean found = false;
for (int x: listOfNumbers)
{
if (param1 == x)
{
found = true;
break; // with this you can skip remaining unnecessary iteration
}
}
return found;
So Im creating a Helper method in Java to calculate postage based on size, but I can't seem to figure out the return part. Im still new to helper methods and accessors etc. Im using Eclipse and its telling me "Add return statement" but I did.. What am I doing wrong here?
Here is my code:
//Helper Method.
public int calculatePostageCost() {
double postCost;
if(satchelSize.equals("small"))
postCost = 10;
else if(satchelSize.equals("Medium") || satchelSize.equals("medium"))
postCost = 13;
else if(satchelSize.equalsIgnoreCase("Large") || satchelSize.equals("large"))
postCost = 17;
else {
return calculatePostageCost();
}
}
The problem is that you do not have a guaranteed return statement at the end of the function. What would happen if your function does not encounter a satchel size, which is either "small", "medium", etc, you will return the value of what your function calculatePostageCost returns (I will return to that later).
In every other case, however, you do not have a return in your function. When you encounter "small" as satchel size, you enter the first if block of code, where you will set postCost to 10, then you jump over the rest of the code (since it is all else if).
Most likely you are missing a statement like return postCode; below the else block. This would at least eliminate the error message from eclipse. I am not fully sure about your code, but you could have an endless recursion here. Your else block might be a problem:
else {
return calculatePostageCost();
}
You need to check if it is possible, that in the next call of this recursion, the else block will not be reached. When this is not the case, you will have an endless recursion everytime you enter this function while you are in a state where the satchel size is not "small", "medium", etc, because you won't have a chance to change the state and get out of these calls anymore.
Don't use strings to compare your size, create an enum to do that:
public enum Size {
SMALL, MEDIUM, LARGE
}
private Size satchelSize; ....
public int calculatePostageCost() {
switch(satchelSize) {
case SMALL:
return 10;
case MEDIUM:
return 13;
case LARGE:
return 17;
}
}
If you are very keen on keeping the strings you can switch on strings too:
private String stachelSize = ....;
public int calculatePostageCost() {
switch(satchelSize.toUpperCase()) {
case "SMALL":
return 10;
case "MEDIUM":
return 13;
case "LARGE":
return 17;
default:
throw new AssertionError("Don't know satchel size " + satchelSize);
}
}
Note that your original code had
else {
return calculatePostageCost();
}
Which would call the same function again, which would end up in the same else branch, calling the same function again, which would end up in the same else branch, which.... would eventually give a StackOverflowException.
(I understand that strictly it does not answer your question 'why doesn't this compile'.)
The problem is that your return statement is within the scope of the else statement ,it should be outside like this : `
public int calculatePostageCost() {
double postCost;
if(satchelSize.equals("small"))
postCost = 10;
else if (satchelSize.equals("Medium") || satchelSize.equals("medium")){
postCost = 13;
else if(satchelSize.equalsIgnoreCase("Large") || satchelSize.equals("large"))
postCost = 17;
return postCost;
}
If you return calculatePostageCost() you create a recursive loop which causes a stack overflow error.
Do like this,
//Helper Method.
public int calculatePostageCost() {
int postCost = 5; // i don't know about default conndition, i am taking 5
if(satchelSize.equals("small"))
postCost = 10;
else if(satchelSize.equals("Medium") || satchelSize.equals("medium"))
postCost = 13;
else if(satchelSize.equalsIgnoreCase("Large") || satchelSize.equals("large"))
postCost = 17;
}
return postCost ;
}
You have to return value on every possible scenario. Right now you are returning (infinite recursion thus stak overflow will happen) only single in case if package is not small not medium nor large. You have to return value for every of this variants, like that:
public int calculatePostageCost() {
int postCost=1234; // default cost for not small nor medium nor large package
if(satchelSize.equals("small"))
postCost = 10;
else if(satchelSize.equals("Medium") || satchelSize.equals("medium"))
postCost = 13;
else if(satchelSize.equalsIgnoreCase("Large") || satchelSize.equals("large"))
postCost = 17;
return postCode
}
Or even better
public int calculatePostageCost() {
if(satchelSize.equalsIgnoreCase("small"))
return 10;
else if(satchelSize.equalsIgnoreCase("Medium"))
return 13
else if(satchelSize.equalsIgnoreCase("Large"))
return 17;
return 12345; // cos of non small, medium nor large package
}
Having a String representation of a number(no decimals), what's the best way to convert it to either one of java.lang.Integer or java.lang.Long or java.math.BigInteger? The only condition is that the converted type should be of minimal datatype required to hold the number.
I've this current implementation that works fine, but I would like to know if there's a better code without exception handling.
package com.stackoverflow.programmer;
import java.math.BigInteger;
public class Test {
public static void main(String[] args) {
String number = "-12121111111111111";
Number numberObject = null;
try {
numberObject = Integer.valueOf(number);
} catch (NumberFormatException nfe) {
System.out.println("Number will not fit into Integer type. Trying Long...");
try {
numberObject = Long.valueOf(number);
} catch (NumberFormatException nfeb) {
System.out.println("Number will not fit into Long type. Trying BigInteger...");
numberObject = new BigInteger(number);
}
}
System.out.println(numberObject.getClass() + " : "
+ numberObject.toString());
}
}
From what you said, here is what I would have done:
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
public class TestSO09_39463168_StringToMinimalNumber {
public static void main(String[] args) {
List<String> strNumbers = Arrays.asList("0", //int
"123", //int
"-456", //int
"2147483700", // Long
"-2147483700", // Long
"9223372036854775900", //BigInt
"-9223372036854775900" //BigInt
);
for(String strNumber : strNumbers){
Number number = stringToMinimalNumber(strNumber);
System.out.println("The string '"+strNumber+"' is a "+number.getClass());
}
}
public static Number stringToMinimalNumber(String s){
BigInteger tempNumber = new BigInteger(s);
if(tempNumber.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0 || tempNumber.compareTo(BigInteger.valueOf(Long.MIN_VALUE)) < 0){
return tempNumber;
} else if(tempNumber.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0 || tempNumber.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0){
return tempNumber.longValue(); //Autobox to Long
} else {
return tempNumber.intValue(); //Autobox to Integer
}
}
}
You must use a temporary BigInteger, or else you'll end up with lazarov's solution, which is correct, but you can't really do something like that for reason mentionned in the comments.
Anyway, every BigInteger (the ones that are not returned) will be garbage collected. As for autoboxing, I don't think it's that of a bad thing. You could also make "BigInteger.valueOf(Long.MAX_VALUE))" as a constant. Maybe the compiler or the JVM will do this on its own.
I'm not really sure of how efficient it is, and using only BigInteger might be a good idea (as Spotted did), because I serioulsy doubt it would really improve the rest of your code to use the right size, and it might even be error prone if you try to use these Numbers with each other ... But again, it all depend on what you need. (and yes, using Exception as flow control is a really bad idea, but you can add a try catch on the BigInteger tempNumber = new BigInteger(s); to throw your own exception if s is not a number at all)
For recreational purpose, I have made the solution without using a BigInteger, and only with String parsing (this is still not what I recommand to do, but it was fun :)
public static final String INT_MAX_VALUE = "2147483647";
public static final String LONG_MAX_VALUE = "9223372036854775807";
public static Number stringToMinimalNumberWithoutBigInteger(String numberStr){
//Removing the minus sign to test the value
String s = (numberStr.startsWith("-") ? numberStr.substring(1,numberStr.length()) : numberStr);
if(compareStringNumber(s, LONG_MAX_VALUE) > 0){
return new BigInteger(numberStr);
} else if(compareStringNumber(s, INT_MAX_VALUE) > 0){
return new Long(numberStr);
} else {
return new Integer(numberStr);
}
}
//return postive if a > b, negative if a < b, 0 if equals;
private static int compareStringNumber(String a, String b){
if(a.length() != b.length()){
return a.length() - b.length();
}
for(int i = 0; i < a.length(); i++){
if( a.codePointAt(i) != b.codePointAt(i) ){ //Or charAt()
return a.codePointAt(i) - b.codePointAt(i);
}
}
return 0;
}
Please don't use exceptions for handling flow control, this is a serious anti-pattern (also here).
As you mentionned in the comments, the real thing you've been asked is to convert a List<String> into a List<Number>.
Also, if I understand correctly, you know that:
You should encounter only numbers without decimals
The biggest value you can encounter is possibly unbound
Based on that, the following method will do the job in a more clever way:
private static List<Number> toNumbers(List<String> strings) {
return strings.stream()
.map(BigInteger::new)
.collect(Collectors.toList());
}
Eidt: if you're not very familiar with the stream concept, here's the equivalent code without streams:
private static List<Number> toNumbers(List<String> strings) {
List<Number> numbers = new ArrayList<>();
for (String s : strings) {
numbers.add(new BigInteger(s));
}
return numbers;
}
Well if you want to do it "by hand" try something like this:
We define the max values as strings :
String intMax = "2147483647";
String longMax = "9223372036854775807";
and our number:
String ourNumber = "1234567890"
Now our logic will be simple :
We will check lenghts of strings firstly
If our numbers length < int max length : IT IS INT
If our numbers length == int max length : Check is it INT or LONG
If our numbers length > int max length :
3.1 If our numbers length < long max length : IT IS LONG
3.2 If our numbers length == long max length : Check is it LONG or BIG INTEGER
3.3 If our numbers length > long max length : IT IS BIG INTEGER
The code should look something like this (I have not tried to compile it may have syntax or other errors) :
if(ourNumber.lenght() < intMax.length ){
System.out.println("It is an Integer");
} else if(ourNumber.lenght() == intMax.length){
// it can be int if the number is between 2000000000 and 2147483647
char[] ourNumberToCharArray = ourNumber.toCharArray();
char[] intMaxToCharArray = intMax.toCharArray();
int diff = 0;
for(int i = 0; i < ourNumberToCharArray.length; i++) {
diff = Character.getNumericValue(intMaxToCharArray[i]) - Character.getNumericValue(ourNumberToCharArray[i]);
if(diff > 0) {
System.out.println("It is a Long");
break;
} else if(diff < 0) {
System.out.println("It is an Integer");
break;
}
}
if(diff == 0){
System.out.println("It is an Integer");
}
} else {
if(ourNumber.lenght() < longMax.length()) {
System.out.println("It is a Long");
} else if(ourNumber.lenght() == longMax.length()){
char[] ourNumberToCharArray = ourNumber.toCharArray();
char[] longMaxToCharArray = longMax.toCharArray();
int diff = 0;
for(int i = 0; i < ourNumberToCharArray.length; i++) {
diff = Character.getNumericValue(longMaxToCharArray[i]) - Character.getNumericValue(ourNumberToCharArray[i]);
if(diff > 0) {
System.out.println("It is a BigInteger");
break;
} else if(diff < 0) {
System.out.println("It is a Long");
break;
}
}
if(diff == 0){
System.out.println("It is a Long");
}
} else {
System.out.println("It is a BigInteger");
}
}
Then logic that checks if the numbers match or not is the same in both cases you can but it in a function for example.
it's for a JOptionPane YES_NO_CANCEL_OPTION I did the following method:
public NewCard()
{
int ans = JOptionPane.showConfirmDialog(null, "Do you wish another card?", "7 in 1", JOptionPane.YES_NO_CANCEL_OPTION );
if (ans == JOptionPane.YES_OPTION)
{
ans = 1;
}
if (ans == JOptionPane.NO_OPTION)
{
ans = 2;
}
if (resp == JOptionPane.CANCEL_OPTION)
{
ans = 3;
}
return ans;
}
I want to read this answer from the main but a grammatical error appears. It says:
Invalid method declaration; return type required but I tried to write a return in all if statements and got the same error.
You must have the return type in the function declaration.
Your function returns an int, so:
public int NewCard() {
See the int there.
If you don't want to return anything, use void.
This really should be a comment, but I needed the formatting.
You need to be careful when you reuse a variable like ans.
The values of the following constants are:
JOptionPane.YES_OPTION = 0;
JOptionPane.NO_OPTION = 1;
JOptionPane.CANCEL_OPTION = 2;
You could have just as easily added 1 to ans as coded the 3 if statements.
The way you wrote your code:
if (ans == JOptionPane.YES_OPTION)
{
ans = 1;
}
if (ans == JOptionPane.NO_OPTION)
{
ans = 2;
}
if (resp == JOptionPane.CANCEL_OPTION)
{
ans = 3;
}
If ans = 0 (the YES option), you set it to 1 in the first if.
Then ans (your ans, not the original ans) = 1 (the NO option).
The same thing happens in the 3rd if.
So, if your user clicks on the YES button, you return a 3. If your user clicks on the NO button, you return a 3. Same for the CANCEL button. You return a 3.
Either use two different variables, change your second and third if to an else if, or just add 1 to ans.
I had some success with this site, and I hope I find more excellent programmers to assist me.
So I am at my wit's end with this code. I am very new to programming, especially exceptions. I have looked very hard through my course material and sought help, but I have been quite unsuccessful. I am trying to create an improved parser that will override another parser. It reads a .txt file with student information of it including an ID, a name, a grade, and an optional email address and optional comment as tokens in a String separated by commas. The override checks for errors in each token and throws an exception called ParserException. The exception will check the code and then return an error message if the error is unfixable.
For example, if a student puts in an AB for the grade, the exception will flag and check if the input is a valid grade (which it is) and then return, if it is not, then it will throw a ParserException, in this case
throw new ParserException(ParserException.GRADE_INVALID_GRADE,lineIndex);
This shows that the does not work and sends out a message GRADE_INVALID on the line indexLine
I have a list of what I need to have as an output:
Any violation of the file format specified in the Input File Format Description section above should result in an ParcerException with an appropriate message
Duplicate IDs are not allowed
Grade values must be a float (92.0) or a letter grade and not an integer
I have all the code to correct and check for errors, but I cannot figure out how to get the try-catch to work. Here's is the override code:
#Override
public ParserResult parseLine(int lineIndex) {
String[] tokens = lines.get(lineIndex).split(",");
ArrayList<Integer> idList = new ArrayList<Integer>();
Integer studentId;
String name;
String grade;
String email;
String comments;
boolean isFloat;
float gradeFinal;
String editName;
studentId = new Integer(tokens[0]);
ParserResult result;
try{
return super.parseLine(lineIndex);
}
catch(ParserException e){
// Check reasonable number of tokens
if(tokens.length >= 3 && tokens.length <= 5){
name = tokens[1];
grade = tokens[2];
// Check the student ID
if(idList.contains(studentId)){
throw new ParserException(ParserException.DUPLICATE_ID, lineIndex);
}else{
idList.add(studentId);
}
// Check the grade
if(grade.trim().equalsIgnoreCase("A")){
gradeFinal = gradeA;
}else if(grade.trim().equalsIgnoreCase("AB")){
gradeFinal = gradeAB;
}else if(grade.trim().equalsIgnoreCase("B")){
gradeFinal = gradeB;
}else if(grade.trim().equalsIgnoreCase("BC")){
gradeFinal = gradeBC;
}else if(grade.trim().equalsIgnoreCase("C")){
gradeFinal = gradeC;
}else if(grade.trim().equalsIgnoreCase("CD")){
gradeFinal = gradeCD;
}else if(grade.trim().equalsIgnoreCase("D")){
gradeFinal = gradeD;
}else if(grade.trim().equalsIgnoreCase("F")){
gradeFinal = gradeF;
}else{
try{
Integer.parseInt(grade);
isFloat = false;
}
catch(Exception fl) {
isFloat = true;
}
if(isFloat){
if((Float.parseFloat(grade) < 100f) && (Float.parseFloat(grade) >= 0f)){
gradeFinal = Float.parseFloat(grade);
}else{
throw new ParserException(ParserException.GRADE_INVALID_GRADE,lineIndex);
}
}else{
throw new ParserException(ParserException.GRADE_INTEGER_VALUE,lineIndex);
}
}
// Check the name
if(name.split(" ").length > 3){
throw new ParserException(ParserException.UNKNOWN, lineIndex);
}else{
editName = name.trim().split(" ")[0];
}
result = new ParserResult(studentId, editName, gradeFinal);
// Checks the email
if(tokens.length >= 4){
email = tokens[3];
// Check for at sign
if(!email.contains("#")){
throw new ParserException(ParserException.UNKNOWN, lineIndex);
}
int count = 0;
// Counts number of # symbols
for(int i=0; i<email.length(); i++){
if(email.indexOf(i) == '#'){
count++;
}
}
if(count > 1){
throw new ParserException(ParserException.EMAIL_MULTIPLE_AT,lineIndex);
}
if(email.split(".").length == 2){
if(!(email.trim().split(".")[1].contains(".edu")) && !(email.trim().split(".")[1].contains(".com"))){
throw new ParserException(ParserException.EMAIL_NOT_EDU_OR_COM,lineIndex);
}else{
result.setEmail(email);
}
}
// Checks if email contains .com or .edu
// Checks the comments
if(tokens.length == 5){
comments = tokens[4];
result.setComment(comments);
}
}
return result;
}
}
// TODO Call Parser's parseLine() here to attempt to parse, catch any exceptions
return null;
}
The original parseLine that is overridden, but still used is:
public ParserResult parseLine(int lineIndex) {
String[] tokens = lines.get(lineIndex).split(",");
ParserResult result = new ParserResult(Integer.parseInt(tokens[0]),
tokens[1], Float.parseFloat(tokens[2]));
result.setEmail(tokens[3]);
return result;
}
Here is the main() file:
public static void main(String[] args){
// TODO Change the line below to use ImprovedParser
Parser parser = null;
try {
parser = new ImprovedParser(args[0]);
} catch (FileNotFoundException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
System.exit(-1);
}
List<ParserResult> results = parser.parse();
int count = results.size();
double sum = 0.0;
for (ParserResult result : results) {
sum += result.getGrade();
}
System.out.println("Number of valid input lines: " + results.size());
System.out.println("Number of invalid input lines: "
+ (parser.getLineCount() - results.size()));
System.out.println("Average grade: " + sum / count);
for (ParserResult result : results) {
System.out.println(result);
}
}
Lastly, here is the .txt file that is being read:
# student_id,name,grade,email
1234,Bob,92.0,bob#test.edu
4321,Alice,95.0,alice#test.edu
1111,Eve,80.0,eve#test.edu
1121,Barry,85.0,barrytest.edu
1131,Harry,86.0,h#rry#test.edu
1121,Larry,87.0,larry#test.edu
1141,Jim Song,88.0,jim#song.edu
1151,Jerry,77.0,jerry#test.net
1161,James,65.0,james#test.com
The last six inputs should cause exceptions, but I can't figure out how to organize it to work. The code ignores the line with # symbol.
Here is a sample successful output:
Number of valid input lines: 3
Number of invalid input lines: 0
Average grade: 89.0
1234, 92.0, Bob, bob#test.edu,
4321, 95.0, Alice, alice#test.edu,
1111, 80.0, Eve, eve#test.edu,
The major changes should be in the orverride method
Please help if you can, I sit at my desk still pondering possibilities, and your help will be most-appreciated.
Assuming ParseException has an error field being an int and someMethod() that throws ParseException:
try {
someMethod();
} catch (final ParseExeption ex) {
if (ex.getError() == ParseException.SOME_ERROR) {
// do something
} else if (ex.getError() == ParseException.OTHER_ERROR) {
// do something else
}
}
Note that it's usually better to use specific exceptions for specific error, something like SomeErrorParseException, OtherErrorParseException, ... (those can extends ParseException if you want) and try-catch like this:
try {
someMethod();
} catch (final SomeErrorParseException ex) {
// do something
} catch (final OtherErrorParseException ex) {
// do something else
}
Some reading: http://docs.oracle.com/javase/tutorial/essential/exceptions/index.html
It seems that there is no code to actually cause the catch clause in the first place. Try adding a throw new ParserException(STUFF_HERE); when an error has been detected while reading the file.