I have a method returning a list of String that need to be sorted. However, I'm running into the old String number sorting issue and was wondering if any one could assist with a Comparator implementation or point me in the direction of one.
The list is going to return something list this:
State Lower Legislative District 1
State Lower Legislative District 11
State Lower Legislative District 12
...
State Lower Legislative District 2
...
State Lower Legislative District 100
...
State Upper Legislative District 1
State Upper Legislative District 11
...
So, first I need to do a basic String sort, but then I need to sort by the number. The number to sort on should always trail, and may be 2 or 3 digits.
(Edit) My initial thought is to split the string on space, run StringUtils.isNumeric on the number portion, then sort. However, it seems a bit of a kludge to me.
Can anyone assist?
There is an article about this on Coding Horror. This is called natural sorting, where you effectively treat a group of digits as a single "character". See this question for some Java implementations of the idea.
Sorting for Humans : Natural Sort Order
The default sort functions in almost every programming language are poorly suited for human consumption. What do I mean by that? Well, consider the difference between sorting filenames in Windows explorer, and sorting those very same filenames via Array.Sort() code:
continued...
I wrote a variation on String.CompareTo that compares the length of numbers found in the two strings. When encounting two numbers of the same length the alphanumeric compare is resumed as normal. It also skips leading zeros.
public static int compareNatural(String a, String b) {
int la = a.length();
int lb = b.length();
int ka = 0;
int kb = 0;
while (true) {
if (ka == la)
return kb == lb ? 0 : -1;
if (kb == lb)
return 1;
if (a.charAt(ka) >= '0' && a.charAt(ka) <= '9' && b.charAt(kb) >= '0' && b.charAt(kb) <= '9') {
int na = 0;
int nb = 0;
while (ka < la && a.charAt(ka) == '0')
ka++;
while (ka + na < la && a.charAt(ka + na) >= '0' && a.charAt(ka + na) <= '9')
na++;
while (kb < lb && b.charAt(kb) == '0')
kb++;
while (kb + nb < lb && b.charAt(kb + nb) >= '0' && b.charAt(kb + nb) <= '9')
nb++;
if (na > nb)
return 1;
if (nb > na)
return -1;
if (ka == la)
return kb == lb ? 0 : -1;
if (kb == lb)
return 1;
}
if (a.charAt(ka) != b.charAt(kb))
return a.charAt(ka) - b.charAt(kb);
ka++;
kb++;
}
}
One way would be to use a simple regex to parse out the fields of interest in your comparator and then compare them manually. Here's an untested example:
private static final Pattern pattern = Pattern.compile("^State (Lower|Upper) Legislative District (\\d+)$");
public int compare(String a, String b) {
Matcher matcher1 = pattern.matcher(a);
Matcher matcher2 = pattern.matcher(b);
if( matcher1.matches() && matcher2.matches() ) {
//compare upper/lower
int upperLowerComparison = matcher1.group(1).compareTo(matcher2.group(1));
if ( upperLowerComparison != 0 ) {
return upperLowerComparison;
}
//number comparison
return Integer.valueOf(matcher1.group(2)).compareTo(Integer.valueOf(matcher2.group(2));
}
//...what to do if they don't match?
}
You have two options. The first one is to create a class having two fields - the name and the number. Of course first parse the name and numbers. Then in the comparator first compare the name and then the number. The second one is to do the parsing at place in the compare method. Choose which one is more appropriate to you.
Have a look at this implementation:
public static int naturalCompare(String a, String b, boolean ignoreCase) {
if (ignoreCase) {
a = a.toLowerCase();
b = b.toLowerCase();
}
int aLength = a.length();
int bLength = b.length();
int minSize = Math.min(aLength, bLength);
char aChar, bChar;
boolean aNumber, bNumber;
boolean asNumeric = false;
int lastNumericCompare = 0;
for (int i = 0; i < minSize; i++) {
aChar = a.charAt(i);
bChar = b.charAt(i);
aNumber = aChar >= '0' && aChar <= '9';
bNumber = bChar >= '0' && bChar <= '9';
if (asNumeric)
if (aNumber && bNumber) {
if (lastNumericCompare == 0)
lastNumericCompare = aChar - bChar;
} else if (aNumber)
return 1;
else if (bNumber)
return -1;
else if (lastNumericCompare == 0) {
if (aChar != bChar)
return aChar - bChar;
asNumeric = false;
} else
return lastNumericCompare;
else if (aNumber && bNumber) {
asNumeric = true;
if (lastNumericCompare == 0)
lastNumericCompare = aChar - bChar;
} else if (aChar != bChar)
return aChar - bChar;
}
if (asNumeric)
if (aLength > bLength && a.charAt(bLength) >= '0' && a.charAt(bLength) <= '9') // as number
return 1; // a has bigger size, thus b is smaller
else if (bLength > aLength && b.charAt(aLength) >= '0' && b.charAt(aLength) <= '9') // as number
return -1; // b has bigger size, thus a is smaller
else
return lastNumericCompare;
else
return aLength - bLength;
}
It should be fast, without any regular expressions or array manipulation, just a couple of flags and a lot of cases.
This should sort any combination of numbers inside strings and properly support numbers which are equal and move on.
I usually do this by prefixing zeros to the number and handle the whole entity as a string. then sort it.
See this:
public abstract class MyNumberComparator {
protected int doCompare(final String number1, final String number2) {
String strNumber1 = fillUpLeftWithZeros(number1, 30);
String strNumber2 = fillUpLeftWithZeros(number2, 30);
return strNumber1.toUpperCase().compareTo(strNumber2.toUpperCase());
}
}
A simple implementation would be like this one (this works with any string that ends with a number):
public class SplitComparator implements Comparator<String> {
static class Pair implements Comparable<Pair> {
private String name;
private Integer number;
public Pair(String value) {
value = value.trim();
this.name = value.substring( 0, value.lastIndexOf(" ") );
this.number = Integer.valueOf( value.substring( value.lastIndexOf(" ") + 1, value.length() ) );
}
#Override
public int compareTo( Pair right) {
int result = this.name.compareTo( right.name );
if ( result == 0 ) {
result = this.number.compareTo( right.number );
}
return result;
}
}
#Override
public int compare(String left, String right) {
return new Pair( left ).compareTo( new Pair( right ) );
}
public static void main( String ... args ) {
String[] values = { "State Lower Legislative District 1",
"State Lower Legislative District 11",
"State Upper Legislative District 1",
"State Upper Legislative District 11"};
SplitComparator comparator = new SplitComparator();
System.out.println( comparator.compare( values[1] , values[0]) );
System.out.println( comparator.compare( values[0] , values[1]) );
System.out.println( comparator.compare( values[0] , values[3]) );
}
}
Related
I have a method returning a list of String that need to be sorted. However, I'm running into the old String number sorting issue and was wondering if any one could assist with a Comparator implementation or point me in the direction of one.
The list is going to return something list this:
State Lower Legislative District 1
State Lower Legislative District 11
State Lower Legislative District 12
...
State Lower Legislative District 2
...
State Lower Legislative District 100
...
State Upper Legislative District 1
State Upper Legislative District 11
...
So, first I need to do a basic String sort, but then I need to sort by the number. The number to sort on should always trail, and may be 2 or 3 digits.
(Edit) My initial thought is to split the string on space, run StringUtils.isNumeric on the number portion, then sort. However, it seems a bit of a kludge to me.
Can anyone assist?
There is an article about this on Coding Horror. This is called natural sorting, where you effectively treat a group of digits as a single "character". See this question for some Java implementations of the idea.
Sorting for Humans : Natural Sort Order
The default sort functions in almost every programming language are poorly suited for human consumption. What do I mean by that? Well, consider the difference between sorting filenames in Windows explorer, and sorting those very same filenames via Array.Sort() code:
continued...
I wrote a variation on String.CompareTo that compares the length of numbers found in the two strings. When encounting two numbers of the same length the alphanumeric compare is resumed as normal. It also skips leading zeros.
public static int compareNatural(String a, String b) {
int la = a.length();
int lb = b.length();
int ka = 0;
int kb = 0;
while (true) {
if (ka == la)
return kb == lb ? 0 : -1;
if (kb == lb)
return 1;
if (a.charAt(ka) >= '0' && a.charAt(ka) <= '9' && b.charAt(kb) >= '0' && b.charAt(kb) <= '9') {
int na = 0;
int nb = 0;
while (ka < la && a.charAt(ka) == '0')
ka++;
while (ka + na < la && a.charAt(ka + na) >= '0' && a.charAt(ka + na) <= '9')
na++;
while (kb < lb && b.charAt(kb) == '0')
kb++;
while (kb + nb < lb && b.charAt(kb + nb) >= '0' && b.charAt(kb + nb) <= '9')
nb++;
if (na > nb)
return 1;
if (nb > na)
return -1;
if (ka == la)
return kb == lb ? 0 : -1;
if (kb == lb)
return 1;
}
if (a.charAt(ka) != b.charAt(kb))
return a.charAt(ka) - b.charAt(kb);
ka++;
kb++;
}
}
One way would be to use a simple regex to parse out the fields of interest in your comparator and then compare them manually. Here's an untested example:
private static final Pattern pattern = Pattern.compile("^State (Lower|Upper) Legislative District (\\d+)$");
public int compare(String a, String b) {
Matcher matcher1 = pattern.matcher(a);
Matcher matcher2 = pattern.matcher(b);
if( matcher1.matches() && matcher2.matches() ) {
//compare upper/lower
int upperLowerComparison = matcher1.group(1).compareTo(matcher2.group(1));
if ( upperLowerComparison != 0 ) {
return upperLowerComparison;
}
//number comparison
return Integer.valueOf(matcher1.group(2)).compareTo(Integer.valueOf(matcher2.group(2));
}
//...what to do if they don't match?
}
You have two options. The first one is to create a class having two fields - the name and the number. Of course first parse the name and numbers. Then in the comparator first compare the name and then the number. The second one is to do the parsing at place in the compare method. Choose which one is more appropriate to you.
Have a look at this implementation:
public static int naturalCompare(String a, String b, boolean ignoreCase) {
if (ignoreCase) {
a = a.toLowerCase();
b = b.toLowerCase();
}
int aLength = a.length();
int bLength = b.length();
int minSize = Math.min(aLength, bLength);
char aChar, bChar;
boolean aNumber, bNumber;
boolean asNumeric = false;
int lastNumericCompare = 0;
for (int i = 0; i < minSize; i++) {
aChar = a.charAt(i);
bChar = b.charAt(i);
aNumber = aChar >= '0' && aChar <= '9';
bNumber = bChar >= '0' && bChar <= '9';
if (asNumeric)
if (aNumber && bNumber) {
if (lastNumericCompare == 0)
lastNumericCompare = aChar - bChar;
} else if (aNumber)
return 1;
else if (bNumber)
return -1;
else if (lastNumericCompare == 0) {
if (aChar != bChar)
return aChar - bChar;
asNumeric = false;
} else
return lastNumericCompare;
else if (aNumber && bNumber) {
asNumeric = true;
if (lastNumericCompare == 0)
lastNumericCompare = aChar - bChar;
} else if (aChar != bChar)
return aChar - bChar;
}
if (asNumeric)
if (aLength > bLength && a.charAt(bLength) >= '0' && a.charAt(bLength) <= '9') // as number
return 1; // a has bigger size, thus b is smaller
else if (bLength > aLength && b.charAt(aLength) >= '0' && b.charAt(aLength) <= '9') // as number
return -1; // b has bigger size, thus a is smaller
else
return lastNumericCompare;
else
return aLength - bLength;
}
It should be fast, without any regular expressions or array manipulation, just a couple of flags and a lot of cases.
This should sort any combination of numbers inside strings and properly support numbers which are equal and move on.
I usually do this by prefixing zeros to the number and handle the whole entity as a string. then sort it.
See this:
public abstract class MyNumberComparator {
protected int doCompare(final String number1, final String number2) {
String strNumber1 = fillUpLeftWithZeros(number1, 30);
String strNumber2 = fillUpLeftWithZeros(number2, 30);
return strNumber1.toUpperCase().compareTo(strNumber2.toUpperCase());
}
}
A simple implementation would be like this one (this works with any string that ends with a number):
public class SplitComparator implements Comparator<String> {
static class Pair implements Comparable<Pair> {
private String name;
private Integer number;
public Pair(String value) {
value = value.trim();
this.name = value.substring( 0, value.lastIndexOf(" ") );
this.number = Integer.valueOf( value.substring( value.lastIndexOf(" ") + 1, value.length() ) );
}
#Override
public int compareTo( Pair right) {
int result = this.name.compareTo( right.name );
if ( result == 0 ) {
result = this.number.compareTo( right.number );
}
return result;
}
}
#Override
public int compare(String left, String right) {
return new Pair( left ).compareTo( new Pair( right ) );
}
public static void main( String ... args ) {
String[] values = { "State Lower Legislative District 1",
"State Lower Legislative District 11",
"State Upper Legislative District 1",
"State Upper Legislative District 11"};
SplitComparator comparator = new SplitComparator();
System.out.println( comparator.compare( values[1] , values[0]) );
System.out.println( comparator.compare( values[0] , values[1]) );
System.out.println( comparator.compare( values[0] , values[3]) );
}
}
I'm trying to implement a method that finds the highest index of common digit between two ints like so:
a = 2224 , b = 4222 the result would be index = 2, which means the index is in reverse. I managed to find the first common digit, but I'm having trouble finding the last common digit. This the method to find the first common digit:
private static int getLowestIndexWithSameDigit(int a, int b) {
if (a < 0 || b < 0)
throw new IllegalArgumentException("Ambos os argumentos devem ser positivos: " + a + " " + b);
else {
if (a % 10 == b % 10) {
return indice;
} else if (a / 10 != 0 && b / 10 != 0) {
indice++;
return getLowestIndexWithSameDigit(a / 10, b / 10);
} else {
return -1;
}
}
}
And the method to initialize index as 0 everytime getLowestIndexWithSameDigit is used:
private static void test_getLowestIndexWithSameDigit(int a, int b) {
try {
System.out.print("getLowestIndexWithSameDigit (" + a + ", " + b + ") = ");
indice = 0;
int res = getLowestIndexWithSameDigit(a, b);
System.out.println(res);
} catch (IllegalArgumentException e) {
System.out.println("Erro: " + e.getMessage());
}
}
I was trying to adapt this method in some way, but I don't think it's adaptable to find the last common digit. Any help is appreciated.
It might be simpler to convert the ints to Strings:
//you can overload method to support other primitives
private static int getHighestIndexWithSameDigit(int a, int b) {
String aS = String.valueOf(a);
String bS = String.valueOf(b);
return getHighestIndexWithSameDigit(aS, bS);
}
//helper method to set strat index
private static int getHighestIndexWithSameDigit(String aS, String bS) {
return getHighestIndexWithSameDigit(aS, bS, 1);
}
//recursively check first letter. First index is 1 - indicates first char from left
private static int getHighestIndexWithSameDigit(String aS, String bS, int index) {
int aLength = aS.length(), bLength = bS.length();
if((aLength == 0) || (bLength == 0)) { return -1;}
if(aS.charAt(0) == bS.charAt(0)) { return index; }
//remove first letters, update index
return getHighestIndexWithSameDigit(aS.substring(1, aLength),
bS.substring(1, bLength), ++index);
}
Test by System.out.println(getHighestIndexWithSameDigit(2224 , 4222) );
Edit:
The code posted assumed that the first index is 1 (1 base), where index of value 1 indicates the first digit from left.
If you meant to use 0 base index, counting from right two of the method should change slightly :
//helper method to set start index.
private static int getHighestIndexWithSameDigit(String aS, String bS) {
return getHighestIndexWithSameDigit(aS, bS, aS.length()-1);
}
//recursively check first letter. Index 0 - indicates last char
private static int getHighestIndexWithSameDigit(String aS, String bS, int index) {
System.out.println(aS +" "+ bS);
int aLength = aS.length(), bLength = bS.length();
if((aLength == 0) || (bLength == 0)) { return -1;}
if(aS.charAt(0) == bS.charAt(0)) { return index; }
return getHighestIndexWithSameDigit(aS.substring(1, aLength),
bS.substring(1, bLength), --index);
}
Getting the lowest or the highest should only be a matter of when you stop the loop, no?
The slightly rewritten version of your code below did the trick for me at least.
private static int getDatIndex(int a, int b, boolean getDatLow) {
int indice = -1;
int index = 0;
while (a/10 != 0 && b/10 != 0) {
if (a % 10 == b % 10) {
indice = index;
// If you want the lowest common digit index: stop the loop.
if (getDatLow) {
break;
}
}
a = a/10;
b = b/10;
index++;
}
return indice;
}
My assignment is to create a recursive method makeDecimal that when passed a number (that is represented by a String) and its base, converts the number to base 10. You will need to use the method Integer.parseInt(str). (Hint: use substrings.) This method takes a String and returns the integer form of it.
For example, Integer.parseInt("21"); will return the int 21.
Here are some examples of how makeDecimal works:
makeDecimal("11", 2) will return 3.
makeDecimal("100", 4) will return 16.
Here was my attempt at it:
public static double makeDecimal(String number, int base){
int len = number.length();
double f = 0;
if(len <= 0)
return 0;
else{
makeDecimal(number,base);
double temp = Integer.parseInt(number.substring(len - 1, len + 1));
f = f + temp * Math.pow(3, len-1);
}
len--;
return f;
}
However, I get an "overflow error", and I don't know if it even is written correctly.
You are recursing with exactly the same arguments that were passed in. As a result, the call will itself recurse the same way, until the stack overflows. That's not how recursion is supposed to work. Instead, you need to figure out how to do one piece of the problem in the current call and then recurse to do a smaller problem.
In your code, it's not even clear what logic you are using. (What's the point of computing 3len-1?) Try this instead:
If the input string has length 0, the answer is 0 (that part you got right)
Otherwise, take the last digit and parse it in the current base. Then the answer is that value plus base times the value of everything up to but not including the last digit of the input. (Hint: this is a good place to use recursion.)
You should be able to translate that description into the appropriate method calls and use of substring().
Oh, one other thing: there's no reason to be using double values here. Just stick with int variables throughout. You won't be needing Math.pow().
Here is simplest solution using recursion, substring and Integer.parseInt:
public int makeDecimal(String value, int base) {
// exit from recursion
if (value.length() < 1)
return 0;
//parsing last character of string
int charValue = Integer.parseInt(value.substring(value.length() - 1), base);
//calling recursion for string without last character
return makeDecimal(value.substring(0, value.length() - 1), base) * base + charValue;
}
Here's my solution after writing the prototype in Python (if you are interested, I can also include the Python source code):
import java.util.HashMap;
import java.util.Map;
public class MakeDecimal {
public static final Map<Character, Integer> alphabet = buildAlphabetTable();
public static void main(String[] args) {
// System.out.println(alphabet);
System.out.println(makeDecimal("af10bb1", 16));
}
// pos refers to the position of the character in the string.
// For example, if you have the following binary string 100
// then 1 at the left is at position 2,
// the 0 in the middle is at position 1,
// and the right most 0 is at position 0
// (you start counting from the right side).
// In general, you would convert that string in the following way:
// 2^2 * 1 + 2^1 * 0 + 2^0 * 0 = 4
// If the base was n, then you would have
// first * n^{pos + "length of string - 1"} + ... + penultimate * n^{pos + 1} + last * n^{pos}
// Note that pos starts from 0.
private static int makeDecimal(String s, int base, int pos) {
if (s.length() == 0) {
return 0;
} else {
int last = (int) Math.pow(base, pos) * alphabet.get(s.charAt(s.length() - 1));
return makeDecimal(s.substring(0, s.length() - 1), base, pos + 1) + last;
}
}
public static int makeDecimal(String s, int base) {
if (s.length() == 0) {
return 0;
}
if (base < 2 || base > 36) {
throw new IllegalArgumentException("not base >= 2 and base <= 36");
}
return makeDecimal(s.toLowerCase(), base, 0);
}
// Creates a table that maps characters to their decimal value
// the characters can be also '0' or '2' (or any other character number)
// or they can be a character of the English alphabet
private static Map<Character, Integer> buildAlphabetTable() {
Map<Character, Integer> a = new HashMap<>(36);
int i = 0;
for (char c = '0'; c <= '9'; c++, i++) {
a.put(c, i);
}
for (char c = 'a'; c <= 'z'; c++, i++) {
a.put(c, i);
}
return a;
}
}
My solution is based on the following post, which you should definitely read to refresh your ideas on how to convert between bases.
http://www.purplemath.com/modules/numbbase.htm
It does not accept bases that are smaller than 2 or greater than 36. It handles also when you pass English characters in upper case.
Edit: At first I've misted that recursion is obligated for this solution so my original answer without it could me four below.
Here is solution with recursion and without substring and Math.pow:
public double makeDecimal(String value, int base) {
makeDecimal(value, base, value.length() - 1);
}
public double makeDecimal(String value, int base, int index) {
double result = 0;
if (index < 0)
return result;
double charValue = 0;
char currentChar = values.get(Character.toUpperCase(value.charAt(index));
if (currentChar >= 0 && currentChar <= '9') {
charValue = currentChar - '0';
} else if (currentChar >= 'A' && currentChar <= 'Z') {
charValue = currentChar - 'A';
} else {
throw new InvalidValueException("Unsupported character '" + currentChar + "'.");
}
if (charValue >= base) {
throw new InvalidValueException("Wrong character '" + currentChar + "' for base '" base + "'.");
}
return makeDecimal(value, base, index - 1)) * base + charValue;
}
Original Answer: Something like this should work for any base starting from 2 till 36:
private Map<Character, Integer> getCharValues(int base)
{
Map<Character, Integer> values = new HashMap<Character, Integer>();
for (int i = 0; i < base; i++){
if (i < 10) {
values.put('0' + i, i);
} else if (i < 36) {
values.put('A' + i - 10, i);
} else {
throw new InvalidValueException("Base '" + base + "' not supported");
}
}
return values;
}
public double makeDecimal(String value, int base)
{
Map<Character, Integer> charValues = getCharValues(base);
double result = 0;
for (int i = 0; i < value.length(); i++){
result = result * base + charValues.get(Character.toUpperCase(Character.toUpperCase(value.charAt(i))));
}
return result;
}
If you need base more then 36 you can extend char set in method getCharValues. Also it will be a good idea do not create HasMap every time but just store it for maximum base and throw exception if char value exceed given base.
I would like to know what it causing the error of "bad operand types for binary operator '>'" down below I have the codes for both my Hand and Card classes. I've also specified the lines that are causing the error.
Thanks for the help. This is for a BlackJack project.
Hand Class
import java.util.Vector;
public class Hand {
private Vector hand; // The cards in the hand.
public Hand() {
// Create a Hand object that is initially empty.
hand = new Vector();
}
public void clear() {
// Discard all the cards from the hand.
hand.removeAllElements();
}
public void addCard(PlayingCard c) {
// Add the card c to the hand. c should be non-null. (If c is
// null, nothing is added to the hand.)
if (c != null)
hand.addElement(c);
}
public void removeCard(PlayingCard c) {
// If the specified card is in the hand, it is removed.
hand.removeElement(c);
}
public void removeCard(int position) {
// If the specified position is a valid position in the hand,
// then the card in that position is removed.
if (position >= 0 && position < hand.size())
hand.removeElementAt(position);
}
public int getCardCount() {
// Return the number of cards in the hand.
return hand.size();
}
public PlayingCard getCard(int position) {
// Get the card from the hand in given position, where positions
// are numbered starting from 0. If the specified position is
// not the position number of a card in the hand, then null
// is returned.
if (position >= 0 && position < hand.size())
return (PlayingCard)hand.elementAt(position);
else
return null;
}
public void sortBySuit() {
// Sorts the cards in the hand so that cards of the same suit are
// grouped together, and within a suit the cards are sorted by value.
// Note that aces are considered to have the lowest value, 1.
Vector newHand = new Vector();
while (hand.size() > 0) {
int pos = 0; // Position of minimal card.
PlayingCard c = (PlayingCard)hand.elementAt(0); // Minumal card.
for (int i = 1; i < hand.size(); i++) {
PlayingCard c1 = (PlayingCard)hand.elementAt(i);
*if ( c1.getCardFace() > c.getCardFace() ||
(c1.getCardFace().equals(c.getCardFace()) && c1.getFaceValue() < c.getFaceValue()) ) {*
pos = i;
c = c1;
}
}
hand.removeElementAt(pos);
newHand.addElement(c);
}
hand = newHand;
}
public void sortByValue() {
// Sorts the cards in the hand so that cards of the same value are
// grouped together. Cards with the same value are sorted by suit.
// Note that aces are considered to have the lowest value, 1.
Vector newHand = new Vector();
while (hand.size() > 0) {
int pos = 0; // Position of minimal card.
PlayingCard c = (PlayingCard)hand.elementAt(0); // Minumal card.
for (int i = 1; i < hand.size(); i++) {
PlayingCard c1 = (PlayingCard)hand.elementAt(i);
*if ( c1.getFaceValue() < c.getFaceValue() ||
(c1.getFaceValue() == c.getFaceValue() && c1.getCardFace() > c.getCardFace()) ) {*
pos = i;
c = c1;
}
}
hand.removeElementAt(pos);
newHand.addElement(c);
}
hand = newHand;
}
}
The error is in the hand class in the lines
if ( c1.getCardFace() > c.getCardFace() ||
(c1.getCardFace().equals(c.getCardFace()) && c1.getFaceValue() < c.getFaceValue()) ) {
and
if ( c1.getFaceValue() < c.getFaceValue() ||
(c1.getFaceValue() == c.getFaceValue() && c1.getCardFace() > c.getCardFace()) ) {
This is the Card Class
public class PlayingCard
{
// Instance Data - all things common to all cards
private String cardFace; // king, q, j, 10 - 2, A
private int faceValue; // numberic value of the card
private char cardSuit; // hold suit of the card
private char suits[] = {(char)(003), (char)(004), (char)(005), (char)(006)};
// Constructor
public PlayingCard(int value, int suit)
{
faceValue = value;
setFace();
setSuit(suit);
}
// helper setFace()
public void setFace()
{
switch(faceValue)
{
case 1:
cardFace = "A";
faceValue = 14;
break;
case 11:
cardFace = "J";
break;
case 12:
cardFace = "Q";
break;
case 0:
cardFace = "K";
faceValue = 13;
break;
default:
cardFace = ("" + faceValue);
}
}
public void setSuit(int suit) // suit num between 0 and 3
{
cardSuit = suits[suit];
}
// other helpers
public int getFaceValue()
{
return faceValue;
}
public String getCardFace()
{
return cardFace;
}
public String toString()
{
return (cardFace + cardSuit);
}
}
getCardFace() returns a String. < and > operators exist only for numeric types.
You can use c1.getCardFace().compareTo(c.getCardFace()) < 0 or c1.getCardFace().compareTo(c.getCardFace()) > 0 instead, to compare the Strings according to their natural ordering.
if ( c1.getCardFace() > c.getCardFace() ||
(c1.getCardFace().equals(c.getCardFace()) && c1.getFaceValue() < c.getFaceValue()) ) {
would become
if ( c1.getCardFace().compareTo(c.getCardFace()) > 0 ||
(c1.getCardFace().equals(c.getCardFace()) && c1.getFaceValue() < c.getFaceValue()) ) {
and
if ( c1.getFaceValue() < c.getFaceValue() ||
(c1.getFaceValue() == c.getFaceValue() && c1.getCardFace() > c.getCardFace()) ) {
would become
if ( c1.getFaceValue() < c.getFaceValue() ||
(c1.getFaceValue() == c.getFaceValue() && c1.getCardFace().compareTo(c.getCardFace()) > 0) ) {
getCardFace() is returning String value but you can't use < , > , <= or >= for comparing String.
Don't use these operators <,> and == to compare two Strings, instead use compareTo method.
From Javadoc:
public int compareTo(String anotherString)
Compares two strings lexicographically. The comparison is based on the
Unicode value of each character in the strings. The character sequence
represented by this String object is compared lexicographically to the
character sequence represented by the argument string. The result is a
negative integer if this String object lexicographically precedes the
argument string. The result is a positive integer if this String
object lexicographically follows the argument string. The result is
zero if the strings are equal; compareTo returns 0 exactly when the
equals(Object) method would return true.
An example of comparing two Strings
String s1="example1", s2="example2";
if ( s1.compareTo(s2) > 0 )
System.out.println("First string is greater than second.");
else if ( s1.compareTo(s2) < 0 )
System.out.println("First string is smaller than second.");
else
System.out.println("Both strings are equal.");
Note: The compareTo method is case sensitive i.e "java" and "Java" are two different strings if you use compareTo method. String "java" is greater than "Java" as ASCII value of 'j' is greater than 'J'. If you wish to compare strings but ignoring the case then use compareToIgnoreCase method.
public int compareToIgnoreCase(String str)
Compares two strings lexicographically, ignoring case differences.
This method returns an integer whose sign is that of calling compareTo
with normalized versions of the strings where case differences have
been eliminated by calling
Character.toLowerCase(Character.toUpperCase(character)) on each
character.
Given a string, does "xyz" appear in the middle of the string? To
define middle, we'll say that the number of chars to the left and
right of the "xyz" must differ by at most one. This problem is harder
than it looks.
My solution works without the second last line except for one condition: if str="xyx"
Is it possible to modify the for loop to take this into account...I'm struggling with understanding why it doesn't.
My solution does work I'm just trying to get a better understanding of what I'm doing. I know I could add it into the first if statement but I want to know why it doesn't work without it.
public boolean xyzMiddle(String str) {
for (int i=0;i<str.length()-3;i++) {
if (str.substring(i,i+3).equals("xyz")) {
String front =str.substring(0,i);
String end = str.substring(i+3);
int a =Math.abs(front.length() -end.length());
if (a<=1) return true;
}
}
if (str.equals("xyz")) return true;
return false;
I think i remember this question - it's this question from Codingbat, I believe. Excellent web site, learned a lot from that site back when I started programming. There's absolutely no reason to use a loop, though.
public boolean xyzMiddle(String str) {
boolean result = false;
int i = str.length()/2 -1;
if (str.length() >= 3 && (str.substring(i, i+3).equals("xyz") || (str.length()%2 == 0 && str.substring(i-1, i+2).equals("xyz")) )) {
result = true;
}
return result;
}
So, let's walk through this and why it works. Firstly, str.length() >= 3, because if the string isn't at least as long as "xyz", there's no way it can contain "xyz".
There are two main cases to this problem, we need to think of. The string can have an even or an uneven length. In the uneven case, it's easy:
The Uneven case
AAAxyzAAA // length = 9
012345678 // the indexes
^ // that's the middle, which can be calculated by length/2
// (since this is an integer divison, we disregard whatever comes after the decimal point)
So to get the start of the xyz-substring, we simply subtract one from this number - which is exactly what i is doing:
AAAxyzAAA // length = 9
012345678 // the indexes
i // i = length/2-1 = 3
So if str.substring(i, i+3) is xyz, we can return true!
The Even Case
Now, this can be a bit more tricky, since there is no true "middle" of the string. In fact, two indexes could be called the middle, so we have two sub-cases:
AAAAAAAA // length = 8
01234567 // the indexes
^^ // Which one is the true middle character?
In fact, the middle would be between index 3 and 4. However, we are performing integer divisions, length/2 is always the largest (rightmost) of the two possible "middles". And since we calculate i using the middle, the same as in the uneven case applies - str.substring(i, i+3) could be considered the middle part of the string.
AAAxyzAA
01234567
^^^ // str.substring(i, i+3)
i
But suppose our string was AAxyzAAA - that could also be considered the middle part of the string. So we need to move our substring check "to the left" - so we subtract 1 from it.
AAxyzAAA
01234567
^^^ // str.substring(i-1, i+2)
i // i is still at "the old" location
So is it even or not?
To check whether the string is even or uneven, we use the modulo operator, %. The easiest way to think of what it does is "what would be left over after i divided with this number?". So 3 % 2 would be 1. In our case, we want to make sure that the number is divisible by 2 with nothing left over - because that means it was an even number. Therefore, we need to check whether str.length() % 2 == 0 before we make our "move-to-the-left" check. If not, we could risk going out of bounds on the string. If the string was 3 characters long, and we moved one to the left... we would check the substring starting at index -1, and that doesn't make a lot of sense.
Put it all together, and there you go!
I'd say something as simple as:
public void test() {
test("Hello", "ll");
test("Hello", "He");
test("Hello", "el");
test("Hello", "lo");
test("Hello", "Hell");
test("Hello", "ello");
test("Hello", "Hello");
test("Hell", "He");
test("Hell", "el");
test("Hell", "ll");
}
private void test(String s, String p) {
System.out.println(p + (inMiddle(s, p) ? " in " : " not in ") + s);
}
// Is the pattern in the middle of the string.
public static boolean inMiddle(String s, String p) {
int d = s.length() - p.length();
return at(s, p, d / 2) || ((d & 1) == 1 && at(s, p, (d / 2) + 1));
}
private static boolean at(String s, String p, int i) {
return i >= 0 && i < s.length() && s.substring(i).startsWith(p);
}
Results look correct to me:
ll in Hello
He not in Hello
el in Hello
lo not in Hello
Hell in Hello
ello in Hello
Hello in Hello
He not in Hell
el in Hell
ll not in Hell
I have confirmed that this matches Tobias' solution exactly when p = "xyz".
public boolean xyzMiddle(String str) {
int len = str.length();
if (len < 3){ return false; }
int even = (len+1)%2;
int mid = len/2;
return str.substring(mid-1-even, mid+2).contains("xyz");
}
The simplest I could come up with:
public boolean xyzMiddle(String str) {
str = " " + str + " ";
int even = (str.length()+1)%2;
int mid = (str.length())/2;
str = str.substring(mid-1-even, mid+2);
return str.contains("xyz");
}
public boolean xyzMiddle(String str) {
int len = str.length();
if(len < 3) {return false;}
if(len==3 && str.equals("xyz")) {return true;}
int index = middleIndex(str);
String left = str.substring(0,index) ;
String right= str.substring(index+3) ;
//Return based on the difference by at most 1
return (Math.abs(left.length()-right.length()) <=1);
}
public int middleIndex(String str) {
int middleLen = (str.length())/2;
int index= 0;
//Find an index that could be in the middle of the string with
// "xyz"
for(int i=middleLen-2; i < middleLen; i++ ) {
if(str.substring(i, i+3).equals("xyz") ) {
index= i;
}
}
return index;
}
public boolean xyzMiddle(String str) {
if (str.length() >= 3)
{
// if odd
if (str.length() % 2 == 1)
{
//axyzb
//01234
//length = 5; 5 is odd.
//length / 2 = 2;
//2 minus 1 = 1
//1 is where xyz starts
//aaxyzbb
//0123456
//length = 7; 7 is odd.
//length / 2 = 3;
//3 minus 1 = 2
//2 is where xyz starts.
//....
//This pattern works with all odd numbers.
if (str.substring((str.length() / 2) - 1, ((str.length() / 2) - 1) + 3).equals("xyz"))
{
return true;
}
else
{
return false;
}
}
//if even
else
{
//for evens that occur with a larger amount before "xyz" than after
//axyz
//0123
//length = 4; 4 is even;
//4 - 1 = 3;
//3 / 2 = 1
//1 is where xyz starts.
//aaxyzb
//012345
//length = 6; 6 is even;
//6 - 1 = 5;
//5 / 2 = 2
//2 is where xyz starts.
//...
//This pattern works for all even numbers where there is a larger amount of characters before the xyz.
if (str.substring((str.length() - 1) / 2, ((str.length() - 1) / 2) + 3).equals("xyz"))
{
return true;
}
//For the cases where there are more characters after "xyz" than before.
//xyzb
//0123
//length = 4; 4 is even;
//4 - 1 = 3;
//3 / 2 = 1
//1 - 1 = 0;
//xyz starts at 0;
//axyzbb
//012345
//length = 6; 6 is even;
//6 - 1 = 5;
//5 / 2 = 3;
//2 - 1 = 1;
//xyz starts at 1;
//...
//The pattern continues onwards for all cases where there are more characters after xyz than before.
else if (str.substring((((str.length() - 1) / 2) - 1), (((str.length() - 1) / 2) -1) + 3).equals("xyz"))
{
return true;
}
//If there is no instance of xyz in these areas.
else
{
return false;
}
}
}
// If our string is less than 3 in length.
else
{
return false;
}
}
public boolean xyzMiddle(String str) {
return str.length()>2 && str.length()%2==1 && str.substring((str.length()-1)/2-1,(str.length()/2+1)+1).contains("xyz") || str.length()>2 && str.length()%2==0 && (str.substring(str.length()/2-2,str.length()/2+1).contains("xyz") || str.substring(str.length()/2-1,str.length()/2+2).contains("xyz"));
}
public boolean xyzMiddle(String str) {
int index = str.indexOf("x");
if(index < 0)
return false;
while(index >=0 && (index+3 <= str.length()) ){
if(str.substring(index,index+3).equals("xyz") && Math.abs(str.substring(0,index).length() - str.substring(index+3).length()) <=1)
return true;
index = str.indexOf("x",index+1);
}
return false;
}