UNPKG

multisort

Version:

Sort an array using any number of separate, ranked criteria

338 lines (302 loc) 10.4 kB
var expect = require('expect.js'); var deepEqual = require("deep-equal"); var multisort = require("./multisort"); var assertObjectEquals = function assertObjectEquals(actual, expected){ if (!deepEqual(actual, expected)){ console.log("\n\nActual:"); console.log(JSON.stringify(actual, null, 2)); console.log("\n\nExpected:"); console.log(JSON.stringify(expected, null, 2)); console.log("\n\n"); expect().fail(); return false; } return true; }; describe("Multisort Tests -", function() { var gameShowHosts; // For easy assertions when sorting "game show hosts" var hostIds = function() { return gameShowHosts.map(function(e) {return e.id;}); }; beforeEach(function() { gameShowHosts = [ { id: 1, name: { first: "Bob", last: "Barker" }, retired: true, fame: 2, show: "The Price is Right", say: { name: function() { return "Bob Barker"; }, catchphrase: function(exclaimations) { if (!exclaimations) throw "No arguments passed" // To test that sorting function passes arguments in. var catchphrase = "Come on down"; for (var i = 0; i < exclaimations; i++) { catchphrase = catchphrase + "!"; } return catchphrase; } } }, { id: 2, name: { first: "Regis", last: "Philbin" }, retired: true, fame: 3, show: "Who Wants to be a Millionaire", say: { name: function() { return "Regis Philbin"; }, catchphrase: function(questionMarks) { if (!questionMarks) throw "No arguments passed" var catchphrase = "Is that your final answer"; for (var i = 0; i < questionMarks; i++) { catchphrase = catchphrase + "?"; } return catchphrase; } } }, { id: 3, name: { first: "Alex", last: "Trebek" }, fame: 3, show: "Jeopardy", say: { name: function() { return "Alex Trebek"; }, catchphrase: function(exclaimations) { if (!exclaimations) throw "No arguments passed" var catchphrase = "This is Jeopardy"; for (var i = 0; i < exclaimations; i++) { catchphrase = catchphrase + "!"; } return catchphrase; } } }, { id: 4, name: { first: "Ben", last: "Bailey" }, show: "Cash Cab", fame: 1, retired: true, say: { name: function() { return "Ben Bailey"; }, catchphrase: function(exclaimations) { if (!exclaimations) throw "No arguments passed" var catchphrase = "You're in The Cash Cab"; for (var i = 0; i < exclaimations; i++) { catchphrase = catchphrase + "!"; } return catchphrase; } } }, { id: 5, name: { first: "Pat", last: "Sajak" }, fame: 2, show: "Wheel of Fortune", say: { name: function() { return "Pat Sajak"; }, catchphrase: function(exclaimations) { if (!exclaimations) throw "No arguments passed" var catchphrase = "Give the wheel a spin"; for (var i = 0; i < exclaimations; i++) { catchphrase = catchphrase + "!"; } return catchphrase; } } } ]; }); describe("numerical criterion -", function() { it("sorts ascending if number is non-negative", function() { var input = [3, 4, 2, 5, 1]; multisort(input, 1); assertObjectEquals(input, [1, 2, 3, 4, 5]); }); it("sorts descending if number is negative", function() { var input = [3, 4, 2, 5, 1]; multisort(input, -1); assertObjectEquals(input, [5, 4, 3, 2, 1]); }); }); describe("one function criterion -", function() { it("identity function", function() { var input = [1, 3, 2, 5, 4]; multisort(input, function(a) {return a;}); assertObjectEquals(input, [1, 2, 3, 4, 5]); }); it("mathematical function", function() { var input = [1, 3, 2, 5, 4]; multisort(input, function(a) {return Math.abs(2.1 - a);}); assertObjectEquals(input, [2, 3, 1, 4, 5]); }); it("typecasting function", function() { var input = [null, undefined, 1, "asdf"]; multisort(input, function(a) {return String(a)}); // issues with deepEqual and undefined values... expect(input[0]).to.be(1); expect(input[1]).to.be("asdf"); expect(input[2]).to.be(null); expect(input[3]).to.be(undefined); }); }); describe("one string criterion -", function() { it("empty string gives default sort", function() { var input = ["a", "b", "ab", "ba"]; multisort(input, ''); assertObjectEquals(input, ["a", "ab", "b", "ba"]); }); it("length property", function() { var input = ["hello", [1], [1, 2], []]; multisort(input, 'length'); assertObjectEquals(input, [[], [1], [1, 2], "hello"]); }); it("reversed length property", function() { var input = ["hello", [1], [1, 2], []]; multisort(input, '~length'); assertObjectEquals(input, ["hello", [1, 2], [1], []]); }); it("length property which is undefined on some members", function() { var input = ["hello", [1], [1, 2], [], 1]; multisort(input, '~length'); assertObjectEquals(input, ["hello", [1, 2], [1], [], 1]); }); it("root-level object property", function() { multisort(gameShowHosts, "show"); assertObjectEquals(hostIds(), [4, 3, 1, 5, 2]); }); it("explicit nested object property", function() { multisort(gameShowHosts, "!name.last"); assertObjectEquals(hostIds(), [3, 5, 2, 1, 4]); }); it("implicit nested object property", function() { multisort(gameShowHosts, "name.first.length"); assertObjectEquals(hostIds(), [1, 4, 5, 3, 2]); }); it("existential criterion", function() { var input = [{prop: true}, {prop: undefined}, {prop: false}, {prop: null}]; multisort(input, 'prop?'); assertObjectEquals(input, [{prop: undefined}, {prop: null}, {prop: true}, {prop: false}]) input = [{prop: true}, {prop: undefined}, {prop: false}, {prop: null}]; multisort(input, '!prop?'); assertObjectEquals(input, [{prop: true}, {prop: false}, {prop: undefined}, {prop: null}]) }); it("functional property (without arguments)", function() { multisort(gameShowHosts, "say.name()"); assertObjectEquals(hostIds(), [3, 4, 1, 5, 2]); }); it("functional property (with arguments)", function() { multisort(gameShowHosts, "~say.catchphrase(3)"); assertObjectEquals(hostIds(), [4, 3, 2, 5, 1]); }); it("toString functional property to sort strings alphabetically", function() { var input = [{name: "dog"}, {name: "Dog"}, {name: "DOG"}, {name: "demon"}, {name: "Demon"}, {name: "DEMON"}]; // multisort(input, 'name'); // assertObjectEquals(input, [{name: "DEMON"},{name: "DOG"},{name: "Demon"},{name: "Dog"},{name: "demon"},{name: "dog"}]); multisort(input, 'name.toLowerCase()') for (var i = 0; i < 6; i++) { if (i < 3) { expect(input[i].name.toLowerCase()).to.be("demon"); } else { expect(input[i].name.toLowerCase()).to.be("dog"); } } }); it("root-level functional property", function() { var input = ["dog", "Dog", "DOG", "demon", "Demon", "DEMON"]; multisort(input, 'toLowerCase()') input = input.map(function(elem) {return elem.toLowerCase()}) assertObjectEquals(input, ["demon", "demon", "demon", "dog", "dog", "dog"]); }); it("functional property (existential)", function() { var input = [{func: function() {return true}}, {func: function() {return null}}]; multisort(input, "func()?"); // Hard to compare functions directly. var output = input.map(function(i){return i.func()}); assertObjectEquals(output, [null, true]); // func()? is not the same as func? var input = [{func: function() {return true}}, {func: function() {return null}}]; multisort(input, "func?"); // Hard to compare functions directly. var output = input.map(function(i){return i.func()}); assertObjectEquals(output, [true, null]); }); }); describe("multiple criteria -", function() { it("three mathematical functions", function() { var input = [8, 7, 6, 5, 4, 3, 2, 1]; multisort(input, [ function(a) {return a % 2}, function(a) {return a % 3}, function(a) {return a} ]); assertObjectEquals(input, [6, 4, 2, 8, 3, 1, 7, 5]); }); it("several nested properties", function() { multisort(gameShowHosts, [ "~fame", "retired", "name.last" ]); assertObjectEquals(hostIds(), [3, 2, 5, 1, 4]) }); }); describe("partial application - ", function() { it("three mathematical functions", function() { var sorter = multisort([ function(a) {return a % 2}, function(a) {return a % 3}, function(a) {return a} ]); var input1 = [8, 7, 6, 5, 4, 3, 2, 1]; sorter(input1); assertObjectEquals(input1, [6, 4, 2, 8, 3, 1, 7, 5]); var input2 = [1, 5, 10, 25, 50, 100]; sorter(input2); assertObjectEquals(input2, [10, 100, 50, 1, 25, 5]); }); it("returns a comparator for use with array.sort()", function() { var sorter = multisort([ function(a) {return a % 2}, function(a) {return a % 3}, function(a) {return a} ]); var comparator = sorter.comparator; var input1 = [8, 7, 6, 5, 4, 3, 2, 1]; input1.sort(comparator); assertObjectEquals(input1, [6, 4, 2, 8, 3, 1, 7, 5]); var input2 = [1, 5, 10, 25, 50, 100]; input2.sort(comparator); assertObjectEquals(input2, [10, 100, 50, 1, 25, 5]); }); }); });