Sentinels in JavaScript

Can you find the substring?

Can you find the substring?

In older procedural languages, the return values that came back from a function were restricted. If you said you were going to return a number, you returned a number. If you wanted to sometimes return a number, but other times return an indication of failure, you resorted to what is known as a “sentinel value” to return the failure.

A sentinel value is a number that doesn’t represent the answer to the problem that the function was asked to solve, but instead flags the caller to the fact that something rather out-of-the-ordinary has happened. For example, a value of -1 can indicate the end of a file being read in.

The problem with a sentinel is that it means nothing special to the language. The programmer has to keep in mind that the sentinel exists and that it has to be handled on every call that could possibly generate the sentinel value. Programmers are notorious for paying attention to and handling sentinels only when they crash a program.

Also, the programmer has to be careful in choosing the value. Is it 0? -1? 99? 255? 999? -999? MAXINT? The choice can bite you if you’ve misunderstood the possible values that can be generated in your function. I regret to inform you that I once wrote a BASIC program that had three different sentinel values returned from three different subroutines!

Sentinel values sound old and busted, don’t they? Their usage fell a bit once we could pass around pointers to data structures. With a little more elbow room, we could put a “success” boolean right up front in the data structure and do away with a lot of sentinels.

They Live!

But sentinels are still around. In JavaScript, the string method “indexOf” returns a -1 if the needle (substring) can’t be found in the haystack (the string to be searched).

Let’s look at a few different ways we can deal with that awkward -1.

function wrappedIndexOf(needle,haystack) {
    var res=haystack.indexOf(needle);
    if (res===-1) {
        res=false;
    }
    return res;
}

And you call it like this:

res=wrappedIndexOf(needle,haystack);
if (res!==false) {
    location=res;
}

We haven’t done much here but replace the need to check explicitly for -1 with a need to check explicitly for false. Too weird to sometimes return a number and sometimes return a boolean? Yeah, probably. I like it a bit better than the numeric sentinel because the calling code makes the exception check a bit more obvious. Because JavaScript functions can return just about anything (including wild things like anonymous functions), you always need to think about what can come out of a function, so what’s important is a consistent convention.

Similarly, you could return null or undefined.

But let’s move on to another solution–returning an object.

function wrappedIndexOf(needle,haystack) {
    var res=haystack.indexOf(needle);
    if (res===-1) {
        res={"success":false,"value":-1};
    } else {
        res={"success":true,"value":res};
    }
    return res;
}

And you call it like this:

obj=wrappedIndexOf(needle,haystack);
if (obj.success) {
    location=obj.value;
}

Note that I’ve left the -1 in obj.value, so you can still use that as a sentinel if you like.

What makes JavaScript really good for this task? Its object literals. You don’t need to have a structure or class around to hold the extra info. You just build the object on the fly and return it.

About these ads

10 Comments

  1. jimmyp22 said,

    February 26, 2009 at 8:34 pm

    Thanks for this. I always wondered why the indexOf method returned -1 instead of false. Object literals are one of those hidden gems in JavaScript, gotta love ‘em!

  2. TNO said,

    February 27, 2009 at 12:07 am

    @jimmyp22

    Because false == 0 == the first character in the string.

    personally I think strings should have been a bit different:

    If something was not found in an string it returns undefined.
    -1 could be used to refer to the last element in the string…but oh well.

    Sadly I admit to using sentinal values from time to time when manipulating ADO objects…..

  3. Nosredna said,

    February 27, 2009 at 5:33 pm

    jQuery has one of these, too. $.inArray() returns a -1 when there are no matches.

    Many sentinels are holdovers from other languages. -1 is used as a sentinel in many libraries.

  4. Mike said,

    March 1, 2009 at 11:23 am

    Just passing by.Btw, your website have great content!

    [Edit: Thanks Mike! Note that I deleted your get rich quick links, you smarmy salesman you!]

  5. monos said,

    March 2, 2009 at 11:32 am

    you are talking about sentinels – which should be avoided for the reasons you stated :)

    I would rather have a Array.contains(String) – the name tells me what it does and it returns true / false. “wrappedIndexOf” is a rough name :)

    but maybe, very likly, that’s beside your point, since you want to explain sentinels :)

  6. Nosredna said,

    March 2, 2009 at 2:41 pm

    monos, thanks for the comment. The only goal of my blog is to get people thinking about the unusual parts of the language. Sometimes the code is useful, and sometimes it’s just illustrative.

  7. Fra_T said,

    August 4, 2009 at 4:37 pm

    A shorter version :P

    function wrappedIndexOf(needle,haystack) {
    var res=haystack.indexOf(needle);
    return {“success”:!!~res, “value”:res};
    }

  8. reply said,

    September 21, 2009 at 12:00 pm

    this one is better:

    http://css-tricks.com/snippets/javascript/javascript-array-contains/

  9. December 1, 2010 at 9:05 pm

    well of course, everyone loves to get rich but not everyone would love to do hard work ~:-

  10. Ben said,

    December 19, 2011 at 9:41 pm

    You might consider something to the tune of:

    String.prototype.indexOf = function(indexOf) {
    return function() {
    var result = new Number(indexOf.apply(this, arguments));
    result.success = result == -1 ? false : true;
    return result;
    }
    }(String.prototype.indexOf);

    This variation has all the virtues of your object-literal approach, with the added benefit of the result remaining the number, albeit a constructed Number object. This way, you can test result.success and use result itself in a call to String.substr or the likes.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: