How do I assign a number to a letter in Java - java

I am working on a rather unique coding situation in Java. The purpose of the program I am trying to write is to take an Amateur Radio call sign, convert the letters in the call sign into numbers as defined by a list or other structure, treat the number as its face integer value, and run these numbers through several mathematical operations to output a unique "User Code" at the end. The length of characters, as well as the number itself, will vary from user to user based on their call sign, which is fine. The biggest obstacle I have encountered is that I do not want the letters to be assigned values in a 1-26 or 0-25 type pattern. I will post my code in a moment to show you. For the moment, the end use of this User Code is unimportant to this example, but suffice it to say that since I will be the only one using this particular code example I am not very concerned with doing validity checks or the like as I will ensure the integrity of the input data manually. With this being said, I do have a working solution which I will post here, but my question is not "it doesn't work" because it does work, my problem is that it is, in my opinion, bloated, and something tells me it could be cut down considerably. Here is the code, and following are some alternatives I considered but rejected:
import java.util.*;
import java.io.*;
public class UserCode
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
int baseNumber = 0;
int finalNumber;
String callSign;
System.out.println("Enter CallSign for Code Generation: ");
callSign = in.nextLine();
String s = callSign.toUpperCase();
for (int i = 0; i < s.length(); i++)
{
char c = s.charAt(i);
if (Character.isDigit(c))
{
int l = Character.getNumericValue(c);
baseNumber = baseNumber + l;
}
else if (Character.isLetter(c))
{
int letNum = 0;
switch (c)
{
case 'A':
letNum = 23;
break;
case 'B':
letNum = 17;
break;
case 'C':
letNum = 5;
break;
case 'D':
letNum = 11;
break;
case 'E':
letNum = 34;
break;
case 'F':
letNum = 18;
break;
case 'G':
letNum = 13;
break;
case 'H':
letNum = 31;
break;
case 'I':
letNum = 27;
break;
case 'J':
letNum = 25;
break;
case 'K':
letNum = 7;
break;
case 'L':
letNum = 25;
break;
case 'M':
letNum = 33;
break;
case 'N':
letNum = 26;
break;
case 'O':
letNum = 28;
break;
case 'P':
letNum = 16;
break;
case 'Q':
letNum = 14;
break;
case 'R':
letNum = 2;
break;
case 'S':
letNum = 4;
break;
case 'T':
letNum = 6;
break;
case 'U':
letNum = 8;
break;
case 'V':
letNum = 10;
break;
case 'W':
letNum = 37;
break;
case 'X':
letNum = 12;
break;
case 'Y':
letNum = 3;
break;
case 'Z':
letNum = 1;
break;
default:
System.out.println("Call Contains a bad character. Try again. \n");
}
baseNumber = baseNumber + letNum;
}
}
System.out.println("\n");
String baseStr = Integer.toString(baseNumber);
System.out.println("The Base number is: " + baseStr + "\n");
int sMod = baseNumber%7;
String sModStr = Integer.toString(sMod);
System.out.println("The Check Digit is: " + sModStr + "\n");
String combine = baseStr + sModStr;
int nextOp = Integer.parseInt(combine);
finalNumber = nextOp * nextOp;
String finalStr = Integer.toString(finalNumber);
System.out.println("The User Code is: " + finalStr + "\n");
}
}
Okay, as I said this code works, but it is long. I had considered a few alternatives, none of which will really work. The first was Enum, but that obviously is outside my parameters as it produces a 1-26 incremented pattern. There are several variations of this using various for{} loops but the result there is the same. I even considered a new HashMap and map.put statements, but that's only marginally shorter and it would seem to me that creating a hashmap would, in the end, actually use more memory than my current solution. As you can see, I did not .split or use .toCharArray() since these created separate entities in memory that I did not need. All the information I needed was already contained in the string itself, except for my chosen numerical values. Finally, I could have created another class file with this assignment code, but the result is the same: the length itself hasn't been changed it's been spread over two files (and actually increased in calling the method).
Having put this out here, can anyone see any way I can shorten this code, particularly in the area of the switch{} block and still retain the same result with the same numerical values? I would be highly interested in any suggestions that can be made in this regard. BTW I didn't mention this but this is not a school assignment, this is a personal project, though my level of Java knowledge is about at that of a beginner taking my first Java class LOL. Thanks

if you want to stick with this logic then use a HashMap<Character, Integer> and setup only once the values like: map.put('Z', 1); and so on, then u don't need the switch. You can get the values by using: map.get('Z'); which will return every time 1
it is easy to implement and easy to change the code if needed

Create an array of corresponding integers:
int[] letnums = {23, 17, 5, ... };
char c = s.charAt(i);
if (Character.isLetter(c)) {
int letnum = letnums(c);
...
}

Related

JFugue note changing not working properly

I'm trying to transpose a note, but the result it returns is not what it should be. Could you please check my code and tell me where I am wrong?
public int changeTone(String chord) {
int changeTone = 0;
switch(chord) {
case "I":
changeTone = 0;
break;
case "II":
changeTone = 1;
break;
case "III":
changeTone = 2;
break;
case "IV":
changeTone = 3;
break;
case "V":
changeTone = 4;
break;
case "VI":
changeTone = 5;
break;
case "VII":
changeTone = 6;
break;
case "i":
changeTone = 0;
break;
case "ii":
changeTone = 2;
break;
case "iii":
changeTone = 4;
break;
case "iv":
changeTone = 5;
break;
case "v":
canviDeTo = 7;
break;
case "vi":
changeTone = 9;
break;
case "vii":
changeTone = 11;
break;
default:
System.out.println("Vaya");
break;
}
return changeTone ;
}
public String getChord(int interval) {
String chord = "";
switch(interval) {
case "0":
chord = "C";
break;
case "2":
chord = "D";
break;
case "4":
chord = "E";
break;
case "5":
chord = "F";
break;
case "7":
chord = "G";
break;
case "9":
chord = "A";
break;
case "11":
chord = "B";
}
return chord;
}
public String WriteChord(String chords, String tone) {
String finalChord;
String[] chordArray = chords.split("-");
for(int i=0; i < chordArray.length; i++){
String chord = chordArray[i];
int interval = changeTone(chord);
chord = getChord(interval);
Note note = new Note(chord);
finalChord += note.changeValue(interval).toString() += "-";
}
return finalChord;
}
OK, so what this tries to do is to change the value of a chord given a chord progession with intervals. Like I-III-IV-iv. The user would choose a tone (the tonical, I note) and the chord would be changed taking the note as a reference. So, for example, running the code should do the following:
The user chooses a tone, say "E".
The code generates a chord progression, say "I-III-IV-iv".
The code gets the interval between I and III, I and IV, and I and iv.
The initial note, "E", changes its value with the interval.
The expected output is:
E-G#-A#-A#
The actual output is:
C-G#-G#-Bb
Why doesn't this work? I have simplified my code, so if you need a bit more let me know! Thanks in advance.
Edit: I have corrected the code and added the expected/gotten output.
JFugue already has support for intervals and chord progressions. I hope the following code satisfies your needs:
import org.jfugue.theory.ChordProgression;
public static void main(String[] args) {
ChordProgression cp = new ChordProgression("I-III-IV-iv").setKey("E");
System.out.println(cp);
}
The output from this code is:
E4MAJ G#4MAJ A4MAJ A4MIN
You can play this directly in a Player:
Player player = new Player(); // Don't forget to import org.jfugue.player.Player
player.play(cp);
If that's playing a little too fast for you because of the default duration of a quarter note, you can play each chord as something longer, say a whole note:
player.play(cp.eachChordAs("$!w"));
If you just want the roots, which in this case are [E, G#, A, A] (if you see an error with my music theory, please let me know), you can use:
cp.eachChordAs("$0");
There are several Chord Progression examples at http://www.jfugue.org/examples.html

Having trouble creating a bag of objects - Scrabble word search

I have to create a scrabble word search for my data structures class. I haven't reached the actual search yet. First, I need to create a bag of scrabble tiles. However, I keep getting errors when trying to add ScrabbleTile objects to my bag.
I have four classes: ScrabbleTile, ScrabbleBag, ScrabbleHand, and WordFinder.
Here is ScrabbleTile:
public class ScrabbleTile {
private char letter;
private int points;
ScrabbleTile (char letter)
{
this.letter = letter;
switch (letter)
{
case '_':
points = 0;
case 'e':
case 'a':
case 'i':
case 'o':
case 'n':
case 'r':
case 't':
case 'l':
case 's':
case 'u':
points = 1; break;
case 'd':
case 'g':
points = 2; break;
case 'b':
case 'c':
case 'm':
case 'p':
points = 3; break;
case 'f':
case 'h':
case 'v':
case 'w':
case 'y':
points = 4; break;
case 'k':
points = 5; break;
case 'j':
case 'x':
points = 8; break;
case 'q':
case 'z':
points = 10; break;
default: System.out.println("Incorrect character. Please enter a lowercase letter, a-z.");
break;
}
}
public char getLetter()
{
return letter;
}
public int getPoints()
{
return points;
}
}
Here is my ScrabbleBag class:
import DSLib.*;
public class ScrabbleBag {
private BagADT<ScrabbleTile> letterBag;
ScrabbleBag()
{
letterBag = new Bag<>();
for (int i = 0; i < 12; i++) {letterBag.add(ScrabbleTile('e'));}
}
}
In the ScrabbleBag constructor, I'm trying to add the correct number of each letter tile, starting with "e". Netbeans had a few suggestions which I tried, but then it was telling me the line syntax was wrong, after creating a few more instance variables in the ScrabbleBag class. How can I properly add ScrabbleTile objects to the bag?
My professor was very clear that we cannot use methods ahead of what we've gone over in class. Thanks in advance!
I think you missed new when create ScrabbleTitle object
for (int i = 0; i < 12; i++) {
letterBag.add(new ScrabbleTile('e'));
}
To create a ScrabbleTile you need the new keyword:
for (int i = 0; i < 12; i++) {
ScrabbleTile tile = new ScrabbleTile('e');
letterBag.add(tile);
}

Adding non-Latin number [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I want to manipulate my mother language mathematical number in Java.
Example:
int a = 1;
int b = 2;
int c = a + b;
System.out.println(c);
I want to add and show these number in my language. As in the following:
int a = ၁;
int b = ၂;
int c = a + b;
System.out.println(c);
supposed that 'a' through 'j' represent 0 to 9 in your mother language and numbers are written left to right.
it's better if you convert the input and output to normal integers and work with them. because this way you have access to all Mathematical methods that java provides and more important java's libraries are more robust than your own methods.
this two method will convert String object(your wanted characters to int)
to int and Vice versa.
public static String convertToString (int value){
StringBuilder result = new StringBuilder();
for (int i=1;value/i>0;i *= 10)
switch( value % (i*10) / i){
case 0:
result.append('a');
break;
case 1:
result.append('b');
break;
case 2:
result.append('c');
break;
case 3:
result.append('d');
break;
case 4:
result.append('e');
break;
case 5:
result.append('f');
break;
case 6:
result.append('g');
break;
case 7:
result.append('h');
break;
case 8:
result.append('i');
break;
case 9:
result.append('j');
break;
}
return result.reverse().toString();
}
public static int convertToInt(String string){
int result = 0;
int j =1;
for (int i=string.length()-1;i>=0;i--,j *= 10)
switch(string.charAt(i)){
case 'b':
result += 1*j;
break;
case 'c':
result += 2*j;
break;
case 'd':
result += 3*j;
break;
case 'e':
result += 4*j;
break;
case 'f':
result += 5*j;
break;
case 'g':
result += 6*j;
break;
case 'h':
result += 7*j;
break;
case 'i':
result += 8*j;
break;
case 'j':
result += 9*j;
break;
}
return result;
}

String index out of range with word to phone number convertion

So i have created this code to convert words into phone numbers, but when i try to run this code with letters under 7 word it will display string index out of range. But 7 or more is fine. How do i fix this? If so how do i set the string range?
{
System.out.println("Enter a word to be converted: ");
String telLetter = console.next ();
telLetter = telLetter.toUpperCase();
String telNumber="7";
int count=0;
int i=0;
while(count <7)
{switch(telLetter.charAt(i))
{case 'A':case 'B':case 'C': case 'a': case 'b': case 'c':
telNumber += "2";
count++;
break;
case 'D':case 'E':case 'F': case 'd': case 'e': case 'f':
telNumber += "3";
count++;
break;
case 'G':case 'H':case 'I': case 'g': case 'h': case 'i':
telNumber += "4";
count++;
break;
case 'J':case 'K':case 'L': case 'j': case 'k': case 'l':
telNumber += "5";
count++;
break;
case 'M':case 'N':case 'O': case 'm': case 'n': case 'o':
telNumber += "6";
count++;
break;
case 'P':case 'R':case 'S': case 'p': case 'r': case 's':
telNumber += "7";
count++;
break;
case 'T':case 'U':case 'V': case 't': case 'u': case 'v':
telNumber += "8";
count++;
break;
case 'W':case 'X':case 'Y':case 'Z': case 'w': case 'x': case 'y': case 'z':
telNumber += "9";
count++;
break;
}
if( count==3) {
telNumber += "-";
}
i++;
}
System.out.println( telNumber );
}
}}
Fixes in code:
Use while(count < telLetter.length()) in place of while(count <7)...
telLetter.charAt(i) can be removed by (telLetter.charAt(count))... By doing this you don't need to create an extra variable int i = 0;... Its a good practice to keep variable minimum.
Use a scanner to get the input... Like.. Scanner sc = new Scanner(System.in); String telLetter = sc.next();...
After you used your resources do close them by using sc.close();....
Also i can see that you are appending 7 at the begining of every number... if that's a requirement then its ok.. other wise you can use... String telNumber="";
I would recommend using StringBuilder because in one object you can do it all.. While appending a character every time in string you are continuously creating a new string and giving it the reference of telNumber.
Also in your code either remove telLetter = telLetter.toUpperCase(); or remove the cases with lowercase alphabets.. because on making toUpperCase() you are just writing extra lines by writing cases for lowercase characters that are not needed...
System.out.println("Enter a word to be converted: ");
Scanner scan=new Scanner(System.in);
String telLetter = scan.next ();
int stringLength=telLetter.length();
telLetter = telLetter.toUpperCase();
String telNumber="7";
while(count <stringLength)
{
switch(telLetter.charAt(count))
Change your code like this.
But as per your code, the 7 is always prefixed in the converted phone number.
String telNumber=""; telNumber should be declared with empty string.
while(count <7)
Your code won't end until it has run through 7 characters. Try using while(count < telLetter.length())
Your while loop condition needs to check for the length of the telLetter string.
while (count <7 && count < telLetter.length())

How to convert an alphanumeric phone number to digits

UPDATE:
The final version of my utility looks like this:
StringBuilder b = new StringBuilder();
for(char c : inLetters.toLowerCase().toCharArray())
{
switch(c)
{
case '0': b.append("0"); break;
case '1': b.append("1"); break;
case '2': case 'a': case 'b': case 'c': b.append("2"); break;
case '3': case 'd': case 'e': case 'f': b.append("3"); break;
case '4': case 'g': case 'h': case 'i': b.append("4"); break;
case '5': case 'j': case 'k': case 'l': b.append("5"); break;
case '6': case 'm': case 'n': case 'o': b.append("6"); break;
case '7': case 'p': case 'q': case 'r': case 's': b.append("7"); break;
case '8': case 't': case 'u': case 'v': b.append("8"); break;
case '9': case 'w': case 'x': case 'y': case 'z': b.append("9"); break;
}
}
return builder.toString();
ORIGINAL QUESTION:
I'm taking on the simple task of converting an alphanumeric phone number to a string of digits. For example, 1-800-HI-HAXOR would become 1-800-44-42967. My initial attempt was to create a nasty switch statement, but I'd love a more elegant, and efficient solution. Here's what I've got:
for(char c : inLetters.toLowerCase().toCharArray())
{
switch(c)
{
case '0': result+="0"; break;
case '1': result+="1"; break;
case '2': case 'a': case 'b': case 'c': result+="2"; break;
case '3': case 'd': case 'e': case 'f': result+="3"; break;
case '4': case 'g': case 'h': case 'i': result+="4"; break;
case '5': case 'j': case 'k': case 'l': result+="5"; break;
case '6': case 'm': case 'n': case 'o': result+="6"; break;
case '7': case 'p': case 'q': case 'r': case 's': result+="7"; break;
case '8': case 't': case 'u': case 'v': result+="8"; break;
case '9': case 'w': case 'x': case 'y': case 'z': result+="9"; break;
}
}
Thanks!
The switch statement is not really that bad. Your algorithm is linear with respect to the length of the phone number. The code is readable and pretty easy to verify by inspection. I wouldn't mess with it, except to add a default case for handling errors. (I'm not a Java programmer, so forgive me if it's called something else.)
If you have to make it faster, a pre-initialized table indexed by character would avoid any comparisons beyond basic error checking. You could even avoid the case conversion by duplicating the values in the table (digit['A'] = digit['a'] = "2";). The cost of initializing the table would be amortized over the total number of conversions.
You could do this using the Apache Commons Lang StringUtils, as follows:
String output = StringUtils.replaceChars(StringUtils.lowerCase(input),
"abcdefghijklmnopqrstuvwxyz",
"22233344455566677778889999");
Assuming speed is not your main concern, of course, and you want a compact solution ;)
Use a Map, where the keys are the letters and digits, and the value is the number on the keypad. (So each keypad number will be indexed by three or four letters and one digit).
Map<Character, Character> keypad = new HashMap<Character, Character>();
...
StringBuilder buf = new StringBuilder(inLetters.length());
for (int idx = 0; idx < inLetters.length(); ++idx) {
Character ch = keypad.get(inLetters.charAt(idx));
if (ch != null)
buf.append(ch);
}
Update: I was curious whether a hand-coded lookup table would perform better than a dense set switch cases. In my casual testing, I found the following code to be the fastest I could come up with:
private static final char[] lut =
"0123456789:;<=>?#22233344455566677778889999[\\]^_`22233344455566677778889999".toCharArray();
private static final char min = lut[0];
String fastest(String letters)
{
int n = letters.length();
char[] buf = new char[n];
while (n-- > 0) {
int ch = letters.charAt(n) - min;
buf[n] = ((ch < 0) || (ch >= lut.length)) ? letters.charAt(n) : lut[ch];
}
return new String(buf);
}
Surprisingly, it was more than twice as fast as similar code using a switch statement (which compiled to a tableswitch instruction). This was just for fun, mind you, but on my laptop, running in a single thread, I could convert 10 million 10-letter-"numbers" in about 1.3 seconds. I was really surprised, because as I understand it, a tableswitch operates in essentially the same way, but I expected it to be faster since it is a JVM instruction.
Of course, unless I were getting paid only for each of a limitless supply of phone numbers I could convert, I would never write code like this. A switch is much more readable, performs well as-is, and is likely to get a free performance boost in some future JVM.
Far and away, the greatest improvement to the original code comes from using a StringBuilder instead of concatenating strings, and that does nothing to impair readability of the code. Using charAt instead of converting the input to a char[] also makes the code simpler and easier to understand and improves performance too. Finally, appending char literals instead of String literals ('1' rather than "1") is a performance improvement that aids readability a little too.
How about simply:
String convert(String inLetters) {
String digits = "22233344455566677778889999";
String alphas = "abcdefghijklmnopqrstuvwxyz";
String result = "";
for (char c : inLetters.toLowerCase().toCharArray()) {
int pos = alphas.indexOf(c);
result += (pos == -1 ? c : digits.charAt(pos));
}
return result;
}
If you want a solution that doesn't force you to enumerate all of the letters, you could do something like:
char convertedChar = c;
if (Character.isLetter(c)) {
//lowercase alphabet ASCII codes: 97 (a)-122 (z)
int charIndex = ((int)c) - 97;
//make adjustments to account for 's' and 'z'
if (charIndex >= 115) { //'s'
charIndex--;
}
if (charIndex == 121) { //'z'-1
charIndex--;
}
convertedChar = (char)(2 + (charIndex/3));
}
result += convertedChar;
If you run this 10^9 times in a tight loop and ctrl-break it a few times, my bet is that nearly every time it will be deep in the string class trying to accomplish one of those innocent-looking "+=" operators.
Switch statements get compiled to a similar form as if-else statements, (each case statement is essentially an if (c == '...') test in disguise) so although this is visually more compact than cascading if's that test for each character, there may or may not be any real performance benefit.
You can potentially streamline it by eliminating some of the comparisons. The key is that char is an integer type (which is why you can switch on a char) so you can use numeric comparison operators. and 'aAssuming your inLetters string only contains alphanumeric characters, this should work... (All other characters will pass through unchanged.)
String result = "";
for (char c : letters.toLowerCase().toCharArray()) {
if (c <= '9') result += c;
else if (c <= 'c') result += "2";
else if (c <= 'f') result += "3";
else if (c <= 'i') result += "4";
else if (c <= 'l') result += "5";
else if (c <= 'o') result += "6";
else if (c <= 's') result += "7";
else if (c <= 'v') result += "8";
else if (c <= 'z') result += "9";
else result += c;
}
The characters of interest have the hexadecimal values: '0' = 0x30, '9' = 0x39, 'a' = 0x61, and 'z' = 0x7a.
Edit: It's better practice to use a StringBuilder and append() to create the string, but for small strings it's not likely to be appreciably faster. (Amdahl's Law demonstrates that the actual speedup you can expect from optimizing code is limited by the percentage of time actually spent in that code.) I only used concatenated strings to make the algorithm clear to the OP.

Categories