Skip to content

Instantly share code, notes, and snippets.

@bttmly
Created August 21, 2014 02:07
Show Gist options
  • Save bttmly/9a3e2f3f1b86a86319f7 to your computer and use it in GitHub Desktop.
Save bttmly/9a3e2f3f1b86a86319f7 to your computer and use it in GitHub Desktop.
Iteration presentation

Why use Array.prototype iterators?

  • Expressive They say something about the purpose and result of the iteration.

  • Contained They don't have side effects if used properly. All relevant stuff occurs within the callback.

  • Scoped They provide automatic function scope for each iteration which eliminates a common type of error.

  • Modular Use of callbacks encourages you to write reusable functions to pass into iterators.

Expressive 1

  • Two kinds of iterators: full-length and breakable.

  • Full length: .map() .filter() .reduce() + .reduceRight() .forEach()

  • Breakable .every() .some()

Expressive 2

  • Each iterator has a specific return

  • Full length: .map() => array of same length as original with new values .filter() => array of subset of original array values .reduce() => result of arbitrary reduction (can be pretty much anything) .forEach() => returns nothing

  • Breakable .every() => returns true if EVERY item returns truthy from callback; exits & returns false on false .some() => exits returns true if ANY item returns truthy from callback; else returns false

// Expressive 3
function timesTwo (num) {
return num * 2;
}
var nums = [ 1, 2, 3, 4, 5 ];
// imperative mapping
var results = [];
for ( var i = 0; i < nums.length; i++ ) {
results.push( timesTwo( nums[i] ) );
}
// functional mapping
var results = nums.map( timesTwo );
// results: [ 2, 4, 6, 8, 10 ]
// Expressive 4
function isOdd ( num ) {
return num % 2 === 1;
}
var nums = [ 1, 2, 3, 4, 5 ];
// imperative filtering
var results = [];
for ( var i = 0; i < nums.length; i++ ) {
if ( isOdd( nums[i] ) ) {
results.push( isOdd( nums[i] ) );
}
}
// functional filtering
var results = nums.filter( isOdd );
// results: [ 1, 3, 5 ]
// Scope
// We're going to make a class that sort of extends array.
// here's the constructor
function SuperArray ( arr ) {
this._arr = arr.slice();
}
// Now we need to add some methods onto its prototype.
// We _could_ add them one by one, but this way we can easily add more later.
var methods = ['push', 'pop', 'shift', 'unshift'];
// This won't work as expected.
for ( var i = 0; i < methods.length; i++ ) {
SuperArray.prototype[ methods[i] ] = function () {
return Array.prototype[ methods[i] ].apply( this._arr, arguments );
};
}
// this will work
methods.forEach( function ( method ) {
SuperArray.prototype[method] = function () {
return Array.prototype[method].apply( this._arr, arguments );
};
});
// Imperative solution
for ( var i = 0; i < methods.length; i++ ) {
( function ( method ) {
SuperArray.prototype[method] = function () {
return Array.prototype[methods].apply( this._arr, arguments );
};
})( methods[i] );
}
// Using .map()
// mapping URLs into an array of promises, and aggregating them
var reqUrls = ['/users/1/info', 'users/1/pic', 'users/1/likes'];
var promises = reqUrls.map( $.ajax )
$.when.apply( $, promises ).then( function () {
// code here executes after all AJAX calls have resolved.
// arguments[0] holds result of first call, etc.
});
// mapping the results of calling a method on each item
function mapInvoke ( arr, methodName ) {
var args = [].slice.call( arguments, 2 );
return arr.map( function ( item ) {
return item[methodName].apply( item, args );
});
}
// var arr = [[1, 2], [3, 4], [5, 6]]
// mapInvoke( arr, "pop" )
// => [2, 4, 6]
// Using .reduce()
// "mapping" an object
//
function mapObject ( obj, callback ) {
return Object.keys( obj ).reduce( function ( acc, key ) {
acc[key] = callback( obj[key], key );
return acc;
}, {} );
}
// Vanilla DOM manipulation
//
var slice = Function.call.bind( [].slice );
function children ( domNodes ) {
return slice( domNodes ).reduce( function ( acc, node ) {
return acc.concat( slice( node.children ) );
}, [] );
}
// Combining methods for complex transformations
function collectionFromTable ( headers, rows ) {
return rows.map( function ( row ) {
return row.reduce( function ( model, val, i ) {
model[ headers[i] ] = val;
return model;
}, {} );
});
}
// Exploit existing methods with composition for extra expressiveness.
function matches ( a, b ) {
return Object.keys( a ).every( function ( key ) {
return a[key] === b[key];
});
}
function where ( arr, obj ) {
return arr.filter( matches.bind( null, obj ) );
}
// Use .some() or .every() for end-early iteration
function find ( arr, testFn ) {
var result = null
arr.some( function ( item, i, arr ) {
if ( testFn( item, i, arr ) ) return result = item;
})
return result;
}
function findWhere ( arr, obj ) {
return find( arr, matches.bind( null, obj ) );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment