HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaModerate

ISBN missing number solver

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
solvernumberisbnmissing

Problem

This is a programming contest question. Question as follows

Problem Definition


An ISBN (International Standard Book Number) is a ten digit code that
uniquely identifies a book. The first 9 digits are used to represent
the book and the 10th digit is used to ensure that the ISBN is
correct. To validate the ISBN number, calculate a sum that is 10 times
the first digit plus 9 times the second digit plus 8 times the third
digit ... all the way until you add 1 times the last digit. If the sum
is divisible by 11, then the 10 digit code is a valid ISBN number.


For example 1111456291 is a valid ISBN, because


101 + 91 + 81 + 71 + 64 + 55 + 46 + 32 + 29 + 11 = 132 which
is divisible by 11.


Each of the first nine digits can take a value between 0 and 9.
Sometimes it is necessary to make the last digit equal to ten. This is
done by writing the last digit as X.


For example, 156881111X is a valid ISBN, because


101 + 95 + 86 + 78 + 68 + 51 + 41 + 31 + 21 + 110 = 231
which is divisible by 11.


You have to write a program to fill in the missing digit from a given
ISBN number where the missing digit is represented as '?'. The missing
digit should be a value between 0 and 9 or 'X' (X represents 10)


Input Format


ISBN Code
(A single line with a ten digit ISBN number that contains '?' in a
single position. The length of the input should be 10 characters.)


Output Format



  • The output should contain the missing digit.



-
For any malformed input print 'INVALID INPUT'

-
If a suitable value for '?' cannot be found which makes the
ISBN valid, then the text 'NO SOLUTION POSSIBLE' should be displayed
as output.



Samples



Solution

```
import java.util.Scanner;

/**
* @see https://gist.github.com/tintinmj/18510d388e4d316c215e
* @author tintinmj
*/

public class ISBNcodeSolver {
private static final String MISSING_NUMBER = "?";
private static final String

Solution

Overview

Your code is generally pretty good, but there are places where it can be edited.

Let's start off with...

private boolean isValid(String input) {
    if (input.length() != ISBN_CODE_LENGTH) {
        return false;
    }
    if (!isOnlyOneNumberMissing(input)) {
        return false;
    }
    if (input.contains(LAST_NUMBER_AS_X)
            && !input.endsWith(LAST_NUMBER_AS_X)) {
        return false;
    }
    int noOfDigits = countDigitOccurence(input);
    if ((input.endsWith(LAST_NUMBER_AS_X) && noOfDigits != ISBN_CODE_LENGTH - 2)
            ||
            (!input.endsWith(LAST_NUMBER_AS_X) && noOfDigits != ISBN_CODE_LENGTH - 1)) {
        return false;
    }
    return true;
}

private boolean isOnlyOneNumberMissing(String input) {
    return countOccurence(input, MISSING_NUMBER) == 1;
}

private int countOccurence(String haystack, String needle) {
    int count = haystack.length() - haystack.replace(needle, "").length();
    return count;
}

private int countDigitOccurence(String haystack) {
    int count = haystack.length() - haystack.replaceAll("\\d", "").length();
    return count;
}


All this code is inefficient and can make your head spin. It is better done using a regular expression:

private boolean isValid(String input) {
    return input.length() == ISBN_CODE_LENGTH
            && input.matches("\\d*\\" + MISSING_NUMBER + "\\d*X?");
}


This method simply checks if it is the right length first, and then checks if it matches "\\d\\" + MISSING_NUMBER + "\\dX?" (which is "\d\?\dX?"). This regex matches a String with any number of digits, followed by a "?", another set of digits, and finally a X, which is optional.

This part can also be edited:

private int calculateSum(String ISBNcode) {
    int sum = 0;
    char[] codeAlphabets = ISBNcode.toCharArray();
    int multiple = 10;
    for (int i = 0; i < codeAlphabets.length; i++) {
        if (Character.isDigit(codeAlphabets[i])) {
            sum += multiple * Character.digit(codeAlphabets[i], 10);
        }
        multiple--;
    }

    if (ISBNcode.endsWith(LAST_NUMBER_AS_X)) {
        sum += 10;
    }

    return sum;
}


Most important is:

if (ISBNcodeWithOneDigitMissing.endsWith(LAST_NUMBER_AS_X)
        && (sum + 10) % MULTIPLE_OF == 0) {
    return LAST_NUMBER_AS_X;
}


I think you meant:

if (ISBNcodeWithOneDigitMissing.endsWith(MISSING_NUMBER)
        && (sum + 10) % MULTIPLE_OF == 0) {
    return LAST_NUMBER_AS_X;
}


Next, the for loop. The if statement could only return true if:

a) It comes to the question mark; or

b) It reaches the end (where X is possible)

That pretty much means that the if will be useful only twice in ten times. You can change the for loop so that it can be changed into something less time-consuming (and since the length of the array is used multiple times, I add an int to hold the length):

int length = codeAlphabets.length;
for (int i = 0; i < length - (codeAlphabets[length - 1] == 'X' ? 1 : 0); i++)


Then change:

if (Character.isDigit(codeAlphabets[i]))


To:

if (codeAlphabets[i] != '?')


Which saves some time for the program.

Also:

private int findMissingNumberMultiple(String ISBNcode) {
    return (ISBN_CODE_LENGTH - ISBNcode.indexOf(MISSING_NUMBER));
}


The parentheses are optional:

private int findMissingNumberMultiple(String ISBNcode) {
    return ISBN_CODE_LENGTH - ISBNcode.indexOf(MISSING_NUMBER);
}


After all this editing...

Final code:

Note: Comments were removed, because while I was editing on Eclipse, it looked very messy with it.

```
import java.util.Scanner;

public class ISBNSolver {

private static final String MISSING_NUMBER = "?";
private static final String LAST_NUMBER_AS_X = "X";
private static final int ISBN_CODE_LENGTH = 10;
private static final int MULTIPLE_OF = 11;

public static void main(String[] args) {
try (Scanner sc = new Scanner(System.in)) {
String code = sc.nextLine();

System.out.println(new ISBNSolver().run(code));
}
}

public String run(String input) {
return findMissingCodeDigit(input);
}

public String findMissingCodeDigit(String ISBNcodeWithOneDigitMissing) {

if (!isValid(ISBNcodeWithOneDigitMissing)) {
return "INVALID INPUT";
}

int sum = calculateSum(ISBNcodeWithOneDigitMissing);
int missingNumberMultiple = findMissingNumberMultiple(ISBNcodeWithOneDigitMissing);

for (int i = 0; i <= 9; i++) {
if ((sum + (missingNumberMultiple * i)) % MULTIPLE_OF == 0) {
return String.valueOf(i);
}
}

if (ISBNcodeWithOneDigitMissing.endsWith(MISSING_NUMBER)
&& (sum + 10) % MULTIPLE_OF == 0) {
return LAST_NUMBER_AS_X;
}

return "NO SOLUTION POSSIBLE";
}

private boolean isValid(String input) {
return input.length() == ISBN_CODE_LENGTH

Code Snippets

private boolean isValid(String input) {
    if (input.length() != ISBN_CODE_LENGTH) {
        return false;
    }
    if (!isOnlyOneNumberMissing(input)) {
        return false;
    }
    if (input.contains(LAST_NUMBER_AS_X)
            && !input.endsWith(LAST_NUMBER_AS_X)) {
        return false;
    }
    int noOfDigits = countDigitOccurence(input);
    if ((input.endsWith(LAST_NUMBER_AS_X) && noOfDigits != ISBN_CODE_LENGTH - 2)
            ||
            (!input.endsWith(LAST_NUMBER_AS_X) && noOfDigits != ISBN_CODE_LENGTH - 1)) {
        return false;
    }
    return true;
}

private boolean isOnlyOneNumberMissing(String input) {
    return countOccurence(input, MISSING_NUMBER) == 1;
}

private int countOccurence(String haystack, String needle) {
    int count = haystack.length() - haystack.replace(needle, "").length();
    return count;
}

private int countDigitOccurence(String haystack) {
    int count = haystack.length() - haystack.replaceAll("\\d", "").length();
    return count;
}
private boolean isValid(String input) {
    return input.length() == ISBN_CODE_LENGTH
            && input.matches("\\d*\\" + MISSING_NUMBER + "\\d*X?");
}
private int calculateSum(String ISBNcode) {
    int sum = 0;
    char[] codeAlphabets = ISBNcode.toCharArray();
    int multiple = 10;
    for (int i = 0; i < codeAlphabets.length; i++) {
        if (Character.isDigit(codeAlphabets[i])) {
            sum += multiple * Character.digit(codeAlphabets[i], 10);
        }
        multiple--;
    }

    if (ISBNcode.endsWith(LAST_NUMBER_AS_X)) {
        sum += 10;
    }

    return sum;
}
if (ISBNcodeWithOneDigitMissing.endsWith(LAST_NUMBER_AS_X)
        && (sum + 10) % MULTIPLE_OF == 0) {
    return LAST_NUMBER_AS_X;
}
if (ISBNcodeWithOneDigitMissing.endsWith(MISSING_NUMBER)
        && (sum + 10) % MULTIPLE_OF == 0) {
    return LAST_NUMBER_AS_X;
}

Context

StackExchange Code Review Q#58441, answer score: 15

Revisions (0)

No revisions yet.