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.

Array#partition – Useful JavaScript Game Extensions #20

Part 20 of 256

There comes a time in every programmer’s life when he must partition an array in twain. Separating apples from oranges, the wheat from the chaff, the yeas from the nays.

/**
 * Partitions the elements into two groups: those for which the iterator returns
 * true, and those for which it returns false.
 * @param {Function} iterator
 * @param {Object} [context] Optional context parameter to be
 * used as `this` when calling the iterator function.
 *
 * @type Array
 * @returns An array in the form of [trueCollection, falseCollection]
 */
Array.prototype.partition = function(iterator, context) {
  var trueCollection = [];
  var falseCollection = [];
 
  this.each(function(element) {
    if(iterator.call(context, element)) {
      trueCollection.push(element);
    } else {
      falseCollection.push(element);
    }
  });
 
  return [trueCollection, falseCollection];
};

Let’s see it in action:

// Separating apples from oranges (non-apples)
var result = ["apple", "apple", "orange", "apple", "orange", "orange", "orange", "apple"].partition(function(item) {
  return item == "apple";
});
 
// result is [["apple", "apple", "apple", "apple"], ["orange", "orange", "orange", "orange"]]

The result of using Array#partition is an array containing to elements, the first is the list of items that the iterator function evaluated to true for, and the second is the list that the iterator function evaluated to false for. You’d be surprised (probably not) how often dividing lists into two categories based on arbitrary criteria comes into play. Additionally this can be used as the foundation for simpler generic methods like select and reject. I’m sure they’ll turn up pretty soon.

Object#reverseMerge – Useful JavaScript Game Extensions #19

Object#reverseMerge a method that complements Object#merge nicely. In regular merge all the properties of the source are copied onto the destination, but in reverseMerge the properties are only copied over if the destination doesn’t already have the property.

Object.prototype.reverseMerge = function(source) {
  for(var key in source) {
    if(source.hasOwnProperty(key) && !(key in this)) {
      this[key] = source[key];
    }
  }
 
  return this;
};

This allows us to easily have a list of default properties in an object and add them to the destination object only if they aren’t already present.

var iceCreamDefaults = {
  flavor: "vanilla",
  price: 2,
  size: 4
};
 
 
var chocolate = {
  flavor: "chocolate"
}.reverseMerge(iceCreamDefaults);
 
chocolate.price; // 2
chocolate.size; // 4

In the example we have an object containing ice cream default values. These values are merged into the chocolate object we create, but only if they don’t already exist within that object.

Extending Object.prototype is fun and rewarding, but it’s not without it’s pitfalls. For example jQuery is not yet 100% compatible with code that extends Object.prototype, though it has plans to be in the future. In addition, the presence of prototype properties in all your objects is one extra edge case to consider when using for in iteration and the in operator. The primary question is whether the benefit added by Object.prototype extensions exceeds the risk of the extra complexity, and in the end it comes down to what the biggest pain points are in the code in your own specific circumstances. Like most programming practices that involve trade-offs their correct usage depends strongly on the context where they are used.

Be sure to subscribe to get all the latest updates!

Bonus section, an alternate implementation using Object#each:

Object.prototype.reverseMerge = function(source) {
  source.each(function(key, value) {
    if(!(key in this)) {
      this[key] = value;
    }
  }, this); // context argument to ensure that `this` references correctly
 
  return this;
};

Again, be sure to subscribe so you don’t miss any of this amazing series!

Object#each: Useful JavaScript Game Extension #18

Part 18 of 256

Previously we discussed extending Object.prototype and adding a merge method. One of the big issues with messing with Object.prototype was that there was no standard and easy way to iterate over the non-prototype properties of an object. Remember that each method that I said was from the future? Today is that future.

/**
* Iterate over each property of this object, excluding prototype properties.
* It does this by excluding properties that aren't its own with `hasOwnProperty`
*
* @param {Function} iterator Receives two arguments for each property, the key and the value.
* @param {Object} [context] Optionally specifies what context the iterator will be called with.
*/
Object.prototype.each = function(iterator, context) {
  for(var key in this) {
    if(this.hasOwnProperty(key)) {
      iterator.call(context, key, this[key]);
    }
  }
 
  return this;
}
var candyPrices = {
  taffy: 0.95,
  rock: 1.25,
  lollipop: 0.50,
  licorice: 0.25
};
 
candyPrices.each(function(name, price) {
  alert("The price of " + name + " is $" + price);
});

This will allow us to iterate without having to write our own brittle for in iterators, and will be much more robust from breakage. If you are using for in you’ll need to decide whether or not prototype methods should be included. Here the decision already made, hopefully in a sane and generally robust manner.

Don’t worry about Array#each, it’s own prototype method will override this one so that array iteration is unaffected by this.

There are a few more useful Object.prototype extensions coming up, so stay tuned for the rest of the series.

Object#merge – Useful JavaScript Game Extensions 17

Yesterday I wrote about Object.extend, a useful method for building objects out of smaller pieces. Commenter Kieran posted a reply saying that it might be better as an Object#merge method. My initial reaction was an aversion to extending Object.prototype, so I went on a journey to figure out why I believed that.

Back in 2003, prototype.js extended Object.prototype with extra methods. There was a big outcry, “You’re breaking my [badly written] code with your library!!1”. Apparently it was common practice to use objects as hash tables and use for in iteration on them without using a check like hasOwnProperty. And jQuery didn’t exist then, and unit testing for JavaScript didn’t exist, or was never practiced, and there where thousands of badly written libraries that you could copy-paste into your app and watch it break because prototype.js extended Object.prototype.

So naturally there was a massive outcry Object.prototype is verboten, and continuing through to December 2009, Prototyping Object in Javascript breaks jQuery?. Though in the latter one the breakage was acknowledged as a bug in jQuery, the general consensus was that it was “bad practice” to extend Object.prototype, citing again for in loops primarily.

So the cautious thing to do is to never extend Object.prototype. This is most likely correct if you are distributing a library to millions of people who need to use it in diverse and hostile environments, aka the web. Are you delivering a library to millions of people who use it in diverse and hostile environments? No? Cool, continue reading then.

For my own code, that I run on my own site, I have quite a lot of control over it. I can choose to use external libraries or not, and patch them if they are using for in loops in some weird way. So I’m going to give it a shot and try extending Object.prototype.

Object.prototype.merge = function(source) {
  for(var key in source) {
    if(source.hasOwnProperty(key)) {
      this[key] = source[key];
    }
  }
 
  return this;
}
var BLT = {
  bacon: true,
  lettuce: true,
  tomato: true
};
 
var Reuben = {
  cornedBeef: true,
  russianDressing: true,
  saurkraut: true,
  swissCheese: true
};
 
var sandwich = {};
 
sandwich.merge(BLT);
sandwich.merge(Reuben);
sandwich.merge({
  name: "custom",
  price: 6
});
 
// each...? Never heard of it. (Don't worry, it's from the future).
sandwich.each(function(key, value) {
  console.log(key + " is " + value);
});
 
// bacon is true
// lettuce is true
// tomato is true
// cornedBeef is true
// russianDressing is true
// sauerkraut is true
// swissCheese is true
// name is custom
// price is 6

That doesn’t seem so bad… though if my past experience with JavaScript has taught me anything it’s that everything is likely to come back and bite me in the ass.

Extending Object.prototype provides a not-insignificant gain of clarity in the code. It also provides a potentially dangerous pitfall that can lead to trouble if used in some situations. I always think it’s a good practice to exchange real problems for hypothetical ones, so this is a trade-off I’m likely to make. I haven’t yet extended Object.prototype in a major JavaScript project yet, I guess I’m still a little gun-shy from having been burned so often by JS in the past. That said, I will definitely be trying it out and seeing for myself if the dangers are real or imagined.

JavaScript Game Extensions: Object.extend

Part 16

Object.extend is an incredibly useful method in JavaScript. It can be used to dynamically augment object capabilities, create composite objects, and to provide a mechanism of inheritance.

Here is the method in it’s simplest form (from prototype.js):

Object.extend = function(destination, source) {
  for (var property in source) {
    destination[property] = source[property];
  }
 
  return destination;
}

The method takes two arguments, a destination and a source. It then copies over each property from the source to the destination and returns the destination. This allows you to do things like:

var BLT = {
  bacon: true,
  lettuce: true,
  tomato: true
};
 
var Reuben = {
  cornedBeef: true,
  russianDressing: true,
  saurkraut: true,
  swissCheese: true
};
 
var sandwich = {};
 
Object.extend(sandwich, BLT);
Object.extend(sandwich, Reuben);
Object.extend(sandwich, {
  name: "custom",
  price: 6
});

Ok, so that’s pretty cool if we’re talking about sandwiches, but how often does that happen? Well if you imagine the sandwiches are classes and the toppings are methods then you’ve got a pretty modular system for constructing objects.

The extend method can be modified to add more smarts and power to it, but at it’s heart it is simply copying properties from one object onto another.

Hmm, all that talk about sandwiches has made me hungry.

Stay tuned for the rest of the series as well as my upcoming book {SUPER: SYSTEM} where I will go into much more detail.

Useful JavaScript Game Extensions: Number#floor and Number#ceil

Parts 14 and 15 of my 256 part series on useful JavaScript game extensions.

JavaScript is missing a lot of features that help a language be object oriented. I don’t see why since they are so easy to add.

/**
 * @returns The number truncated to the nearest integer of less than or equal value.
 * 
 * (4.9).floor(); // => 4
 * (4.2).floor(); // => 4
 * (-1.2).floor(); // => -2
 */
Number.prototype.floor = function() {
  return Math.floor(this);
};
/**
 * @returns The number truncated to the nearest integer of greater than or equal value.
 * 
 * (4.9).ceil(); // => 5
 * (4.2).ceir(); // => 5
 * (-1.2).ceil(); // => -1
 */
Number.prototype.ceil = function() {
  return Math.ceil(this);
};