Divisibility By Three, Six & Nine

When people say that being able to do simple math is what counts the most, they may not be far off. Did you know that as long as you can add, you can look at any number and tell whether or not the number is evenly divisible by three and/or nine?

Let’s start off with a short example. Do you know if 528 is divisible by 3 and/or 9? To figure it out, you can do the following:

  1. Add all of the digits together:
    5 + 2 + 8 =  15
  2. Since 15 is still more than one digit long, add up those digits:
    1 + 5 = 6
  3. Since the one digit isn’t a 9, we automatically know that 528 isn’t divisible by 9.
  4. Since 3 fits into 6 evenly (3 + 3), we know that 528 is evenly divisible by 3.

Believe it or not, this process works for all numbers!  Here is an example of checking a really long number:  1234567890.

  1. Add all of the digits together:
    1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 = 45
  2. Since 45 is still more than one digit long, add up those digits:
    4 + 5 = 9
  3. Since the one digit is 9, we know that 1234567890 is evenly divisible by 9.  (By the way, since it is divisible by nine, it must be divisible by 3.)
  4. Since 3 fits into 9 evenly (3 + 3 + 3), we know that 1234567890 is also evenly divisible by 3.

It is important to note that you could stop at step 2 since 45 is evenly divisible by 9.  The only reason I took it a step further was to show the easiest way to determine divisibility for people who aren’t necessarily good at math.

For those of you who are wondering why this three and nine process works, it is due to the fact that we represent numbers in base 10.  If we represented our number in base 8 (octal), we would be able to use this same process to easily determine divisibility by 7.  If we represented our number in base 16 (hexadecimal), we would be able to use this same process to easily determine divisibility by 3, 5, and 15.

What about divisibility by 6? Since we already know how to determine divisibility by 3, all we have to do is also check whether or not the original number is even. If it is divisible by 3 and even (divisible by 2), this means that it is divisible by 6.

Solid Type Checking Functions

Now Available in YourJS

John Resig definitely has some awesome code inside of jQuery. One of the things that I think is cool is how jQuery does type checking. After looking through the code, I developed my own flavor of functions using the same principle. Here is the code that I was able to come up with:

(function() {
  var types = "Object null undefined Array Boolean Date Error Function Number RegExp String".split(" ");
  // Defines the getTypeOf() function on the local and global level.
  // Local variable also declared to prevent errors that would occur
  // from externally redefining getTypeOf().
  var getTypeOf = this.getTypeOf = function(arg) {
    return arg == null
      ? arg + ""
      : types[Object.prototype.toString.call(arg)] || "Object";
  };
  // Defines isNull(), isUndefined(), isArray(), isBoolean(), isDate(),
  // isError(), isFunction(), isNumber(), isObject(), isRegExp(), and
  // isString() on the global level.
  for(var i = types.length - 1; i >= 0; i--) {
    types[i > 1 ? "[object " + types[i] + "]" : types[i]] = types[i];
    (function(type) {
      this["is" + type.charAt(0).toUpperCase() + type.substring(1)] = function(arg) {
        return getTypeOf(arg) == type;
      };
    })(types[i]);
  }
  i = this.isObject;
  this.isObject = function(arg, allowAnyType) {
    return allowAnyType ? arg != null : i(arg);
  };
})();

Basically, my getTypeOf() function is just like jQuery’s jQuery.type() function with one exception. All of the types that are returned, with the exception of undefined and null, are returned as in the same casing as their constructor. In other words, where jQuery.type(/re/) would return "regexp", getTypeOf(/re/) would return "RegExp".

The other function that are created are isNull(), isUndefined(), isArray(), isBoolean(), isDate(), isError(), isFunction(), isNumber(), isObject(), isRegExp(), and isString(). The isObject() function is can take one or two parameters. If one parameter is passed in or two are passed in and the second one evaluates to false, the boolean returned will indicate whether or not the specified argument was an object that is not an array, boolean, date, error, function, number, regular expression or string. If two parameters are passed and the second evaluates to true, the function will return true if the argument is not null or undefined, otherwise it will return false. All of the other functions take one parameter and return a boolean indicating whether or not the type is as specified by the function name.

The following is how I tested the functions:

// Test the specified object on all of the new functions.
function testOnAll(obj, strObj) {
  var arr = "Array Boolean Error Date Function Null Number Object RegExp String Undefined".split(" ");
  for(var i = arr.length - 1; i >= 0; i--) {
    if(arr[i] == "Object") {
      arr.splice(i + 1, 0, eval("is" + arr[i])(obj, true) + " <= isObject(" + strObj + ", true)");
    }
    arr[i] = eval("is" + arr[i])(obj) + " <= is" + arr[i] + "(" + strObj + ")";
  }
  arr.unshift('"' + getTypeOf(obj) + '" <= getTypeOf(' + strObj + ")");
  alert(arr.join("\n"));
}
// All of the tests which display the type of the object and
// the evaluation after using all of the new is functions.
testOnAll("Hello", '"Hello"');
testOnAll([1,2,"Three",[4,[[5]]]], '[1,2,"Three",[4,[[5]]]]');
testOnAll(false, 'false');
testOnAll(null, 'null');
testOnAll(undefined, 'undefined');
testOnAll(window, 'window');
testOnAll(alert, 'alert');
testOnAll(function(){}, 'function(){}');
testOnAll(/#/, '/#/');
testOnAll(new Date(), 'new Date()');
testOnAll(2342, '2342');
testOnAll(3981.3491, '3981.3491');
testOnAll(NaN, 'NaN');
testOnAll(-Infinity, '-Infinity');
testOnAll({}, '{}');
testOnAll(new Object(), 'new Object()');
testOnAll(new (function(){}), 'new (function(){})');
testOnAll(new Error("an error"), 'new Error("an error")');
testOnAll(new EvalError("an error"), 'new EvalError("an error")');
testOnAll(new SyntaxError("an error"), 'new SyntaxError("an error")');
testOnAll(new TypeError("an error"), 'new TypeError("an error")');
testOnAll(new RangeError("an error"), 'new RangeError("an error")');
testOnAll(new ReferenceError("an error"), 'new ReferenceError("an error")');
testOnAll(new URIError("an error"), 'new URIError("an error")');

I think it is important to note that using getTypeOf(NaN) will result in "Number" because NaN is a number object believe it or not. This is the same reason that isNumber(NaN) returns true. As far as I can tell, all of the other tests evaluate as expected.

Function Overloading in JavaScript

Method overloading is one of the nice things that exists in real programming languages such as Java. Even though JavaScript starts with “Java”, there are very few similarities between the two languages. Still, due to the power of the scripting language, there is a way to overload functions. The most commonly used method is to simply write some if statements to examine the arguments. Still, I thought it would be nice to be able to overload functions in a more natural way. Therefore, I wrote the following prototype function for functions which will be included in the next version of jPaq:

// Function.prototype.overload - By Chris West - MIT Licensed
Function.prototype.overload = function(fnToAdd, objFilters) {
  var fnOriginal = this;
  return function() {
    var fnToUse = fnToAdd, args = arguments, len = args.length;
    if(objFilters) {
      for(var key in objFilters) {
        if((key == "all" && !objFilters[key].call(this, args))
           || (key == "length" && len != objFilters[key])
           || (/^\d+$/.test(key) && !objFilters[key](args[key]))) {
          fnToUse = fnOriginal;
          break;
        }
      }
    }
    else if(fnToAdd.length !== len) {
      fnToUse = fnOriginal;
    }
    return fnToUse.apply(this, args);
  };
};

First Parameter (fnToAdd)

The first parameter that you pass in must be the function that you want to overload the current function.

Second Parameter (objFilters)

The second parameter is optional. If it is not specified, the only way that fnToAdd will be used is if the amount of arguments specified in the header of that function matches the number of arguments passed in. If the second parameter is specified, it must be an object literal which will be used to determine whether or not to use this function instead of the original. To filter based on a specific parameter, you should make the key be the index of the parameter and the value should be the function that will check that parameter. This filter function will only receive the specified parameter as its only argument. If you want to filter by the amount of parameters passed, you can define the length of the object literal.  If you want use an all-purpose filter function, you can define the all property of the object literal as a function which will receive all of the parameters just as the function would.  In addition, this all-purpose filter function will also receive the this object of the function.  Just as in the case of the other filter functions, if the return value evaluates to false, the original function will be used, otherwise the new function will be used.

Test Code

Now, instead of trying to clarify the above description with more words, let’s let the code do the talking. Assuming that the definition for Function.prototype.overload() appears before this code, the following will test and prove that function overloading can be quite easy:

// Returns a boolean indicating whether or not the argument is an array.
function isArray(arg) {
  return Object.prototype.toString.call(arg) == "[object Array]";
}

// Returns a boolean indicating whether or not the argument is a number.
function isNumber(arg) {
  return Object.prototype.toString.call(arg) == "[object Number]";
}

// The default callAny() function.
function callAny() {
  alert("Default callAny() function was called.");
};

// The callAny() function that will be used if the first parameter is an array.
function callArray(arr) {
  alert("callArray() called with [" + arr.join(", ") + "]");
}

// The callAny() function that will be used if two numbers are passed.
function callNum(num1, num2) {
  alert("callNum() called with " + num1 + " and " + num2);
}

// The callAny function that will be used if no arguments are passed.
function callNone() {
  alert("callNone() called without arguments as expected.");
}

// Easily overload the callAny() function.
callAny = callAny
  .overload(callNone)
  .overload(callArray, {0 : isArray})
  .overload(callNum, {0 : isNumber, length : 2});

/********** Example calls the callAny() **********/
// Calls the default function.
callAny();

// Calls the callNum() function.
callAny(1, 2);

// Calls the default function.
callAny(3);

// Calls the callArray() function.
callAny(["Hello", "World!!!"]);

The first thing to note is that the isNumber() and isArray() functions are defined to filter the arguments. Both callNone(), callArray() and callNum() will overload callAny(). If callAny() is called without any parameters, it acts as if callNone() was invoked. If callAny() is called with the first parameter being an array, it acts as if callArray() was called. If callAny() is called with the first parameter being a number and is called with exactly two parameters, it acts as if callNum() was called. In all other cases the default version of callAny() is called.

Future

I personally think that this may become useful for other JavaScript programmers. Therefore, I plan on incorporating this function into jPaq. Unfortunately, I don’t have an exact date for the next release, therefore, I am fine with you using this code as long as you give me credit for my code. 8)