Saturday, July 6, 2013: Cool things you can do with it.js

I just released a new small JavaScript library called it.js.

If you’ve ever used any of these: array.map, array.filter, _.sortBy, _.all, or _.any, you’ll know that these “higher-order” functions wants you to to pass in some kind of function. Usually this function just takes an object, looks up a property or call a method, and return the result.

This library, it.js, helps you create that function, so you can write:

var ok = _.every(fields, It.send('validate'))

Instead of

var ok = _.every(fields, function(field) { return field.validate() })

This library is a byproduct of me building another AngularJS app, the new version of Friendlist, friend list manager for Facebook.

This article will show you what you can do with it.js. For more detailed documentation, please see the project page on GitHub!.

it.js generates functions

Generating functions is a main duty of it.js.

Let’s see some analogies. The first line is how you can do it without it.js, and the second line shows how you can use it.js instead of writing these functions by hand.

(function(x) { return x.length })
 => It.get('length')

(function(x) { return x.charCodeAt(0) })
 => It.send('charCodeAt', 0)

(function(x) { return x * 2 })
 => It['*'](2)

(function(x) { return x % 2 === 0 })
 => It['%'](2)['==='](0)

(function(x) { return x.age >= 13 })
 => It.get('age')['>='](13)

(function(a) { return a.map(function(x) { return x.name }) })
 => It.pluck('name')

(function(a) { return a.map(function(x) { return x.toUpperCase() }) })
 => It.splat(It.send('toUpperCase'))

(function(o) { return o && o[0] && o[0].user && o[0].user.screen_name && userLink(o[0].user.screen_name) })
 => It.maybe('0').maybe('user').maybe('screen_name').maybe(userLink)

Here are the things

Here we have an array of “things”.

var things = [
  { name: 'Apple',          type: 'fruit' },
  { name: 'CoffeeScript',   type: 'language' },
  { name: 'Cat',            type: 'animal' },
  { name: 'Dog',            type: 'animal' },
  { name: 'Guava',          type: 'fruit' },
  { name: 'JavaScript',     type: 'language' },
  { name: 'Mountain Lion',  type: 'animal' },
  { name: 'Pineapple',      type: 'fruit' },
  { name: 'Ruby',           type: 'language' } ]

Let’s see what we can do with it…


The first line shows how you can do it without using it.js.
Subsequent lines shows how it.js can help simplify your code.

Get all the names!

things.map(function(thing) { return thing.name })
things.map(It.get('name'))

How about having them lowercased?

things.map(function(thing) { return thing.name.toLowerCase() })
things.map(It.get('name').send('toLowerCase'))

What if we only want the fruits?

things.filter(function(thing) { return thing.type == 'fruit' })
things.filter(It.get('type')['==']('fruit'))

And if we only want the name of these fruits?

things.filter(function(thing) { return thing.type == 'fruit' })
      .map(function(thing) { return thing.name })
things.filter(It.get('type')['==']('fruit')).map(It.get('name'))

Here are the strings

var strings = [
  'Piece', 'of', 'my', 'life',
  'I', 'still', 'to', 'still',
  'Wall', 'is', 'in', 'my', 'heart' ]

What if we want to get a case-insensitive sorted copy? Using Lo-Dash or underscore:

_.sortBy(strings, function(s) { return s.toLowerCase() })
_.sortBy(strings, It.send('toLowerCase'))

Conclusion

As you can see, where we could create a simple function manually, we can use it.js to help.

As a note about performance, it takes a lot of operation to generate a function, so you should not generate these functions inside a loop.

You can, and should, generate these functions somewhere else. They can be used later.

For example, you could write this:

var caseInsensitive = It.send('toLowerCase')

And use them like this:

_.sortBy(strings, caseInsensitive)