0

I am trying to create a Unit Converter. My Idea is to have a select box at the top to select Length/Temp etc. which will populate the two select boxes below with appropriate units. The below code should Work only for Milimieters to Milimeter, Meters and Kilometers. It should show the calculated value in the alert box that appears when go is pressed..

What I was able to determine by using firebug is that the script doesn't falls into the switch block and the calculations never happen AND I am unable to determine WHY? I will be grateful if some one can explain it to me. I am fairly new to javascript, this is just my first app.

For a good view how the site works: http://saad749.freeoda.com/MetricConversions/conversions.html

You can view the source code in firefox for the whole site. Thanks a lot. The major part of code is given below:

function calculate()
{
	
	var e;
	var uniti;
	var unito;
	var vali;
	var valo;
	
	e = document.form1.entity.options[document.form1.entity.selectedIndex].value;
	uniti = document.form1.Iunit.options[document.form1.Iunit.selectedIndex].value;
	unito = document.form1.Ounit.options[document.form1.Ounit.selectedIndex].value;
	vali = document.form1.Iamount.value;
	valo = document.form1.Oamount.value;
	
	
	switch (e)
	{
		case 1:
			switch(uniti)
			{
				case 1:
					switch(unito)
					{
						case 1:
							valo = vali*1;
							break;
						case 2:
							valo = vali*100;
							break;
						case 3:
							valo = vali*1000;
							break;
					}
					break;
			}
			break;
		
	}
	
	alert(e + "\t" + uniti + "\t" + unito + "\t" + vali + "\t" + valo);
}
<input type="submit" name="go" id="go" value="Go !" onclick="calculate();"/>
3
Contributors
8
Replies
9
Views
6 Years
Discussion Span
Last Post by saad749
1

Try forcing e, unito, and uniti to be integers so the comparison is definitely integer == integer?. When you grab the value off an element it is generally read as a string. Forcing it to read as an integer sometimes makes comparisons like this work better. You can also use console.log to see what each is before the switch statement and do a comparison in the console.log statement to see what the compared result is. Using the console.log statements is a time saver since you can see objects as well as value in the console. This way you can ensure you are even getting the correct element in the first place.

var e;
	var uniti;
	var unito;
	var vali;
	var valo;
 
	e = parseInt(document.form1.entity.options[document.form1.entity.selectedIndex].value);
	uniti = parseInt(document.form1.Iunit.options[document.form1.Iunit.selectedIndex].value);
	unito = parseInt(document.form1.Ounit.options[document.form1.Ounit.selectedIndex].value);
	vali = parseInt(document.form1.Iamount.value);
	valo = parseInt(document.form1.Oamount.value);
/* the debug statement I mentioned */
console.log("e", e);
console.log("does e == 1?", (e===1)); /* switch is a more restrictive comparison so we want to compare using === not ==*/

Edited by scrappedcola: n/a

1

Saad749,

You will hate me for telling you that you don't need to use switch/case, and that the code will simplify significantly by working out efficient strategies for:

  • populating the two select meus
  • storing the conversion factors
  • performing the calculations.

Populating the Select Menus
Both menus get exactly the same options so they can both be populated from the same data.

//inside still inside setOptions() 
if (chosen == "1") {
  var params = [
    ['Milimeters','1'],//base unit
    ['Meters','2'],
    ['KiloMeters','3'],
    ['Feet','4'],
    ['Yards','5'],
    ['Miles','6']
  ];
}
//then similarly for the other entities ...

// then, still inside setOptions(), populate both menus in one go ...
for(var i=0; i<params.length; i++) {
  selbox0.options[selbox0.options.length] = new Option(params[i][0],params[i][1]);
  selbox1.options[selbox1.options.length] = new Option(params[i][0],params[i][1]);
}

Storing the conversion factors
Above I used values of '1','2','3' etc., which forces you into interpretation in the javascript. By specifying the conversion factors directly, as values to the options, no further interpretation is necessary and we can go straight to the calculation proper.

if (chosen == "1") {
  var params = [
    ['Milimeters','1'],//base unit
    ['Meters','1000'],
    ['KiloMeters','1000000'],
    ['Feet','304.8'],
    ['Yards','914.4'],
    ['Miles','1609344']
  ];
  //then similarly for the other entities (but beware, Temperature has to be different!) ...
}

Performing the calculations
As you can see above, I indicate a "base unit". It doesn't particularly matter which one is used but it's easiest to use the smallest (all other conversion factors are then greater than 1).

Converting from any unit to any other unit (of the same entity) is a two step algorithm comprising :

  • Conversion of the input value to the BASE value
  • Conversion of the BASE value to the output value.

I will let you think about how you might implement that in the calculate() function.

Length|Area|Volume|Mass calcs are trivial. Temperature is a real rotter because Fahrenheit and Celcius are not ratio scales (they have false zeros).

After a little thought I managed to develop generalised calculate() function for all entities, including temperature, at just 15 lines of code (one of which is a comment). I could reduce it further but it would become unreadable.

Airshow

Edited by Airshow: minor corrections &amp; typos

0

Thanks a lot both of you.
@Airshow: Well, I love u now :P

I will try it now and then respond. :)

0

Thanks a lot AirShow, I was able to make the complete code work.

Some Additions needed while populating the two select boxes were:

function setOptions(chosen) {
var selbox0 = document.form1.Iunit;
var selbox1 = document.form1.Ounit;

//Addition START
selbox0.options.length = 0;
selbox1.options.length = 0;
//Addition END
if (chosen == "1") { // Continued...

Reason:
If u won't add that it wont clear the select boxes and will keep adding the area units to length and so on if u change the selection.

Though Now I am facing 2 problems:
1. I want to show the output in the textbox beside the output unit selector's selection box. The problem is once the output is generated, the alert box & textbox shows the output and once clicked ok, the page will reload and output disappears.

The Problem can be seen here: http://saad749.freeoda.com/MetricConversions/conversions.html

I am using

document.form1.Oamount.value = valo;

for output. I want the textboxes to be filled until a new entity has been selected so how can I stop the page from realoading?.

2. I have yet not been successful for creating the temperature calculation :(. I tried to use if else[if chosen == '5'//"5",5 too// else {//the default method }] but the script was not calculating for the rest of the entities too, let alone temp. Any insight on this will be very helpful, I am trying my best but don't have much time left.

1

1. I want to show the output in the textbox beside the output unit selector's selection box. The problem is once the output is generated, the alert box & textbox shows the output and once clicked ok, the page will reload and output disappears.

I am using

document.form1.Oamount.value = valo;

for output. I want the textboxes to be filled until a new entity has been selected so how can I stop the page from realoading?.

That's very simple. Change the GO button from type="submit" to type="button" , otherwise the form submits and the page will refresh (with action="" , you get the same page again). You should then be able to get rid the alert.

2. I have yet not been successful for creating the temperature calculation :(. I tried to use if else[if chosen == '5'//"5",5 too// else {//the default method }] but the script was not calculating for the rest of the entities too, let alone temp. Any insight on this will be very helpful, I am trying my best but don't have much time left.

The Math
Remember all that graph work you did when you were 13 or so? Well it's probably more recent for you than for me!

All your conversions (including temperature) are linear so they fit the mathematical model y = mx + c . Rearranging the equation, we get, x = (y - c) / m . For temperature, both forms are needed in calculate().

Storing the values
For everything other than temperature, c == 0; the line on the graph passes through (0,0) and that's why the calculation is so simple. You just need to know m; the conversion factors to/from the chosen base unit, and that's what I suggested you code as option values.

For temperature, you need to know m AND c, so you need to devise a way to store two numbers in the value attribute of the temperature options (Fahrenheit, Celcius and Kelvin). You can do this by concatenating the two values (m and c) together with a separator (I used the beam symbol "|").

The most obvious BASE is Kelvin, because it is a genuine ratio scale (has a true zero). Trying to use either F or C as the BASE would be identical in principal but trickier to implement.

Encode Kelvin's m and c like this; params ..... ['Kelvin', "1|0"] ;

Now you have to work out the m and c for the other two scales.

  • ['Fahrenheit', "m|c"]
  • ['Celcius', "m|c"]

This took me a wee while and a lot of scribbling.

The calculation
For temperature, separate out the concatenated values (m and c, for the input and output scales) using javascript's String.split(separator_char) , which returns an array of string segments, eg.

valuesArray = ...form...Iunit[...form....Iunit.selectedIndex].value.split('|');
var m_in = Number(valuesArray[0]);
var c_in = Number(valuesArray[1]);

And the same again for ...form....Ounit.

Now, this is where you use the two forms of the linear equation.

  1. Input-value to BASE : use BASE = (val_in - c_in) / m_in .
  2. BASE to output-value: y = (BASE * m_out) + c_out .

It's a double linear transform, which can be implemented in two consecutive lines of js (or one if you want to get confused).

Then, write the output value to screen and you're home and dry.

It's harder to describe in words than than to write in Javascript so I'm sure it will all come clear as you start to code.

When you are all done, maybe we could exchange code. My page is currently 102 lines of HTML/javascript (but without your navigation etc).

Airshow

Edited by Airshow: n/a

0

What I was able to grasp from the explanation:
For Temp:
c = 9/5
m = 32
Default:
c=0;
m= conversion factors.

Some THing Iam not able understand is, Will a generalized function be produced as I have tried below or we will need to have a different case for temp.

var params = [
  		['Kelvin','1|0'],
		['Fahrenheit','9/5|32'],
  		['Celsius','9/5|32']
	];	


function calculate()
{

	var uniti = document.form1.Iunit.options[document.form1.Iunit.selectedIndex].value;
	var unito = document.form1.Ounit.options[document.form1.Ounit.selectedIndex].value;
	var vali = document.form1.Iamount.value;
	var c_in = 0;  //It is zero for others...
	
	var valuesArray = document.form1.Iunit.options[document.form1.Iunit.selectedIndex].value('|');
	unito = Number(valuesArray[0]); //Part I am little confused in..
	c_in = Number(valuesArray[1]); //If there is any other value it will take.

	var valo = (vali * uniti) + c_in;
	valo = (valo - c_in) / unito;

	document.form1.Oamount.value = valo;
}
1

You're nearly there.

The temperature params are difficult to construct. Remember that both C and F convert to/from K (the chosen base), not to/from each other.

For F, try (5/9)+'|459.4' (note that 5/9 is actually evaluated at this point).
For C, try '1|273' .

In calculate you will find it much easier to handle temperature and non-temperature separately. The generalized code is just for showing off.

I can't give you the whole thing but here's the template:

function calculate()
{
	var f = document.form1;//helps keep other statements short
	var valuesArrayI = f.Iunit.options[f.Iunit.selectedIndex].value.split('|');//the input arguments split into an array
	var valuesArrayO = f.Ounit.options[f.Ounit.selectedIndex].value.split('|');//the output arguments split into an array
	if(valuesArrayI.length == 1) {//non-temperature
		// Here apply your (existing?) formula, substituting:
		//   Number(valuesArrayI[0]) for uniti
		//   Number(valuesArrayO[0]) for unito
	}
	else {//temerature
		var BASE = ...  //here apply the second version of the linear equation (see earlier post)
		f.Oamount.value = ... //here apply the first version of the linear equation (see earlier post)
	}
}

Airshow

Edited by Airshow: n/a

Votes + Comments
Excellent Explanation
This question has already been answered. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.