UNPKG

lively.lang

Version:

JavaScript utils providing useful abstractions for working with collections, functions, objects.

440 lines (371 loc) 13.5 kB
/*global beforeEach, afterEach, describe, it, setInterval, clearInterval, setTimeout*/ import { expect } from "mocha-es6"; import { range, mapAsync, max, drop, take, mask, partition, groupBy, rotate, swap, forEach, flatten, flatmap, reMatches, isSorted, pluck, zip, withN, uniq, uniqBy, without, batchify, sum, min, dropWhile, takeWhile, delimWith, permutations, histogram, combinations, mapAsyncSeries, mutableCompact, combinationsPick } from "../array.js"; import { curry } from "../function.js"; import { random } from "../number.js"; import { equals } from "../object.js"; describe('arr', function() { it('forEach', function() { var result = ''; forEach([4,5,6], function(ea, i) { result += '[' + ea + ',' + i + ']'; }) expect(result).to.equal('[4,0][5,1][6,2]') }); it("range", function() { expect(range(1,1)).to.eql([1]); expect(range(1,1, 10)).to.eql([1]); expect(range(1,5)).to.eql([1,2,3,4,5]); expect(range(1,10, 2)).to.eql([1,3,5,7,9]); expect(range(10, 1, -2)).to.eql([10,8,6,4,2]); expect(range(10, 1, 20)).to.eql([10]); }); it('without', function() { var array = ["a"]; expect([]).to.eql(without(array, "a")); expect(["a"]).to.eql(without(array, "c")); delete array[0]; expect([]).to.eql(without(array, "a")); }); it('mutableCompact', function() { var a = ["a", "b", "c", undefined]; delete a[1]; mutableCompact(a); expect(["a", "c", undefined]).to.eql(a); }); it('min', function() { var a = [{x:2,y:12},{x:5,y:6},{x:9,y:4}]; expect(2).to.eql(min(pluck(a, 'x'))); expect(4).to.eql(min(pluck(a, 'y'))); expect({x:2,y:12}).to.eql(min(a, function(ea) { return ea.x })); expect({x:9,y:4}).to.eql(min(a, function(ea) { return ea.y })); expect(2).to.eql(min([5,3,2,6,4,3,2])); expect(-10).to.eql(min([-3,-3,-5,-10])); expect(-10).to.eql(min([-3,-3,-5,-10])); expect(-5).to.eql(min([-3,null,-5,null])); expect(0).to.eql(min([0, 10])); expect({x: 'foo'}).to.eql(min([{x: 'bar'},{x: 'foo'}, {x: 'baz'}], function(ea) { return ea.x.charCodeAt(2); })); }); it('max', function() { var a = [{x:2,y:12},{x:5,y:6},{x:9,y:4}]; expect(9).to.equal(max(pluck(a, 'x'))); expect(12).to.equal(max(pluck(a, 'y'))); expect({x:9,y:4}).to.eql(max(a, function(ea) { return ea.x })); expect({x:2,y:12}).to.eql(max(a, function(ea) { return ea.y })); expect(6).to.equal(max([5,3,2,6,4,-3,2])); expect(-1).to.equal(max([-3,-2,-1,-10])); expect(-2).to.equal(max([-3,-2,null,-10])); expect(0).to.equal(max([0, -10])); expect({x: 'baz'}).to.eql( max([{x: 'bar'},{x: 'foo'}, {x: 'baz'}], function(ea) { return ea.x.charCodeAt(2); })); }); it('swap', function() { var a = ['a', 'b', 'c', 'd', 'e']; swap(a, 1,4); expect(a).to.eql(['a', 'e', 'c', 'd', 'b']); swap(a, 0, -1) expect(a).to.eql(['b', 'e', 'c', 'd', 'a']); }); it('rotate', function() { var a = ['a', 'b', 'c', 'd', 'e']; a = rotate(a); expect(a).to.eql(['b', 'c', 'd', 'e', 'a']); a = rotate(a, 2); expect(a).to.eql(['d', 'e', 'a', 'b', 'c']); }); it('partition', function() { expect(partition(range(0,10), function(n) { return n % 2 === 0 })) .to.eql([[0,2,4,6,8,10], [1,3,5,7,9]]); }); it('groupBy', function() { var elts = [{a: 'foo', b: 1}, {a: 'bar', b: 2}, {a: 'foo', b: 3}, {a: 'baz', b: 4}, {a: 'foo', b: 5}, {a: 'bar',b:6}], group = groupBy(elts, ea => ea.a), expected = { foo: [elts[0],elts[2],elts[4]], bar: [elts[1],elts[5]], baz: [elts[3]] }; expect(group).to.containSubset(group); expect([[elts[0],elts[2],elts[4]],[elts[1],elts[5]],[elts[3]]]) .to.eql(group.toArray(), 'toArray'); expect(['foo', 'bar', 'baz']).to.eql(group.keys(), 'groupNames'); expect({foo: 3, bar: 2, baz: 1}).to.eql(group.count(), 'coount'); var mapGroupsResult = group.mapGroups((groupName, group) => sum(pluck(group, 'b'))); expect(mapGroupsResult).to.containSubset({foo: 9, bar: 8, baz: 4}, 'mapGroupsResult'); var mapGroupResult = group.map(function(groupName, groupEl) { return groupEl.b; }); expect(mapGroupResult).to.containSubset({foo: [1,3,5], bar: [2,6], baz: [4]}, 'mapGroupResult'); }); it('uniq', function() { expect(uniq([6,3,2,6,4,2])).equals([6,3,2,4]); expect(uniq([])).equals([]); }); it('uniqBy', function() { var a = [{x:33}, {x: 1}, {x: 2}, {x: 3}, {x: 99}, {x: 1}, {x: 2}, {x:1}, {x: 1}], result = pluck(uniqBy(a, function(a,b) { return a.x === b.x; }), 'x'), expected = [33, 1,2,3,99]; expect(expected).to.eql(result); expect(uniqBy([ "edit...", "local", "local", "http://localhost:9011/eval", "http://localhost:9011/eval", {value: {name: "l2l 28E1E - undefined"}}, {value: {name: "l2l 28E1E - undefined"}} ], (a, b) => { let valA = a.value || a; let valB = b.value || b; return valA == valB || equals(valA, valB) })).deep.equals([ "edit...", "local", "http://localhost:9011/eval", {value: {name: "l2l 28E1E - undefined"}} ]) }); it('mask', function() { var a = range(1,4), bools = [false, true, false, true]; expect([2,4]).to.eql(mask(a, bools), 'mask'); }); it('reMatches', function() { var a = ['foo', 'bar', 'zork'], result = reMatches(a, /.r.?/i); expect(result).containSubset([ null, {'0': 'ar', index: 1, input: 'bar'}, {'0': 'ork', index: 1, input: 'zork'}]); }); describe("batchify", function() { it('splits array ccording to constraint', function() { function batchConstrained(batch) { return batch.length == 1 || sum(batch) < batchMaxSize; } var batchMaxSize = Math.pow(2, 28)/*256MB*/, sizes = [ Math.pow(2, 15), // 32KB Math.pow(2, 29), // 512MB Math.pow(2, 29), // 512MB Math.pow(2, 27), // 128MB Math.pow(2, 26), // 64MB Math.pow(2, 26), // 64MB Math.pow(2, 24), // 16MB Math.pow(2, 26)],// 64MB batches = batchify(sizes, batchConstrained); expect(flatten(batches)).to.have.length(sizes.length, 'not all batches included?'); // the sum of each batch should be < 256MB or batch shoudl just have single item expect(batches.every(batchConstrained)).to.equal(true); }); it('needs to consume', function() { function batchConstrained(batch) { return sum(batch) < batchMaxSize; } var batchMaxSize = 3, sizes = [1,4,2,3]; expect(() => batchify(sizes, batchConstrained)) .throws(/does not ensure consumption of at least one/); }); }); describe("permutations", function() { it("creates them", function() { expect(permutations([3,1,2])).to.eql( [[3, 1, 2], [3, 2, 1], [1, 3, 2], [1, 2, 3], [2, 3, 1], [2, 1, 3]]) }) }); it("delimWith", function() { expect(delimWith(["test", "abc", 444], "aha")).to.eql(["test","aha","abc","aha",444]); }); describe("flatten", function() { it("un-nest arrays", function() { expect(flatten([1, [2], [3, [4, [[[5]]]]]])).to.eql([1,2,3,4,5]); }); it("un-nest arrays to a certain depth", function() { expect(flatten([1, [2], [3, [4, [[[5]]]]]], 2)).to.eql([1,2,3,4,[[[5]]]]); }); }); describe("flatmap", function() { it("flatmaps", function() { var result = flatmap([1,2,3], function(ea, i) { return withN(i+1, ea) }); expect(result).to.eql([1,2,2,3,3,3]); }); it("flatmaps big time", function() { // old flatmap version threw stack overlflow errors var result = flatmap(range(1,800000), function(ea, i) { return [ea, i] }); expect(result.length).to.eql(800000*2); }); }); describe("sorting", function() { it("isSorted", function() { expect(isSorted([2,4,7,9])).to.equal(true); expect(isSorted([2,4,3,9])).to.equal(false); }); it("isSorted descending", function() { expect(isSorted([4,2,1], true)).to.equal(true); expect(isSorted([4,5,1])).to.equal(false); }); }); describe("combinations", function() { it("combines all elements of lists", function() { expect(combinations([['a', 'b', 'c'], [1, 2]])) .to.eql([["a", 1], ["a", 2], ["b", 1], ["b", 2], ["c", 1], ["c", 2]]); }); it("retrieves a specific combination", function() { var searchSpace = [["a", "b", "c"], [1,2]]; expect(combinationsPick(searchSpace, [0,1])).to.eql([["a",2], [1,0]]); expect(combinationsPick(searchSpace, [1,0])).to.eql([["b",1], [1,1]]); expect(combinationsPick(searchSpace, [2,1])).to.eql([["c",2], undefined]); }); }); it('histogram', function() { var data = [0,1,2,3,7,2,1,3,9]; var hist = histogram(data); expect([[0,1], [2,3], [7,2], [1,3], [9]]).to.eql(hist); var hist = histogram(data, 3); // 3 bins expect([[0,1,2],[3,7,2],[1,3,9]]).to.eql(hist); var hist = histogram(data, [0,3,6]); // 3 bins expect([[0,1,2,2,1],[3,3],[7,9]]).to.eql(hist); var data = [1,2,3,4]; var hist = histogram(data, [0,3,6]); // 3 bins expect([[1,2],[3,4],[]]).to.eql(hist); }); it("zips", function() { expect(zip([1,2,3], [4,5,6])).to.eql([[1,4],[2,5],[3,6]]); }); describe("async", function() { var numbers = range(1,10); describe("mapAsyncSeries()", function() { it("iterates in order", function(done) { mapAsyncSeries(numbers, function(n, i, next) { setTimeout(function() { next(null, i+1); }, random(0,100)); }, function(err, result) { expect(result).to.eql(numbers); done(); }); }); it("catches errors", function(done) { mapAsyncSeries(numbers, function(n, i, next) { if (i === 2) throw new Error("FOO!"); next(); }, function(err, result) { expect(err.message).to.eql("FOO!"); done(); }); }); it("does not invoke callbacks multiple times", function(done) { mapAsyncSeries(numbers, function(n, i, next) { if (i === 2) next(null, n); setTimeout(curry(next, null, n), 10); }, function(err, result) { expect(result).to.eql(numbers); done(); }); }); }); describe("mapAsync()", function() { it("maps asynchronously", function(done) { mapAsync(numbers, function(n, i, next) { setTimeout(function() { next(null, n); }, random(0,100)); }, function(err, result) { expect(result).to.eql(numbers); done(); }); }); it("maps asynchronously one elem", function(done) { mapAsync([1], function(n, i, next) { setTimeout(function() { next(null, n); }, random(0,100)); }, function(err, result) { expect(result).to.eql([1]); done(); }); }); it("maps asynchronously empty list", function(done) { mapAsync([], function(n, i, next) { setTimeout(function() { next(null, n); }, random(0,100)); }, function(err, result) { expect(result).to.eql([]); done(); }); }); it("does not invoke callbacks twice", function(done) { mapAsync(numbers, function(n, i, next) { setTimeout(function() { next(null, n); }, random(0,100)); if (i == 2) next(null, n); }, function(err, result) { expect(result).to.eql(numbers); done(); }); }); it("catches errors", function(done) { mapAsync(numbers, function(n, i, next) { if (i == 2) throw new Error("FOO!"); setTimeout(function() { next(null, n); }, random(0,100)); }, function(err, result) { expect(err.message).to.equal("FOO!"); done(); }); }); it("can control the number of parallel iterator invocations", function(done) { var maxInvocations = 0, invocations = 0; mapAsync(numbers, {parallel: 3}, function(n, i, next) { invocations++; maxInvocations = Math.max(maxInvocations, invocations); setTimeout(function() { invocations--; next(null, n); }, random(0,100)); }, function(err, result) { expect(maxInvocations).to.equal(3); done(); }); }) }); }); describe("dropping and taking", function() { it("take", function() { expect(take([1,2,3,4,5], 3)).to.eql([1,2,3]); }); it("drop", function() { expect(drop([1,2,3,4,5], 3)).to.eql([4,5]); }); it("takeWhile", function() { expect(takeWhile([1,2,3,4,5], function(n) { return n < 3; })).to.eql([1,2]); expect(takeWhile([1,2,3,4,5], function(n) { return true; })).to.eql([1,2,3,4,5]); expect(takeWhile([1,2,3,4,5], function(n) { return false; })).to.eql([]); }); it("dropWhile", function() { expect(dropWhile([1,2,3,4,5], function(n) { return n < 3; })).to.eql([3, 4,5]); expect(dropWhile([1,2,3,4,5], function(n) { return true; })).to.eql([]); expect(dropWhile([1,2,3,4,5], function(n) { return false; })).to.eql([1,2,3,4,5]); }); }) });