@DanCouper I needed it to work correctly for all uppercase units too, like “KM”, so I used toUpperCase
and checked for uppercase units. So now “KM” and “km” both work. But when I try 3/4/5in
, it says, “invalid number” instead of “invalid number and unit”. It should report it as the latter.
Here’s my current code:
function ConvertHandler() {
const checkUnit = (input) => {
const result = input.match(/^(?<num>\d*(\.\d+)?(\/\d+(\.\d+)?)?)(?<unit>([a-zA-Z]+))$/);
if (result) {
switch (result.groups["unit"].toUpperCase()) {
case "MI":
case "GAL":
case "LBS":
case "KM":
case "L":
case "KG":
return true;
}
}
return false;
}
const checkNumber = (input) => {
const result = input.match(/^\d*(\.\d+)?(\/\d+(\.\d+)?)?/);
if (!result) {
return false;
}
return true;
}
const checkNumberAndUnit = (input) => {
if (!(checkNumber(input) && !checkUnit(input))) {
return false;
}
return true;
}
this.getNum = function(input) {
if (/^(km|kg|L|gal|lbs|mi)$/i.test(input)) {
// We only got a unit, so the number should be 1
return 1;
}
if (!checkNumber(input)) {
throw new Error("invalid number");
} else if (!checkNumberAndUnit(input)) {
throw new Error("invalid number and unit");
}
let number = 0;
const result = input.match(/^(?<num>\d*(\.\d+)?(\/\d+(\.\d+)?)?)(?<unit>([a-zA-Z]+))$/);
// if there was a match, result is truthy
if (result) {
number = result.groups["num"];
// check if we've got a fraction (indexOf returns -1 when the character is not found)
if (number.toString().indexOf("/") !== -1) {
const numbers = number.toString().split("/");
// if there are more than two elements in the numbers array, it's invalid
// because this means it's a double (or more) fraction
if (numbers.length > 2) {
throw new Error("invalid number");
} else if (numbers.length === 2) {
const numerator = numbers[0];
const denominator = numbers[1];
number = Number((numerator / denominator).toString());
}
} else {
number = Number(result.groups["num"]);
}
} else {
throw new Error("invalid number");
}
return number;
};
this.getUnit = function(input) {
const result = input.match(/^(?<num>\d*(\.\d+)?(\/\d+(\.\d+)?)?)(?<unit>([a-zA-Z]+))$/i);
return result.groups["unit"];
};
this.getReturnUnit = function(initUnit) {
let result;
switch (initUnit.toString().toUpperCase()) {
case "MI":
result = "km";
break;
case "GAL":
result = "L";
break;
case "KM":
result = "mi";
break;
case "LBS":
result = "kg";
break;
case "KG":
result = "lbs";
break;
case "L":
result = "gal";
break;
default:
throw new Error("invalid unit");
}
return result;
};
this.spellOutUnit = function(unit) {
let result;
if (unit) {
switch (unit.toString().toUpperCase()) {
case "MI":
result = "miles";
break;
case "GAL":
result = "gallons";
break;
case "KM":
result = "kilometers";
break;
case "LBS":
result = "pounds";
break;
case "KG":
result = "kilograms";
break;
case "L":
result = "liters";
break;
default:
throw new Error("invalid unit");
}
return result;
}
};
this.convert = function(initNum, initUnit) {
const galToL = 3.78541;
const lbsToKg = 0.453592;
const miToKm = 1.60934;
let result;
switch (initUnit.toString().toUpperCase()) {
case "GAL":
result = initNum * galToL;
break;
case "LBS":
result = initNum * lbsToKg;
break;
case "MI":
result = initNum * miToKm;
break;
case "L":
result = initNum / galToL;
break;
case "KG":
result = initNum / lbsToKg;
break;
case "KM":
result = initNum / miToKm;
break;
}
if (result) {
result = result.toFixed(5);
return result;
}
};
this.getString = function(initNum, initUnit, returnNum, returnUnit) {
let result = `${initNum} ${this.spellOutUnit(initUnit)} converts to ${returnNum} ${this.spellOutUnit(returnUnit)}`;
return result;
};
}
module.exports = ConvertHandler;
Can you tell what the problem might be?
Thanks in advance.
Edit: I was erroneously doing switch (result.groups["num"].toUpperCase()) {
in checkUnit
earlier which I fixed, but now it’s rejecting even completely valid input with an invalid number and unit
error. What did I do wrong? I suspect I messed something up when I tried to make it case-insensitive.
Edit2: I figured out the issue with everything being rejected: the problem was in checkNumberAndUnit
where I was negating the if-condition completely.
const checkNumberAndUnit = (input) => {
if (!checkNumber(input) && !checkUnit(input)) {
return false;
}
return true;
}
I had it like this originally: !(checkNumber(input) && !checkUnit(input))
. That was probably the reason.
But I need to return this JSON object:
{"initNum":4,"initUnit":"km","returnNum":2.48549,"returnUnit":"mi","string":"4 kilometers converts to 2.48549 miles"}
For both “KM” and “km” for initUnit
, for example. Right now it returns one where the initUnit
case is dependent on the case it’s entered as, so e.g. entering “KM” would give me an object that has “KM” as initUnit
. It should be “km” in either case.