Thrown for a for loop
Over the course of learning any programming language you will inevitably run into loops. They are a foundational part of all computing and for the most part seem pretty straight forward. Today I thought we would take a look at a situation where things aren’t as straight forward and figuring out the loops might just drive you loopy (Ill be here all week :/).
Here are the problems:
Write a function called map2 that accepts two arrays and a function as arguments, and constructs a new array by invoking its function argument on the elements of both arrays, e.g.:
map2([1, 2, 3], [4, 5, 6], function(a, b) {
return a * b;
});
// => [4, 10, 18]
This first problem is fairly easy and straight forward but lets break it down so we know exactly where we are at.
function map2(array1, array2, fn) {
//first lets create the results array we are going to return
var results = [];
//create the normal for loop
for(var i=0; i<array1.length; i++) {
//inside the loop is where the magic happens
//notice we are pushing the results of calling fn on each item in the arrays simultaneously
results.push(fn(array1[i], array2[i]));
}
return results;
}
Ok pretty easy. Now for the hard stuff…
Now, write a function called mapN that accepts an arbitrary number of arrays and a n-ary function as arguments, and constructs a new array by combining the elements of all the arrays, e.g.:
mapN([1, 2, 3], [4, 5, 6], [2, 2, 2], function(a, b, c) {
return (a * b) + c;
});
// => [6, 12, 20]
mapN([1, 2, 3], function(x) { return x * x; }) // => [1, 4, 9]
note: You’ll need to read about the arguments keyword and apply to complete this function.
Ok so lets again break this down…
function mapN () {
//This nifty piece of code uses the powers of call to manipulate the Array object into letting us slice the arguments array.
//Note that the arguments array isn't actually an array but is only array-like, meaning it doesn't come with the convenient
//methods Arrays do. Using call we can tell the method exactly what to work on, in this case the arguments 'array'.
//We slice off all of the arguments except the last one knowing that it will be our function. This way we have now separated the
//arrays from the function.
var arrays = Array.prototype.slice.call(arguments, 0, arguments.length - 1);
//now lets create a simple variable to hold our function, again pulling it from the array-like arguments list.
var fn = arguments[arguments.length - 1];
var results = [];
//Ok this is where things get really sticky so stay with me...
//The first part of our loop we want to loop over the **first index** of each array so we make sure we call it with
//the length of the first array within our arrays variable. (?very meta?)
for(var i=0; i<arrays[0].length; i++) {
//Now we need a place to temporarily store the reorganizing of our arrays
var tempArr = [];
//Now for the second loop we will make sure it is looping over each array, staying at whatever index the initial loop has us on
for(var j=0; j<arrays.length; j++) {
//Here we push each index of each array into our temp array essentially rearranging the pieces to the way we want them
tempArr.push(arrays[j][i]);
}
//Here is where the magic happens. We take and push the results of calling (in this case we use the apply method because we are using arrays)
//our function on the temp array into the results array.
results.push(fn.apply(null, tempArr));
}
return results;
}
Here is a simple visualization of what is happening:
// INPUT
[[1, 2, 3, 4],
[4, 5, 6, 7],
[7, 8, 9, 10]]
// INTERMEDIATE --> this where we do the rearranging, taking and matching each index from each array and putting them together.
[[1, 4, 7],
[2, 5, 8],
[3, 6, 9],
[4, 7, 10]]
// RESULT
[12, 15, 18, 21]