Problem
This is my version of Merchant’s Guide to the Galaxy:
A merchant buys and sells items in the galaxy. Buying and selling over
the galaxy requires you to convert numbers and units. The numbers used
for intergalactic transactions follows similar convention to the roman
numerals. Roman numerals are based on seven symbols:
- I 1
- V 5
- X 10
- L 50
- C 100
- D 500
- M 1000
Numbers are formed by combining symbols together and adding the
values. For example, MMVI is 1000 + 1000 + 5 + 1 = 2006. Generally,
symbols are placed in order of value, starting with the largest
values. When smaller values precede larger values, the smaller values
are subtracted from the larger values, and the result is added to the
total. For example MCMXLIV = 1000 + (1000 − 100) + (50 − 10) + (5 − 1)
= 1944.The symbols “I”, “X”, “C”, and “M” can be repeated three times in
succession, but no more. (They may appear four times if the third and
fourth are separated by a smaller value, such as XXXIX.) “D”, “L”, and
“V” can never be repeated. “I” can be subtracted from “V” and “X”
only. “X” can be subtracted from “L” and “C” only. “C” can be
subtracted from “D” and “M” only. “V”, “L”, and “D” can never be
subtracted. Only one small-value symbol may be subtracted from any
large-value symbol. A number written in [16]Arabic numerals can be
broken into digits. For example, 1903 is composed of 1, 9, 0, and 3.
To write the Roman numeral, each of the non-zero digits should be
treated separately. Inthe above example, 1,000 = M, 900 = CM, and 3 =
III. Therefore, 1903 = MCMIII. Input to your program consists of lines
of text detailing your notes on the conversion between intergalactic
units and roman numerals. You are expected to handle invalid queries
appropriately.Test input:
- glob is I
- prok is V
- pish is X
- tegj is L
- glob glob Silver is 34 Credits
- glob prok Gold is 57800 Credits
- pish pish Iron is 3910 Credits
- how much is pish tegj glob glob?
- how many Credits is glob prok Silver?
- how many Credits is glob prok Gold?
- how many Credits is glob prok Iron?
- how much wood could a woodchuck chuck if a woodchuck could chuck wood?
Test output:
- pish tegj glob glob is 42
- glob prok Silver is 68 Credits
- glob prok Gold is 57800 Credits
- glob prok Iron is 782 Credits
- I have no idea what you are talking about
I would appreciate suggestions on how to better break up my code into different classes, more efficient ways to have done it or a better way to have done it without using many if
statements, or more ways to have used Java 8.
public class ParseInput {
public static void main(String [] args){
String filePath = "/Users/s/interviews/tw/testinput.txt";
try (Stream<String> lines = Files.lines(Paths.get(filePath))){
lines
.forEach(line -> RomanNumerals.parse(line));
}
catch (IOException io){
io.printStackTrace();
}
}
}
public class RomanNumerals {
static Map<String, String> howMuchMoney = new HashMap<>();
public static int romanToInt(String s) {
int nums[] = new int[s.length()];
for (int i = 0; i < s.length(); i++) {
switch (s.charAt(i)) {
case 'M':
nums[i] = 1000;
break;
case 'D':
nums[i] = 500;
break;
case 'C':
nums[i] = 100;
break;
case 'L':
nums[i] = 50;
break;
case 'X':
nums[i] = 10;
break;
case 'V':
nums[i] = 5;
break;
case 'I':
nums[i] = 1;
break;
}
}
int sum = 0;
for (int i = 0; i < nums.length - 1; i++) {
if (nums[i] < nums[i + 1])
sum -= nums[i];
else {
sum += nums[i];
}
} return sum + nums[nums.length - 1];
}
public static void whichMetal(String s, String metal) {
double sum = 0;
String[] output = s.split(Pattern.quote(metal));
// Galatic words before the type of metal
String individualWords = output[0];
String[] outputWords = individualWords.split(" ");
String romanNumeral = "";
// Convert galactic words to Roman numerals
for (String ow : outputWords) {
romanNumeral += howMuchMoney.get(ow);
}
// Convert Roman numerals to regular numbers
double convertedInt = romanToInt(romanNumeral);
String creditWords = output[1];
// Splits the part of the String before Credit so that we get the number
String[] creditOutputWords = creditWords.split(" ");
int money = creditOutputWords.length - 2;
double creditMoney = Double.parseDouble(creditOutputWords[money]);
// Divides the number of credits by the value before the metal so we get the value for one unit of that metal
double valueToInsert = 0;
if (convertedInt !=0 ) {
valueToInsert = creditMoney / convertedInt;
}
else {
System.out.println("creditMoney " + creditMoney);
System.out.println("convertedInt " + convertedInt);
}
// inserts the value of metal and the numeric value into the hashmap
howMuchMoney.put(metal, Double.toString(valueToInsert));
}
static void translate(String s, String separator){
double sum = 0;
DecimalFormat df2 = new DecimalFormat("");
// Splits the string based on whether we are asking how much or how many Credits
String[] output = s.split(Pattern.quote(separator));
// Intergalatic words (plus metal), ie glob prok Silver or tegj glob glob
String individualWords = output[1];
String[] outputWords = individualWords.split(" ");
String romanNumeral ="";
double value =0;
for (String ow: outputWords){
if (!ow.equals("")) {
if (!ow.equals("Silver") && (!ow.equals("Gold") && (!ow.equals("Iron")))) {
// Gets Roman numeral for the Galatic words ( sentence doesn't have Credits)
romanNumeral += howMuchMoney.get(ow);
}
else {
// Gets Roman numeral before the metal
value= Double.parseDouble(howMuchMoney.get(ow));
// System.out.println("with metals " + romanNumeral);
// Multiplies the number of units of the metal by the value of the metal
sum = value * romanToInt(romanNumeral);
// Remove the last space and ? and also show without decimals
System.out.println(output[1].trim().replaceAll(" \?", "") +" is " + df2.format(sum).toString().replaceAll(",", "") + " Credits");
}
}
}
// Only prints out if it is not How many credits (since that has already been printed out)
if (!output[1].contains("Silver") && !output[1].contains("Gold") && !output[1].contains("Iron"))
System.out.println(output[1].trim().replaceAll(" \?", "") + " is " + romanToInt(romanNumeral));
}
public static boolean validate(String s){
if (s.contains("DD") || s.contains("LL") || s.contains("VV") || s.contains("MMMM") || s.contains("CCCC") || s.contains("XXXX"))
return false;
else
return true;
}
public static void parse(String s){
boolean validString = validate(s);
if (validString == true) {
if (s.contains("how many Credits")) {
translate(s, "how many Credits is");
} else if (s.contains("Silver")) {
whichMetal(s, "Silver");
} else if (s.contains("Gold")) {
whichMetal(s, "Gold");
} else if (s.contains("Iron")) {
whichMetal(s, "Iron");
} else if (s.contains("how much is")) {
translate(s, "how much is ");
} else if (!s.equals("") && s.contains("is")) {
String delimiter = " is ";
String[] output = s.split(Pattern.quote(delimiter));
howMuchMoney.put(output[0], output[1]);
} else System.out.println("I have no idea what you are talking about");
}
else {
System.out.println("The string is invalid");
}
}
}
Solution
You can use HashMap
for storing the integer values aligned with the corresponding letters instead of using switch case.
i.e.
HashMap<String, Integer> hmap = new HashMap<String, Integer>();
hmap.put("M", 1000);
hmap.put("D", 500);
hmap.put("C", 100);
hmap.put("L", 50);
hmap.put("X", 10);
hmap.put("V", 5);
hmap.put("I", 1);
And retrieve them in the following manner:
String var = hmap.get("M"); //where you get 1000 in var.
You can iterate through the HashMap
using an Iterator
like this:
Set set = hmap.entrySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
Map.Entry mentry = (Map.Entry)iterator.next();
}