Skip to content

Instantly share code, notes, and snippets.

@olsonpm
Last active August 29, 2015 14:13
Show Gist options
  • Save olsonpm/3dad4232784d057d66c5 to your computer and use it in GitHub Desktop.
Save olsonpm/3dad4232784d057d66c5 to your computer and use it in GitHub Desktop.
Sequence.prototype.equals extension to Lazy.js

file -> lazy-extensions.js

'use strict';

var Lazy = require('lazy.js').strict();
var Sequence = Lazy.Sequence;
var xor = require('component-xor');

Sequence.prototype.equals = function equals(other_, eqFn_) {
    if (!(other_ instanceof Sequence)) {
        throw new Error("Invalid Argument: <Sequence>.equals requires a Sequence argument");
    } else if (this === other_) {
        return true;
    } else if ((typeof this.length === 'number') && (typeof other_.length === 'number') && this.length !== other_.length) {
        // if both sequences have a length property, we might as well check that before iterating through each element.
        return false;
    }

    var normalizedEqFn = normalizeEqualityFunction(eqFn_, "Invalid Argument: <Sequence>.equals requires the second argument to be undefined, a string, or a function");

    var thisIterator = this.getIterator();
    var otherIterator = other_.getIterator();
    var stillEqual = true;
    var shouldIterate
        , unequalLength;

    function iterate(it1_, it2_) {
        var it1Moved = it1_.moveNext();
        var it2Moved = it2_.moveNext();
        return {
            shouldIterate: (it1Moved && it2Moved)
            , unequalLength: (xor(it1Moved, it2Moved))
        };
    }

    var itRes = iterate(thisIterator, otherIterator);
    shouldIterate = itRes.shouldIterate;
    unequalLength = itRes.unequalLength;
    while (stillEqual && shouldIterate && !unequalLength) {
        stillEqual = normalizedEqFn(thisIterator.current(), otherIterator.current());

        itRes = iterate(thisIterator, otherIterator);
        shouldIterate = itRes.shouldIterate;
        unequalLength = itRes.unequalLength;
    }

    return stillEqual && !unequalLength;
};

// helper fxn
function normalizeEqualityFunction(eqFn_, err_) {
    var normalizedEqFn;

    // Logic for below is as follows:
    //   - If eqFn is undefined, then test for strict equality
    //   - If it is a string, then we assume the iterated element has an equality property named whatever eqFn is
    //   - If it's a function, then we assume it takes in two arguments and returns a boolean for equality.
    //   - normalizedEqFn turns each of the above into a normalized equality function(left, right).
    if (typeof eqFn_ === 'undefined') {
        normalizedEqFn = function(left, right) {
            return left === right;
        };
    } else if (typeof eqFn_ === 'string') {
        normalizedEqFn = function(left, right) {
            return left[eqFn_](right);
        };
    } else if (typeof eqFn_ === 'function') {
        normalizedEqFn = eqFn_;
    } else {
        var msg = (typeof err_ === 'string')
            ? err_
            : "Invalid Argument: normalizedEqualityFunction requires an undefined, string, or function argument";

        throw new Error(msg);
    }

    return normalizedEqFn;
}

Mocha tests

file -> test.js

'use strict';

var Lazy = require('./lazy-extensions');

suite("lazy-extensions.js", function() {
    var vals
        , valsConst
        , objs
        , objsConst;

    function TestObj(name, val) {
        this.name = name;
        this.val = val;
    }
    TestObj.prototype.equals = function equals(other) {
        return this.name === other.name
            && this.val === other.val;
    };
    TestObj.equals = function equals(left, right) {
        return left.equals(right);
    };

    function getNewObjs() {
        return [new TestObj('name1', 'val1')
            , new TestObj('name2', 'val2')
            , new TestObj('name3', 'val3')
            , new TestObj('name4', 'val4')
            , new TestObj('name5', 'val5')
        ];
    }

    function getNewVals() {
        return [1, 2, 3, 4, 5];
    }

    setup(function() {
        vals = Lazy(getNewVals());
        valsConst = Lazy(getNewVals());

        objs = Lazy(getNewObjs());
        objsConst = Lazy(getNewObjs());
    });

    test("Sequence.equals", function Sequence_equals() {
        // using strictEqual because isTrue wasn't giving a line number in its error message.
        assert.strictEqual(vals.equals(valsConst), true);
        var vals2 = Lazy(getNewVals()).concat([6]);
        assert.strictEqual(vals.equals(vals2), false);
        assert.strictEqual(objs.equals(objsConst), false);
        assert.strictEqual(objs.equals(objsConst, 'equals'), true);
        assert.strictEqual(objs.equals(objsConst, TestObj.equals), true);
    });
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment