Check a string to see if possible to convert to Number

I have a string that could be a number or text. If it's a number I want to do a function with it, but if it's just text I want to display the text. If I just try

variable = mValue.toNumber();

and it's text then it crashes. Is there a way to try to convert and catch the error?
  • My first question, is why are you using the same string for both?


    You could walk the string and set a flag if you see something other that 0-9, but would you need to do that often or just once at startup?
  • I'm with Jim. Where are you getting data that is a string or a number that you don't have some control over? I'm sure there is a reasonable explanation, it just seems like a really weird situation to get into.

    Assuming that you have good reason, have you tried catching the exception thrown when doing the toNumber() conversion? If you can catch it (I'm not certain that you can), you could just ignore it...

    // returns `s' as a Lang.Number if possible, otherwise returns `s'
    function safe_toNumber(s)
    {
    // this may not work!!
    try {
    return s.toNumber();
    }
    catch (ex) {
    // ignore the exception
    }

    return null;
    }


    If that doesn't work, the only thing that I know of to do is to examine each character and verify they are numbers. I believe that this will work to verify the conversion is legal and get the result (if you can assume ASCII)...

    const _digits = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];

    // returns null if `s' is not composed entirely of base-10 digits, otherwise converts
    // `s' to a Number and returns the value.
    function safe_toNumber(s)
    {
    if (s.length() == 0) {
    return null;
    }

    s = s.toCharArray();

    var value = 0;
    for (var i = 0, j = s.length(); i < j; ++i) {
    var d = _digits.indexOf(s);
    if (d < 0) {
    return null;
    }

    value = (value * 10) + d;
    }

    return value;
    }
    [/code]
  • I did try to catch the error, but for .toNumber() it doesn't seem to catch it. I will try option 2. It's for a very specific use case using nameOfNextPoint. Thanks guys!
  • Any Luck?

    I was fighting the same thing the entire flight from LAX to KOA. What did you end up doing?
  • The try/catch thing does not work in the simulator. I haven't tried it on a device, but I have doubts that it will work if it doesn't work on the simulator. If you want to verify the conversion is possible, you have to do it yourself.

    Travis
  • Ended up writing a brute force method

    Here's the code I ended up with:

    function intValue(stringIn){
    //converts the string to a integer or returns null on error (non digits, null, or 0 length)
    if ((stringIn == null) || (stringIn.length() == 0)) {
    return null;
    }
    var bytes = stringIn.toUtf8Array(); //array of bytes
    var len = stringIn.length(); //length of the string
    var val = 0; //The return value
    var ix; //just a counter

    for(ix=0;ix<len;ix++){
    if((bytes[ix] < 48) || //byte code is less than that of '0'
    (57 < bytes[ix])){ //or it is greater than '9'
    return null;
    } else {
    val = val*10+ (bytes[ix]-48); //shift and add the ones digit
    }
    }
    return val; //looks like it worked.
    }
  • Oh, yeah.... I figured out why the error could not be caught. For some reason when trying to convert a non-numeric string to a number a "Symbol Not Found" error is generated when toNumber is used. A "nan" is generated when toFloat is called with a non-numeric string. In the programmer's guide says that that the "Symbol Not Found" is a fatal error and could not be caught. The undocumented "nan" I can understand (I would like a have a way to test for it), but the undocumented "Symbol Not Found" baffles me. For that matter, the inconsistent and undocumented behavior does too. I would have expected to have a defined non-fatal testable error condition / exception generated.
  • Former Member
    Former Member over 8 years ago
    Funny thought came to my mind: if toNumber() statement returns "nan" and doesn't raise an exception, then maybe this check can be simplified to
    var outNumber = inString.toNumber();
    outNumber = outNumber instanceof Number ? outNumber : 0;

    Don't have an access to my Eclipse right now, thus cannot verify whether this will actually work.
  • For some reason when trying to convert a non-numeric string to a number a "Symbol Not Found" error is generated when toNumber is used.

    Not in my testing.

    A "nan" is generated when toFloat is called with a non-numeric string.

    Again, this is not what I'm seeing. I get exactly the same exception for toNumber() and toFloat(). In either case, if the input string has an optional sign and a leading digit the that part of the string is parsed. If there is no leading digit, then the exception is thrown. Here is a test and the output to show it...

    using Toybox.Application as App;
    using Toybox.System as Sys;
    using Toybox.Lang as Lang;

    function attempt_toNumber(logger, s)
    {
    logger.debug(Lang.format("\"$1$\".toNumber()", [ s ]));
    var n = s.toNumber();
    logger.debug(Lang.format(" $1$", [ n ]));
    }

    (:test) function test_to_number(logger)
    {
    attempt_toNumber(logger, "-999");
    attempt_toNumber(logger, "-100");
    attempt_toNumber(logger, "-10");
    attempt_toNumber(logger, "-1");
    attempt_toNumber(logger, "-0");
    attempt_toNumber(logger, "0");
    attempt_toNumber(logger, "+0");
    attempt_toNumber(logger, "+1");
    attempt_toNumber(logger, "+10");
    attempt_toNumber(logger, "+100");
    attempt_toNumber(logger, "+999");

    attempt_toNumber(logger, "-0777");
    attempt_toNumber(logger, "-0100");
    attempt_toNumber(logger, "-010");
    attempt_toNumber(logger, "-01");
    attempt_toNumber(logger, "-00");
    attempt_toNumber(logger, "00");
    attempt_toNumber(logger, "+00");
    attempt_toNumber(logger, "+01");
    attempt_toNumber(logger, "+010");
    attempt_toNumber(logger, "+0100");
    attempt_toNumber(logger, "+0777");

    attempt_toNumber(logger, "-0xFFF");
    attempt_toNumber(logger, "-0x100");
    attempt_toNumber(logger, "-0x10");
    attempt_toNumber(logger, "-0x1");
    attempt_toNumber(logger, "-0x0");
    attempt_toNumber(logger, "0x0");
    attempt_toNumber(logger, "+0x0");
    attempt_toNumber(logger, "+0x1");
    attempt_toNumber(logger, "+0x10");
    attempt_toNumber(logger, "+0x100");
    attempt_toNumber(logger, "+0xFFF");

    attempt_toNumber(logger, "-1abc");
    attempt_toNumber(logger, "1abc");
    attempt_toNumber(logger, "+1abc");

    attempt_toNumber(logger, "abc");

    return true;
    }


    function attempt_toFloat(logger, s)
    {
    logger.debug(Lang.format("\"$1$\".toFloat()", [ s ]));
    var f = s.toFloat();
    logger.debug(Lang.format(" $1$", [ f ]));
    }

    (:test) function test_toFloat(logger)
    {
    attempt_toFloat(logger, "-999");
    attempt_toFloat(logger, "-100");
    attempt_toFloat(logger, "-10");
    attempt_toFloat(logger, "-1");
    attempt_toFloat(logger, "-0");
    attempt_toFloat(logger, "0");
    attempt_toFloat(logger, "+0");
    attempt_toFloat(logger, "+1");
    attempt_toFloat(logger, "+10");
    attempt_toFloat(logger, "+100");
    attempt_toFloat(logger, "+999");

    attempt_toFloat(logger, "-1abc");
    attempt_toFloat(logger, "1abc");
    attempt_toFloat(logger, "+1abc");

    attempt_toFloat(logger, "abc");

    return true;
    }

    class TestApp extends App.AppBase
    {
    function initialize() {
    AppBase.initialize();
    }

    function getInitialView() {
    return null;
    }
    }


    Here is the output...

    Connecting...
    Connecting to device...
    Device Version 0.1.0
    Device id 1 name "A garmin device"
    Shell Version 0.1.0
    ------------------------------------------------------------------------------
    Executing test test_toNumber...
    DEBUG (10:39): "-999".toNumber()
    DEBUG (10:39): -999
    DEBUG (10:39): "-100".toNumber()
    DEBUG (10:39): -100
    DEBUG (10:39): "-10".toNumber()
    DEBUG (10:39): -10
    DEBUG (10:39): "-1".toNumber()
    DEBUG (10:39): -1
    DEBUG (10:39): "-0".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "0".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "+0".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "+1".toNumber()
    DEBUG (10:39): 1
    DEBUG (10:39): "+10".toNumber()
    DEBUG (10:39): 10
    DEBUG (10:39): "+100".toNumber()
    DEBUG (10:39): 100
    DEBUG (10:39): "+999".toNumber()
    DEBUG (10:39): 999
    DEBUG (10:39): "-0777".toNumber()
    DEBUG (10:39): -777
    DEBUG (10:39): "-0100".toNumber()
    DEBUG (10:39): -100
    DEBUG (10:39): "-010".toNumber()
    DEBUG (10:39): -10
    DEBUG (10:39): "-01".toNumber()
    DEBUG (10:39): -1
    DEBUG (10:39): "-00".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "00".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "+00".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "+01".toNumber()
    DEBUG (10:39): 1
    DEBUG (10:39): "+010".toNumber()
    DEBUG (10:39): 10
    DEBUG (10:39): "+0100".toNumber()
    DEBUG (10:39): 100
    DEBUG (10:39): "+0777".toNumber()
    DEBUG (10:39): 777
    DEBUG (10:39): "-0xFFF".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "-0x100".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "-0x10".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "-0x1".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "-0x0".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "0x0".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "+0x0".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "+0x1".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "+0x10".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "+0x100".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "+0xFFF".toNumber()
    DEBUG (10:39): 0
    DEBUG (10:39): "-1abc".toNumber()
    DEBUG (10:39): -1
    DEBUG (10:39): "1abc".toNumber()
    DEBUG (10:39): 1
    DEBUG (10:39): "+1abc".toNumber()
    DEBUG (10:39): 1
    DEBUG (10:39): "abc".toNumber()
    Failed invoking <symbol>
    Invalid Value
    in attempt_toNumber (source\TestApp.mc:8)
    in test_toNumber (source\TestApp.mc:54)
    in runTest (UnitTests:31)
    ERROR
    ------------------------------------------------------------------------------
    Executing test test_toFloat...
    DEBUG (10:39): "-999".toFloat()
    DEBUG (10:39): -999.000000
    DEBUG (10:39): "-100".toFloat()
    DEBUG (10:39): -100.000000
    DEBUG (10:39): "-10".toFloat()
    DEBUG (10:39): -10.000000
    DEBUG (10:39): "-1".toFloat()
    DEBUG (10:39): -1.000000
    DEBUG (10:39): "-0".toFloat()
    DEBUG (10:39): -0.000000
    DEBUG (10:39): "0".toFloat()
    DEBUG (10:39): 0.000000
    DEBUG (10:39): "+0".toFloat()
    DEBUG (10:39): 0.000000
    DEBUG (10:39): "+1".toFloat()
    DEBUG (10:39): 1.000000
    DEBUG (10:39): "+10".toFloat()
    DEBUG (10:39): 10.000000
    DEBUG (10:39): "+100".toFloat()
    DEBUG (10:39): 100.000000
    DEBUG (10:39): "+999".toFloat()
    DEBUG (10:39): 999.000000
    DEBUG (10:39): "-1abc".toFloat()
    DEBUG (10:39): -1.000000
    DEBUG (10:39): "1abc".toFloat()
    DEBUG (10:39): 1.000000
    DEBUG (10:39): "+1abc".toFloat()
    DEBUG (10:39): 1.000000
    DEBUG (10:39): "abc".toFloat()
    Failed invoking <symbol>
    Invalid Value
    in attempt_toFloat (source\TestApp.mc:63)
    in test_toFloat (source\TestApp.mc:85)
    in runTest (UnitTests:38)
    ERROR

    ==============================================================================
    RESULTS
    Test: Status:
    test_toNumber ERROR
    test_toFloat ERROR
    Ran 2 tests

    FAILED (failures=0, errors=2)
    Connection Finished
    Closing shell and port


    It isn't a Symbol Not Found error, it is an Invalid Value exception. The result isn't really any different though, since neither of them can be caught.

    I would have expected to have a defined non-fatal testable error condition / exception generated.

    Agreed. I think I'd prefer that null were returned if the conversion isn't possible, but it isn't really my place to make suggestions.

    Funny thought came to my mind: if toNumber() statement returns "nan" and doesn't raise an exception, then maybe this check can be simplified...

    As pointed out above, I don't ever see NAN get returned, so I'm not sure how this could be helpful.
  • Former Member
    Former Member over 8 years ago
    We've had a ticket around for a while to make "invalid value" errors catchable. I'll see if we can get a priority bump on that issue, or maybe get the behavior of this API modified.