Bitovi take-home exercise

by ryanwheale

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.9.3/lodash.min.js"></script>
<h1>Bitovi take home exercise</h1>
<p>Using <q>whatever resources you want</q> I have created several different techniques for making an array unique. The scope of this exercise only required the handling of objects, which was my first prerogative. However, creating a utility method like this should be able to handle any type of data <abbr title="in my opinion">IMO</abbr>.</p>
<p>In day-to-day work I would lean on existing utilities like lodash to handle things like this - so that was my first attempt &lt;/too_easy&gt;. The next two attempts reflect what I might write on some Tuesday without a utility library. Items marked in <em class="inaccurate">red</em> highlight techniques which do not result in an array of truly unique values (due to <code>NaN</code>). The last attempt is a bit overboard but the result of what happens when I get into a battle with a stopwatch.</p>
<p>After it was all said and done, I wanted to know how the lodash guys got it so fast - and they are leveraging ES6 <code>Set</code> - which brings me back to my intial inclination to use a utility library for these types of things. Did I learn a lot in this exercise: <strong>yes</strong>. I even <a href="http://jsfiddle.net/ryanwheale/ohe9gzxd/3/" target="_top">tried my hand at the <code>Set</code> technique</a> with some rather pleasing results</p>

CSS

h2 {
    background-color: #345;
    color: #fed;
    padding: .3em;
}

code {
    background-color: #eee;
    padding: 0 .3em;
}

table {
    width: 100%;
}

.inaccurate {
    color: red;
}

JavaScript

var count = 15000,
    generatePrimitives = false,
    primitiveValues = [null, undefined, NaN],
    val, i, tests;

// add some ints & strings
for (i = 48; i < 70; i++) {
    val = String.fromCharCode(i);
    primitiveValues.push(i < 58 ? parseInt(val, 10) : val);
}

function _random (max) {
    return Math.floor(Math.random() * max);
}

function generateData () {
    var arr = [],
        i = count,
        primVals = primitiveValues.slice(),
        n, item;
    
    while (i--) {
        if (generatePrimitives && primVals.length) {
            item = primVals.pop();
        } else {
            item = {};
            for (n = 0; n < 5; n++) {
                item["prop_" + n] = Math.random();
            }
        }
        
        arr.push(item);
    }
    
    while (i++ < count/2) {
        if (generatePrimitives && Math.random() < 0.5) {
            item = primitiveValues[_random(primitiveValues.length)];
        } else {
            item = arr[i];
        }
        
        arr.splice(_random(count), 0, item);
    }
    
    return arr;
}

tests = [{
    title: "Lodash",
    fn: _.uniq
}, {
    title: "Array.prototype.indexOf",
    fn: function (arr) {
        var uniqs = [], i = -1, l = arr.length, item;
        while (++i < l) {
            item = arr[i];
            if (uniqs.indexOf(item) === -1) {
                uniqs.push(item);
            }
        }
        return uniqs;
    }
}, {
    title: "Nested Loop",
    maxOut: 15000,
    fn: function (arr) {
        var uniqs = [], 
            count = 0, i = -1, l = arr.length, item, n, nl;
        
        while(++i < l) {
            item = arr[i];
            for (n = 0, nl = count; n < nl; n++) {
                if (uniqs[n] === item) {
                    break;
                }
            }
            
            if (n === count) {
                uniqs.push(item);
                count++;
            }
        }
        return uniqs;
    }
}, {
    title: "Custom technique",
    fn:...