Array#wrap Useful JavaScript Game Extension #29

/**
 * Pretend the array is a circle and grab a new array containing length elements.
 * If length is not given return the element at start, again assuming the array
 * is a circle.
 *
 * @param {Number} start The index to start wrapping at, or the index of the
 * sole element to return if no length is given.
 * @param {Number} [length] Optional length determines how long result
 * array should be.
 * @returns The element at start mod array.length, or an array of length elements,
 * starting from start and wrapping.
 * @type Object or Array
 */
Array.prototype.wrap = function(start, length) {
  if(length != null) {
    var end = start + length;
    var result = [];

    for(var i = start; i < end; i++) {
      result.push(this[i.mod(this.length)]);
    }

    return result;
  } else {
    return this[start.mod(this.length)];
  }
};

Simple use:

[1, 2, 3].wrap(-1) => 3

[1, 2, 3].wrap(6) => 1

Or get fancy and tile your kitchen:

["w", "o", "o", "o"].wrap(0, 16) => ["w", "o", "o", "o", "w", "o", "o", "o", "w", "o", "o", "o", "w", "o", "o", "o"]

Array#clear Useful JavaScript Game Extension #28

Here’s a useful little method that empties out an array. Sure you could type myArray.length = 0; but that is kind of weird. For some reason myArray.clear() just makes more sense to me.

/**
* Empties the array of it's contents. It is modified in place.
*
* @type Array
* @returns this, now emptied.
*/
Array.prototype.clear = function() {
  this.length = 0;
  return this;
};

The series continues…

Javascript String#constantize and String#parse – Useful Extensions 26 and 27

Here are two extensions to the String class that I’ve found quite useful. constantize and parse. They are in CoffeeScript because JavaScript fills me with a growing disgust.

String::constantize = () ->
  if this.match /[A-Z][A-z]*/
    eval("var that = #{this}")
    that
  else
    undefined

String::parse = () ->
  try
    return JSON.parse(this)
  catch e
    return this

constantize is based on the ActiveSupport method. It transforms a string that represents the name of a class into a reference to that class. It uses eval to accomplish this, but until JavaScript gets more extensive reflection capabilities it’s probably the best we can do.

Parse is a useful way to convert the string into a raw JavaScript type if possible, otherwise it returns just the string itself. For example if I have a string "false", "false".parse() will return the JavaScript value false. Likewise '{"a": 7}'.parse() will return a JavaScript object with a property a that has the value 7.

Pretty sweet.

Number#snap Useful JavaScript Game Extension #25

Well my friends, it’s been quite some time. So without further ado back to the series!

/**
 * Returns the the nearest grid resolution less than or equal to the number.
 *
 *   EX:
 *    (7).snap(8) => 0
 *    (4).snap(8) => 0
 *    (12).snap(8) => 8
 *
 * @param {Number} resolution The grid resolution to snap to.
 * @returns The nearest multiple of resolution lower than the number.
 * @type Number
 */
Number.prototype.snap = function(resolution) {
  return (this / resolution).floor() * resolution;
};

This handy method is useful for when you need to snap input to a grid (like in a tile editor).

I hope you enjoyed this installment of the wonderful 256 part series on JavaScript game extensions.

Array#invoke Useful JavaScript Game Extension #24

Array#invoke is a method to make the common task of inoking a method on all elements in an array easier. Though you could use your own anonymous function as a map or each iterator to achieve the same result, this shortcut method makes it clear what the intention is and removes the extra syntax clutter.

What invoke does is to call the named method on each element in the array, passing in any arguments if given.

/**
* Invoke the named method on each element in the array
* and return a new array containing the results of the invocation.
*
* @param {String} method The name of the method to invoke.
* @param [arg...] Optional arguments to pass to the method being invoked.
*
* @type Array
* @returns A new array containing the results of invoking the
* named method on each element.
*/
Array.prototype.invoke = function(method) {
  var args = Array.prototype.slice.call(arguments, 1);

  return this.map(function(element) {
    return element[method].apply(element, args);
  });
};

Here are some examples of it in action:

[1.1, 2.2, 3.3, 4.4].invoke("floor")
=> [1, 2, 3, 4]

['hello', 'world', 'cool!'].invoke('substring', 0, 3)
=> ['hel', 'wor', 'coo']

The result of each invocation is returned in an array. This allows for easy chaining of data transformations:

values.invoke("scale", 4).invoke("subtract", 3)...

Fans of Ruby and Rails will know about Symbol#to_proc, a closely related idiom:

names = people.map &:name

Thanks for reading!

Select and Reject – Useful JavaScript Game Extensions #22 and #23

Remember back when we discussed Array#partition? Cool, because having a partition method makes these next two a breeze!

/**
 * Return the group of elements for which the iterator's return value is true.
 *
 * @param {Function} iterator The iterator receives each element in turn as
 * the first agument.
 * @param {Object} [context] Optional context parameter to be
 * used as `this` when calling the iterator function.
 *
 * @type Array
 * @returns An array containing the elements for which the iterator returned true.
 */
Array.prototype.select = function(iterator, context) {
  return this.partition(iterator, context)[0];
};

/**
 * Return the group of elements for which the iterator's return value is false.
 *
 * @param {Function} iterator The iterator receives each element in turn as
 * the first agument.
 * @param {Object} [context] Optional context parameter to be
 * used as `this` when calling the iterator function.
 *
 * @type Array
 * @returns An array containing the elements for which the iterator returned false.
 */
Array.prototype.reject = function(iterator, context) {
  return this.partition(iterator, context)[1];
};

We can cheat quite a bit by delegating the brunt of the work to partition and just return the partition that matches what we want, the true partition in the case of select, and the false partition in the case of reject.

Another thing to note is that in most modern browsers JavaScript already has an equivalent method to select implemented. It’s called filter. It may make sense to use select as an alias for filter, in the cases where it exists.

The important thing to notice is that by implementing robust extensions that are general it opens the door for simpler implementations of specific common cases. This in turn leads to an increase in consistency and a reduction of errors because there is only one method that does the real work instead of several similar methods. When the code changes there is only one place necessary to make the change in rather than many places, and that’s always a good thing.

Array#copy – Useful JavaScript Game Extensions #21

It’s not unheard of to want to copy an array. Though usually you’d prefer to use map, select, or reject, sometimes you just need your own copy. Fortunately the copy method is extremely easy to implement by leveraging the existing Array#concat method.

/**
* Creates and returns a copy of the array. The copy contains
* the same objects.
*
* @type Array
* @returns A new array that is a copy of the array
*/
Array.prototype.copy = function() {
  return this.concat();
}

Array#concat glues arrays together and returns the result. If you call it with many arguments the contents of each argument are appended and returned as a new resulting array. If you call it with zero arguments you still get a new resulting array, just no additional items are appended, so it makes a great copy.