How to create a large scale money conversion algorithm in java - java

I was just trying to create this small android app in android studio to convert currency.
I used 2 Spinner objects to hold only 3 values (USD, POUND, EURO) :
if(actualType.equals("USD")){
if(wantedType.equals("POUND")){
montantConv = montantNonConv * 0.87;
}
else if(wantedType.equals("EURO")){
montantConv = montantNonConv;
}
}
else if(actualType.equals("POUND")){
if(wantedType.equals("EURO")){
montantConv = montantNonConv * 1.15;
}
else if(wantedType.equals("USD")){
montantConv = montantNonConv * 1.15;
}
}
else if(actualType.equals("EURO")){
if(wantedType.equals("USD")){
montantConv = montantNonConv * 1;
}
else if(wantedType.equals("POUND")){
montantConv = montantNonConv * 0.87;
}
}
With if-else the code is too long for a combination of only 3 choices (input + output).
i was just wondering is there a better algorithm to do this ? How does the online ones do it that have 50+ currencies to chose from ?

As you are finding out, putting names of the currencies in the conversion logic is not a good idea. The same applies with the conversion values. You should want to write code to minimize things like that. You want the actual conversion logic coded without explicitly knowing ("hard coding") the names of the currencies or their exchange rates.
Rather than have something like actualType.equals("POUND") in your code, you want to try to have code that has each value represented by a variable: actualType.equals(otherType).
In real life, your data would come from an external source, such as a text file, a database, or another computer. In your small demo app, you might want to have the data load when the program starts, and be stored using array(s) and or (a) Collection object(s). At this time, you would also load the values in the Spinner objects, with the same data.
You might think about whether it is worth having another class or not:
class Money {
String name;
double conversionValue;
Money (String name, double value) {
this.name = name;
conversionValue = value;
}
And so on. (Note: I omitted visibility specifiers.)
In your conversion app, you could simulate the external source with an array:
Money [] market = { new Money ("Euro", 1.00), new Money ("USD", 1.00),
new Money ("Pound", 1.15), new Money ("Yen", 0.007),
new Money ("Rupee", 0.0125), ... };
An alternative to creating another class to glue a currency and exchange factor is to use a Map <String, Double>, with the name of the currency as the key and the exchange factor as the value.
So, the conversion app might have a method like this:
public double convertMoney (String selling, int amt, String buying)
{ ...
Depending on how the names and values were stored, it would use the string values to look up 2 conversion values. The exchange rate would be conversion value associated with selling divided by the conversion value associated with buying.

Related

Converting volume (gallons) to mass (grams) using jscience?

I can't seem to find/remember a specific formula to convert units of volume to units of mass. I think it has to do with density, but how would this be done?
In my case, I'm trying to write a program using the JScience library to convert any type of unit the user might input into the standard SI.GRAM unit.
The problem comes in with trying to describe things like honey, which are commonly measure in gallons, grams, pounds, and ounces.
I have the following code mostly working when working with units of mass, but I get a ConversionException when trying to work with gallons, which is understandable, since it's measuring volume and not mass.
javax.measure.converter.ConversionException: gal is not compatible with g
Here's what I've got so far. Also, I chose to break up the Amount<Mass> into amount_value and amount_unit because I want to store this in an SQLite database and also have it be represented as text, so I am storing it like this for serialization purposes.
public class Sugar {
private String type;
private double amount_value;
private String amount_unit;
public Sugar(SUGAR_TYPES type, Amount<Mass> amount) {
this.type = type.toString();
this.amount_value = amount.getEstimatedValue();
this.amount_unit = amount.getUnit().toString();
}
public Amount<Mass> getAmount() {
BaseUnit<Mass> mass_unit = new BaseUnit<>(amount_unit);
return Amount.valueOf(amount_value, mass_unit);
}
public SUGAR_TYPES getType() {
return (type != null) ? SUGAR_TYPES.valueOf(type) : null;
}
public double getAmountInGrams() {
Amount<Mass> mass_unit = getAmount();
switch (mass_unit.getUnit().toString().toLowerCase()) {
case "g":
return mass_unit.getEstimatedValue();
case "gal":
// this throws the ConversionException
return NonSI.GALLON_LIQUID_US.getConverterTo(SI.GRAM).convert(mass_unit.getEstimatedValue());
case "lb":
return NonSI.POUND.getConverterTo(SI.GRAM).convert(mass_unit.getEstimatedValue());
default:
Log.e(TAG, String.format("Failed to get Amount<Mass> in SI.GRAM for amount %s and unit %s.",
amount_value, amount_unit));
throw new IllegalArgumentException(mass_unit.getUnit().toString());
}
}
public enum SUGAR_TYPES {
HONEY, SUCROSE, APPLES, APRICOTS, APRICOTS_DRIED, BANANAS, BLACKBERRY, BLUEBERRY,
BOYSENBERRY, CANTALOUPE, CELERY, CHERRY_DARK_SWEET, CRANBERRY, CURRANT_BLACK, DATES,
DATES_DRIED, DEWBERRY, ELDERBERRY, FIGS, FIGS_DRIED, GOOSEBERRY, GRAPE_CONCORD,
GRAPES, GRAPEFRUIT, GUANABANA, GUAVAS, HONEYDEW_MELON, JACKFRUIT, KIWIS, LEMON_JUICE,
LITCHI, LOGANBERRY, MANGOS, MAPLE_SYRUP, PLUMS, RAISINS_DRIED, RASPBERRY_BLACK,
RASPBERRY_RED, RHUBARB, STRAWBERRY, TANGERINES, WATERMELONS
}
}
Is there a better way of doing this? I have other classes that need to have a similar way of converting a generic unit into the SI unit.
You will need mass per volume values for everything you want to calculate. For example, you could expand your enum to include a gram per litre (or kg per m^3, whatever works best for you). You cannot just convert volume to weight without this information:
Example: 1 litre of water=1kg, but 1 litre of petrol=0.88 kg.
Then you only need to convert input volume to your default volume, look up the type, multiply the converted volume with your saved gram per litre and return the result.

Finding index in an ArrayList

I have a CSV file that i stored as an arraylist in Java. Here's the code i ran
public class StockData {
private ArrayList<StockRecord> records;
public StockData() {
records = new ArrayList<StockRecord>();
}
//reads the file from the folder
public void loadPriceData(String filepath) {
try {
Scanner scanner = new Scanner(new FileReader(filepath));
String line;
StockRecord record;
scanner.nextLine();
while (scanner.hasNextLine()) {
line = scanner.nextLine();
String[] results= line.split(",");
double open = Double.parseDouble( results[1]);
double high = Double.parseDouble( results[2]);
double low = Double.parseDouble( results[3]);
double close = Double.parseDouble( results[4]);
double volume = Double.parseDouble( results[5]);
double adjClose = Double.parseDouble( results[6]);
//create the record
record = new StockRecord(results[0], open, high, low, close, volume, adjClose);
records.add(record);
}
scanner.close();
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
The code works correctly however, i am having trouble with the next part of the project.
Data Example
Date Open High Low Close Volume Adj Close
6/10/2011 128.85 128.93 127.26 127.6 238629400 126.97
6/13/2011 127.89 128.24 127.05 127.7 207599800 127.07
6/14/2011 128.87 129.77 128.82 129.32 160570400 128.68
6/15/2011 128.24 129.3 126.68 127.02 300958000 126.39
6/16/2011 127.06 127.97 126.32 127.3 308032800 126.67
I'm trying to print out the AdjClose price for a date range. say between 6/13/2011 and 06/15/2016. It seems like i would need to find the index of the date first and then print the price record.
How can i do this? I'm not sure where to start. Do i first need to save the date in the its own list/array and use that to find the index?
Thanks
You would need to iterate through the list of StockRecord and compare each record's date to your specified range. If it falls in the range you would print it. This can be optimized if your list is sorted.
To compare first date is preferable to use a data type date
If your array could scouring records and objects of type StockRecord comparing your date field you want to evaluate
for (StockRecord reg : records) {
if(reg.date.after(historyDate) && reg.date.before(futureDate)) {
System.out.println(reg.toString()); // print object
}
}
Your Question is really a duplicate of many others in Stack Overflow.
LocalDate
Your example code does not actually show the date as part of your StockRecord class. But it should be a member of type LocalDate. Search Stack Overflow for countless posts on how to parse your input string into a LocalDate.
Comparator
Define a Comparator for your class, to extract the LocalDate for comparisons. The LocalDate class itself implements the compareTo method, as well as isBefore, isAfter, and isEqual.
Benefit of sorting
If you will be doing this query often, then it makes sense to sort the StockRecord objects into a List. Then you can be smart about searching, again discussed on many other posts in Stack Overflow.
SortedMap
You could organize the StockRecord objects into a SortedMap, mapping the LocalDate to a Collection of the StockRecord objects sharing that particular date. Using a SortedMap rather than a mere Map lets you do smarter searches for a date range, as mentioned above. Again, many posts on Stack Overflow about maps.
Apache Commons CSV
By the way, Apache Commons CSV project makes easy work of reading and parsing a CSV data file.
BigDecimal
Do not use double or Double for currency amounts, or for any fractional number where accuracy matters. Those types are floating-point types, built for speed but trading away accuracy. You will be introducing extraneous extra digits into the end of the decimal fraction.
Instead use BigDecimal. Yet again, many posts on Stack Overflow on this class.
Integers
Do not use a fractional number like Double for an integer number such as your Volume field. Doing so wastes memory, and creates confusion to anyone reading your code.
Use a 32-bit integer or Integer if your maximum value is two billion or less (2^31-1 = 2,147,483,647), otherwise a 64-bit long or Long.

Lost on how to find the largest price below the target value from a given menu

I am trying to create a method called selectFood that takes the amount of money I have as a parameter, outputs the selections on the screen and returns the percentage tip I will be leaving rounded to one decimal place.
I have the possible costs without tip done through recursion but I need to compare them and find the larger one. Please help
public static void selectFood(double money){
/*String[]menu={"Bandera Pizza Bread","Boston's Pizza Bread","Garlic Twist Bread","Single Order",
"Sun-Dried Tomato Bruschetta","Three Cheese Toast","Double Order wings","Starter Size wings",
"Cactus Nachos","Baked Ravioli Bites","Southwest Quesadilla"};
*/
double[]itemCost={6.49,5.35,7.49,5.35,6.99,6.35,16.49,8.99,10.29,8.49,9.25};
possibilities(itemCost.length,"",itemCost,money);
//selectFood(n,itemCost,0);
}
public static void possibilities(int length,String sofar,double[]itemCost,double money){
if(length==0){
//selectFood(sofar,itemCost,money,0);
float totCost=0;
double target=money/1.15;
double minTip=money-target;
char[]sofarList=sofar.toCharArray();
for(int i=0;i<sofarList.length;i++){
if(sofarList[i]=='1'){
totCost+=itemCost[i];
}
}
if(totCost<target){
System.out.println(totCost);
}
}
else{
possibilities(length-1,sofar+"0",itemCost,money);
possibilities(length-1,sofar+"1",itemCost,money);
}
}
Right now I get back
0.0
8.49
6.35
6.99
5.35
7.49
5.35
6.49
To find the largest value below the target value, you need a variable to hold the current maximum in each iteration you are making thru recursion.
I changed the logic of your program to keep track of the current maximum value
public static void possibilities(int length,String sofar,double[]itemCost,double money){
if(length==0){
//selectFood(sofar,itemCost,money,0);
float totCost=0;
double target=money/1.15;
double minTip=money-target;
char[]sofarList=sofar.toCharArray();
for(int i=0;i<sofarList.length;i++){
if(sofarList[i]=='1'){
totCost+=itemCost[i];
}
}
if(totCost<target && totCost>max){
max = totCost;
System.out.println(totCost);
for(int i=0;i<sofarList.length;i++){
System.out.print(sofarList[i]);
}
System.out.println("\n");
}
}
else{
possibilities(length-1,sofar+"0",itemCost,money);
possibilities(length-1,sofar+"1",itemCost,money);
}
}
and you need to declare the variable max as static inside your class
static double max=0;
I added print statement to show the maximum value chosen in each feasible iteration.
After the end of all iterations the maximum value is stored in the max variable and to keep track of the menu list we need to add one more static variable as given below
static String menulist=null;
and we need to assign the value inside the method possibilities
if(totCost<target && totCost>max){
max = totCost;
menulist = sofarList;
}
The trouble you probably have with your method (besides it being relatively hard to understand) is, that your method is not actually computing a value (well... not returning any at least), but it is printing something to System.out. Generally, your methods should either compute something or alter state (e.g., perform I/O), but not both.
A simple fix would be to store totCost in an instance variable instead of printing it. That is, you can change your lines:
if(totCost < target){
System.out.println(totCost);
}
to
if(totCost < target && highestPrice < totCost){
highestPrice = totCost;
}
where highestPrice is an instance variable. Now you can access highestPrice from the selectFood method.
Note that this is a hack rathar than a clean solution! Your code would be much cleaner if your possibilities method would return the value it computes as the regular return value.
Rethink how you would recursivly compute the cost:
You are given a set (in your case, an array) of prices, and a budget.
For each price you can either take the item and pay the price, or you can not take the item and keep the money
If you have only one item in the list, the most expensive combination you can make is either 0, if you cannot afford the item, or the value of the item, if you can.
The last point is your base-case (not considering that you could also have an empty array). So you have for a base case
double possibilities(double[] items, double money){
if (items.length == 1 && items[0] < money / 1.15) return items[0]; else return 0;
A side note here: Don't put magic numbers (e.g., 1.15) in your code. They do not convey any meaning.
Now for the general case where items.length>1: We can either take the first item, or we can not take it. If we do take it, then we will have less money to spend on the remaining items. Also, we can only take it, if we can afford it, i.e., if money>items[0]. With the remaining money and the remaining items we call the method recursivly and take the bigger value.
if(items.length > 1)
if (items[0] < money / 1.15)
double totCostWithItem0 = items[0] + possibilities(/* items[1 to length], money - items[0] */)
double totCostWithoutItem0 = possibilities(/* items[1 to length], money */)
return totCostWithItem0 > totCostWithoutItem0 ? totCostWithItem0 : totCostWithoutItem0;
else // cannot afford it
return possibilities(/* items[1 to length], money */
Sorry for being so sloppy with the code here. Because you are working with arrays, you need to copy items 1 to length in a new array in order to pass it in the recursive call. Also... I am not quite sure what the division by 1.15 is supposed to do, so you may need to adjust the money you are recursively passing.

Calculate average from sources that sends data at different time.

In my current project, I am going to write a function of calculating average Temperature.
A notatable point of this function is that it receives data from many temperature sources at different time. Could you advise me "How can I write a function with such a behavior?"
For instance, following function receives tempSensorData from many temperature Sensors, which sends temperature data at different time. I need to write a logic of calculating average temperature.
public void calculateRoomAvgTemp(TempStruct tempSensorData) {
// Write logic of calculating Average Temperature.
}
Why don't you just keep a running average? Assuming that your method gets called each time a temperature reading is taken, you can do the following:
private final List<Double> temps = Collections
.synchronizedList(new ArrayList<Double>());
private double currentAverage = 0d;
public void calculateRoomAvgTemp(TempStruct tempSensorData) {
synchronized (this.temps) {
this.temps.add(tempSensorData.temp);
this.currentAverage = 0d;
for (Double temp : this.temps) {
this.currentAverage += temp;
}
this.currentAverage /= this.temps.size();
}
}
can you store past temperatures in a separate field in the class? I noticed that your method is void, therefore doesn't return anything. I'm going to assume you want to store the average in the separate field. Example:
public class TempCalculator {
private double totalTemp = 0;
private int sensors = 0;
private double averageTemp = 0;
public void calculateRoomAvgTemp(TempStruct tempSensorData) {
totalTemp += tempSensorData.getTemp(); // Assuming getTemp() exists
sensors += 1;
averageTemp = totalTemp / sensors;
}
public double getAvgTemp() {
return averageTemp;
}
}
Of course, this can be done with a list if you want to store temperatures used and shaped to your needs. Hope this helps.
one way I could think of - insert values as you receive them into an ArrayList. If you need to calculate the average temperature after each data point that you receive, run through a for loop and average out the data points in the ArrayList to get the required value. One could look into using memoization to avoid running through the loop for all values.
Another option would be to use apache commons math whereby you can use the descriptive statistics option (using a rolling array) to get the mean (average).
I am assuming your code is single threaded. If multithreaded, you could use a thread safe vector (or add locks/synchronization on your own).
Hope this helps.
To calculate the average without keeping all historic values in memory (or a database), use a "moving average". This mathematical tool can give you the new average from the last one plus the current value.
You can create one thread which will calculate the temperature and many which will measure it values from different places. When some measurement is done the thread pushes the result into e.g. LinkedBlockingQueue. It's important to synchronize this operation because many threads might try to push results at the same time. The thread which calculate average temperature would run in a loop, pop new result from the queue, recalculate the temperature and then once again try to pop another result. As far as the queue will be empty the thread would be blocked on reading from it. In this way you would have an asynchronous communication between threads and the average temperature would be recalculated immediately after the measurements are done.

Check value to 'ceiling' values and return corresponding variable

I have some constants f.e.:
BigDecimal ceiling1 = new BigDecimal(5);
BigDecimal ceiling2 = new BigDecimal(10);
BigDecimal ceiling3 = new BigDecimal(20);
BigDecimal rate1 = new BigDecimal(0.01);
BigDecimal rate2 = new BigDecimal(0.02);
BigDecimal rate3 = new BigDecimal(0.04);
BigDecimal rate4 = new BigDecimal(0.09);
Now based on a parameter f.e.:
BigDecimal arg = new BigDecimal(6);
I want to retrieve the right rate which is based on this if structure (simplified):
if(arg <= ceiling1) {
rate = rate1;
}else if(arg <= ceiling2) {
rate = rate2;
} else if (arg <= ceiling3) {
rate = rate3;
}else rate = rate4;
So in my example rate should be rate2
But I'm wondering if someone knows a better way to implement this, instead of a bunch of ifs.
Any pointers are welcome!
PS: I know my code isn't 100% right, just wanted to show the idea
You can store your ceilings as keys in a TreeMap and your rates as values. Then use floorEntry and see also here.
final TreeMap<BigDecimal, BigDecimal> rates = new TreeMap<BigDecimal, BigDecimal>();
rates.put(new BigDecimal(0), new BigDecimal(0.01));
rates.put(new BigDecimal(5), new BigDecimal(0.02));
rates.put(new BigDecimal(10), new BigDecimal(0.04));
rates.put(new BigDecimal(20), new BigDecimal(0.09));
System.out.println(rates.floorEntry(new BigDecimal(0)).getValue());
System.out.println(rates.floorEntry(new BigDecimal(6)).getValue());
System.out.println(rates.floorEntry(new BigDecimal(10)).getValue());
System.out.println(rates.floorEntry(new BigDecimal(100)).getValue());
Test: http://ideone.com/VrucK. You may want to use a different representation as you can see in the test it looks ugly(Like Integers for the ceiling). Btw the ugly output comes from the fact that 0.01 is a double which does funny things with decimal representations.
Edit: Suggested cleanup.
I would probably get rid of the BigDecimal objects and store the rates (and the ceilings) as CONSTANTS (FINAL variables).
Then i will use a Switch statement to find the right rate.
class RateCalculator {
double ceiling[] = new double[]{5,10,20};
double rate[] = new double[]{0.01,0.02,0.04}
// use assertions to ensure that the sizes of these two arrays are equal.
// ensure that successive values in ceiling are higher than the last.
public double calculateRate(double value) {
for (int i=0;i<ceiling.length;++i) {
if (value < ceiling[i]) {
return rate[i];
}
// the rate for values higher than the highest ceiling
return 0.09;
}
}
You can change the number of rates by changing the size of the arrays. Some of the values should be named constants to follow good programming style - they are left as numbers here to illustrate the correspondance between the OPs values and the ones here.
Conversion to BigDecimal is left as an exercise for the reader.

Categories

Resources