FCC Bonfire Series 133: Finders Keepers

Time for a new bonfire it is, this time, we’ll fight our way through Finders Keepers. This bonfire will have us create a function that takes two arguments; The first will be an array, and the second a will be truth test (a function that returns true/false). Our function must return the first item in the array that passes the test.

Free Code Camp hints at the using of the Array.prototype.some() method, and that’s where we are going to be starting, but we will also create a less functional version that, will, once again, be quite faster.

The Array.prototype.some() method will iterate over an array and execute a callback (function) over each item until one of them returns true. Once an item has passed the test, some() will return true; if no item passed, then, it is false. But… Since some() will only return a boolean value (true or false), we need a way to store the item that passed said test, so instead of doing something like this:

var myArray = [1, 2, 3, 4, 5, 6];
var testFunction = function(item) { return item > 5; };
myArray.some(testFunction); // -> true: number 6 passed the test, but we have no way of storing or returning the result.

We’ll do this:

var result; // -> We will store the result here.
var myArray = [1, 3, 5, 6, 8];
var testFunction = function(item) { return item % 2 === 0; };

myArray.some(function(currentItem) {
    if (testFunction(currentItem)) {
        result = currentItem;
        return true;
    }
});

console.log(result); // -> First item that passed: 6

Now that we have done most of the work, simply wrap it in a function!

function find(array, testFunction) {
    var result;

    array.some(function(currentItem) {
        if (testFunction(currentItem)) {
            result = currentItem;
            return true;
        }
    });

    return result;
}

For not-overly-huge arrays, or arrays with a passing item relatively close to the first position, this function should be good to go. In my machine, for an array of one million items where the matching result was at the last position, it took 400ms, not too bad. But we can do better, and many times, better means going more basic. Let’s try to do the same thing using a single for loop, and returning a value as soon as it passes the test.

function find(array, testFunction) {
    for (var i = 0; i < array.length; i++) {
        if (testFunction(array[i])) {
            return array[i];
        }
    }
}

Simple and efficient, let’s see how both compare:

// -- Testing with 100 items (result in last position) -- //
Version 1: 0ms | Result: 100
Version 2: 0ms | Result: 100

So far, so good, let’s up it to 10000:

// -- Testing with 10000 items (result in last position) -- //
Version 1: 1ms | Result: 10000
Version 2: 0ms | Result: 10000

Still OK! How about a million?

// -- Testing with 1000000 items (result in last position) -- //
Version 1: 39ms | Result: 1000000
Version 2: 4ms  | Result: 1000000

Starting to see the difference? Last test then, a hundred millions!

// -- Testing with 100000000 items (result in last position) -- //
Version 1: 3945ms | Result: 100000000
Version 2: 186ms  | Result: 100000000

Now we can start seeing the real difference. Still, both solutions will work for the tests proposed by FCC, so there is no need to go that deep, though it is nice to know! Try it out and see if you can come up with another version to it, I’m sure you will!