Reputation: 45
I'm trying to make a program that counts all instances of a letter in a user-inputted phrase. For example, the phrase "Hello" will output "h: 1", "e: 1", "l: 2", "o: 1"
.
I've figured out how to find and count all the letters, except "a"
. When I compare "a"
to itself more than once, it returns a different value each time (so the phrase "aaaa"
would give me a: 1, b: 1, c: 1, d: 1
)
Here's the code I'm using:
int[] countLetter = new int[26];
char[] alphabet = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
phrase = phrase.toLowerCase();
String a = "a";
for (int i = 0; i<phrase.length(); i++ ) {
char count = phrase.charAt(i);
if (Character.isLetter(count))
countLetter[a.compareTo(phrase.substring(i))*-1] = countLetter[a.compareTo(phrase.substring(i))*-1] + 1;
}
for (int i = 0; i<26; i++) {
if (countLetter[i]>0) {
System.out.println(alphabet[i] + ": " + countLetter[i]);
}
}
I'm not sure what I'm doing wrong, so how can I get "a" to stay the same?
Upvotes: 3
Views: 255
Reputation: 3718
if You are allowed to use lambda Your program gets significantly shorter:
String phrase = "hello";
creating the map:
Map<Character,Long> counts = phrase.chars().boxed().collect(
groupingBy( t -> (char)(int)t, counting() ) );
collecting the counts of the mapped characters:
StringJoiner joiner = new StringJoiner( ", " );
for( char c = 'a'; c <= 'z'; c++ )
if( counts.containsKey( c ) )
joiner.add( c + ": " + counts.get( c ) );
printing the collected counts:
System.out.println( joiner );
gets: e: 1, h: 1, l: 2, o: 1
Upvotes: 0
Reputation: 1665
The error in your code was because of the way compareTo returns its result. As Mureinik said 'you should never rely on the exact return value of compareTo'. I will try to explain what went wrong.
If two strings are different, then either they have different characters at some index that is a valid index for both strings, or their lengths are different, or both. If they have different characters at one or more index positions, let k be the smallest such index. The compareTo method then returns
return str1.charAt(k) - str2.charAt(k);
If there is no index position at which they differ, then the shorter string lexicographically precedes the longer string. In this case, compareTo returns the difference of the lengths of the strings.
return str1.length() - str2.length();
When the phrase String is "aaaaa", for different values of from 0-4 this happens,
//when i = 0
a.compareTo(phrase.substring(i))
//"a".compareTo("aaaaa")
//length of "a" - length of "aaaaaa"
//which is -4
On multiplying it with -1 like you did it, -4 changed to 4 which is the index of letter 'e'. You can check this for other values of i and you will understand that is how you got d, c, b and a counter to 1.
I hope I helped in finding your error. Do check this link for more details on compareTo() https://www.w3resource.com/java-tutorial/string/string_compareto.php
Upvotes: 1
Reputation: 119
This is because compareTo
compare the entire String so if the String contains successively the same character the result will change. In your code you do compareTo
with phrase.substring(i)
so you send an entire String.
If you want to use compareTo
replace phrase.substring(i)
by phrase.charAt(i) + ""
.
But in reality you should never base your code on the returned values of compareTo
. Instead use the character itself because a character is a number.
Upvotes: 0
Reputation: 1427
I suggest a different approach, using a Map could be a better way to solve this problem, a map can store 2 things <key, value> per entry, so in the key you can store the character of the phrase and the number of instances of the character as the value, here is an example with comments:
public static void main(String[] args) {
String phrase = "hello";
HashMap<Character, Integer> map = new HashMap<Character, Integer>(); // Define a map, key type character, value type integer
phrase = phrase.toLowerCase();
for (int i = 0; i<phrase.length(); i++ ) { // iterate phrase
char c = phrase.charAt(i); // get current character
if(map.containsKey(c)) // if key already exists in the map
map.put(c, map.get(c) + 1); // increment the current value by 1 for this character
else // if key doesn't exists in the map yet
map.put(c, 1); // add the character with a value of 1
}
for (Map.Entry<Character, Integer> entry : map.entrySet()) { // iterate map
System.out.println(entry.getKey() + ": " + entry.getValue()); // print key and value
}
}
Input: "hello"
Output:
e: 1
h: 1
l: 2
o: 1
Input: "aaaa"
Output:
a: 4
Upvotes: 0
Reputation: 311188
You should never rely on the exact return value of compareTo
, unless it's 0
. If you want to convert the characters a
, b
, c
etc to indexes of an array, the best approach is to find the difference between them an a
. This way a
is translated to 0
, b
to 1
, etc:
for (int i = 0; i < phrase.length(); i++) {
char ch = phrase.charAt(i);
if (Character.isLetter(ch)) {
countLetter[ch - 'a']++;
}
}
Upvotes: 1