ds-algo-study
Version:
Just experimenting with publishing a package
1,764 lines (1,346 loc) • 55 kB
JavaScript
/* jshint esversion: 6 */
(function() {
'use strict';
describe('Exercises in Recursion', function() {
describe('1. Factorial', function() {
var originalFactorial;
before(function() {
originalFactorial = factorial;
factorial = sinon.spy(factorial);
});
afterEach(function() {
factorial.reset();
});
after(function() {
factorial = originalFactorial;
});
it('should return a number', function() {
expect(factorial(5)).to.be.a('number');
});
it('should return factorial for non-negative integers', function() {
expect(factorial(0)).to.equal(1);
expect(factorial(1)).to.equal(1);
expect(factorial(4)).to.equal(24);
expect(factorial(5)).to.equal(120);
});
it('should return null for negative integers', function() {
expect(factorial(-5)).to.be.null;
});
it('should use recursion by calling self', function() {
factorial(4);
expect(factorial.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
factorial(4);
factorial.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('2. Sum of Integers', function() {
var originalSum;
before(function() {
originalSum = sum;
sum = sinon.spy(sum);
});
afterEach(function() {
sum.reset();
});
after(function() {
sum = originalSum;
});
it('should return a number', function() {
expect(sum([1,2,3,4,5,6])).to.be.a('number');
});
it('should return the sum of an array of non-negative integers', function() {
expect(sum([1,2,3,4,5,6])).to.equal(21);
expect(sum([3,0,34,7,18])).to.equal(62);
});
it('should return the sum of an array of negative integers', function() {
expect(sum([-1,-2,-3,-4,-5,-6])).to.equal(-21);
expect(sum([-3,-0,-34,-7,-18])).to.equal(-62);
});
it('should return the sum of an array of mixed non-negative and negative integers', function() {
expect(sum([1,-2,3,-4,5,-6])).to.equal(-3);
expect(sum([-12,34,-56,78])).to.equal(44);
expect(sum([3,0,-34,-7,18])).to.equal(-20);
});
it('should return 0 for empty array', function() {
expect(sum([])).to.equal(0);
});
it('should accept an array with a single integer', function() {
expect(sum([4])).to.equal(4);
expect(sum([0])).to.equal(0);
expect(sum([-37])).to.equal(-37);
});
it('should not mutate the input array', function() {
var input = [1,2,3,4,5];
sum(input);
expect(input).to.eql([1,2,3,4,5]);
});
it('should use recursion by calling self', function() {
sum([1,2,3,4,5,6]);
expect(sum.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
sum([1,2,3,4,5,6]);
sum.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('3. Sum Integers in Array', function() {
var originalArraySum;
before(function() {
originalArraySum = arraySum;
arraySum = sinon.spy(arraySum);
});
afterEach(function() {
arraySum.reset();
});
after(function() {
arraySum = originalArraySum;
});
it('should return a number', function() {
expect(arraySum([[1],[[2]],3,4])).to.be.a('number');
});
it('should return the sum of nested arrays containing non-negative integers', function() {
expect(arraySum([[1],[2,3],[[4]],5])).to.equal(15);
expect(arraySum([[12,[[34],[56]],78]])).to.equal(180);
expect(arraySum([3,[0,[34,[7,[18]]]]])).to.equal(62);
});
it('should return the sum of nested arrays containing negative integers', function() {
expect(arraySum([[-1],[-2,-3],[[-4]],-5])).to.equal(-15);
expect(arraySum([[-12,[[-34],[-56]],-78]])).to.equal(-180);
expect(arraySum([-3,[0,[-34,[-7,[-18]]]]])).to.equal(-62);
});
it('should return the sum of nested arrays containing both non-negative and negative integers', function() {
expect(arraySum([[1],[-2,3],[[-4]],5,-6])).to.equal(-3);
expect(arraySum([[-12,[[34],[-56]],78]])).to.equal(44);
expect(arraySum([3,[0,[-34,[-7,[18]]]]])).to.equal(-20);
});
it('should return 0 for empty array', function() {
expect(arraySum([])).to.equal(0);
});
it('should accept an array with a single integer', function() {
expect(arraySum([4])).to.equal(4);
expect(arraySum([0])).to.equal(0);
expect(arraySum([-37])).to.equal(-37);
});
it('should not mutate the input array', function() {
var input = [[1],[[2]],3,4];
arraySum(input);
expect(input).to.eql([[1],[[2]],3,4]);
});
it('should use recursion by calling self', function() {
arraySum([[1],[[2]],3,4]);
expect(arraySum.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
arraySum([[1],[[2]],3,4]);
arraySum.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('4. Check if Even', function() {
var originalIsEven;
before(function() {
originalIsEven = isEven;
isEven = sinon.spy(isEven);
});
afterEach(function() {
isEven.reset();
});
after(function() {
isEven = originalIsEven;
});
it('should return a boolean', function() {
expect(isEven(5)).to.be.a('boolean');
expect(isEven(8)).to.be.a('boolean');
});
it("should not use modulo", function() {
var stringified = originalIsEven.toString();
expect(stringified).to.not.contain('%');
var originalModulo = modulo;
modulo = sinon.spy(modulo);
isEven(8);
expect(modulo.called).to.be.false;
modulo = originalModulo;
});
it('should return true for even numbers', function() {
expect(isEven(48)).to.be.true;
expect(isEven(0)).to.be.true;
});
it('should return false for odd numbers', function() {
expect(isEven(17)).to.be.false;
expect(isEven(1)).to.be.false;
});
it('should work with negative integers', function() {
expect(isEven(-14)).to.be.true;
expect(isEven(-31)).to.be.false;
});
it('should use recursion by calling self', function() {
isEven(8);
expect(isEven.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
isEven(8);
isEven.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('5. Sum Below', function() {
var originalSumBelow;
before(function() {
originalSumBelow = sumBelow;
sumBelow = sinon.spy(sumBelow);
});
afterEach(function() {
sumBelow.reset();
});
after(function() {
sumBelow = originalSumBelow;
});
it('should return a number', function() {
expect(sumBelow(10)).to.be.a('number');
});
it('should return the sum of non-negative integers below given integer', function() {
expect(sumBelow(0)).to.equal(0);
expect(sumBelow(1)).to.equal(0);
expect(sumBelow(2)).to.equal(1);
expect(sumBelow(7)).to.equal(21);
expect(sumBelow(12)).to.equal(66);
});
it('should return the sum of negative integers above given negative integer', function() {
expect(sumBelow(-1)).to.equal(0);
expect(sumBelow(-2)).to.equal(-1);
expect(sumBelow(-6)).to.equal(-15);
expect(sumBelow(-11)).to.equal(-55);
});
it('should use recursion by calling self', function() {
sumBelow(5);
expect(sumBelow.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
sumBelow(5);
sumBelow.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('6. Integer Range', function() {
var originalRange;
before(function() {
originalRange = range;
range = sinon.spy(range);
});
afterEach(function() {
range.reset();
});
after(function() {
range = originalRange;
});
it('should return an array', function() {
expect(range(2,7)).to.be.an('array');
});
it('should return the integers between two numbers', function() {
expect(range(3,8)).to.eql([4,5,6,7]);
expect(range(127,131)).to.eql([128,129,130]);
});
it('should return empty array if no integers in range', function() {
expect(range(5,5)).to.eql([]);
expect(range(2,3)).to.eql([]);
});
it('should accept negative integers', function() {
expect(range(-9,-4)).to.eql([-8,-7,-6,-5]);
expect(range(-3,2)).to.eql([-2,-1,0,1]);
expect(range(-3,-2)).to.eql([]);
expect(range(-2,-2)).to.eql([]);
});
it('should accept starting integer that\'s larger than ending', function() {
expect(range(7,2)).to.eql([6,5,4,3]);
expect(range(3,-3)).to.eql([2,1,0,-1,-2]);
expect(range(-9,-4)).to.eql([-8,-7,-6,-5]);
});
it('should use recursion by calling self', function() {
range(3,8);
expect(range.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
range(3,8);
range.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('7. Compute Exponent', function() {
var originalExponent;
before(function() {
originalExponent = exponent;
exponent = sinon.spy(exponent);
});
afterEach(function() {
exponent.reset();
});
after(function() {
exponent = originalExponent;
});
it('should return a number', function() {
expect(exponent(4,3)).to.be.a('number');
});
it("should not use complex math", function() {
expect(originalExponent.toString()).to.not.contain('Math');
});
it('should compute exponent of non-negative integers', function() {
expect(exponent(3,4)).to.equal(81);
expect(exponent(12,5)).to.equal(248832);
expect(exponent(7,2)).to.equal(49);
});
it('returns 1 when exponent is 0', function() {
expect(exponent(8,0)).to.equal(1);
expect(exponent(244,0)).to.equal(1);
});
it('returns base when exponent is 1', function() {
expect(exponent(9,1)).to.equal(9);
expect(exponent(2300,1)).to.equal(2300);
});
it('should accept negative integer for exponent', function() {
expect(exponent(4,-2)).to.equal(0.0625);
expect(exponent(5,-4)).to.equal(0.0016);
expect(exponent(2,-5)).to.equal(0.03125);
});
it('should use recursion by calling self', function() {
exponent(3,4);
expect(exponent.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
exponent(3,4);
exponent.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
// remove the 'x' to enable test
xit('optimize for even numbers', function() {
exponent(3,4);
expect(exponent.callCount).to.be.at.most(4);
exponent.reset();
exponent(12,5);
expect(exponent.callCount).to.be.at.most(5);
exponent.reset();
exponent(19,7);
expect(exponent.callCount).to.be.at.most(6);
});
// remove the 'x' to enable test
xit('should accept negative integer for base', function() {
expect(exponent(-3,4)).to.equal(81);
expect(exponent(-12,5)).to.equal(-248832);
expect(exponent(-7,2)).to.equal(49);
expect(exponent(-7,4)).to.equal(2401);
expect(exponent(-3,5)).to.equal(-243);
});
});
describe('8. Power of Two', function() {
var originalPowerOfTwo;
before(function() {
originalPowerOfTwo = powerOfTwo;
powerOfTwo = sinon.spy(powerOfTwo);
});
afterEach(function() {
powerOfTwo.reset();
});
after(function() {
powerOfTwo = originalPowerOfTwo;
});
it('should return a boolean', function() {
expect(powerOfTwo(5)).to.be.a('boolean');
expect(powerOfTwo(8)).to.be.a('boolean');
});
it('should return true for powers of two', function() {
expect(powerOfTwo(1)).to.be.true;
expect(powerOfTwo(2)).to.be.true;
expect(powerOfTwo(128)).to.be.true;
});
it('should return false when input is not power of two', function() {
expect(powerOfTwo(0)).to.be.false;
expect(powerOfTwo(10)).to.be.false;
expect(powerOfTwo(270)).to.be.false;
});
it('should use recursion by calling self', function() {
powerOfTwo(16);
expect(powerOfTwo.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
powerOfTwo(16);
powerOfTwo.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('9. Reverse String', function() {
var originalReverse;
before(function() {
originalReverse = reverse;
reverse = sinon.spy(reverse);
});
afterEach(function() {
reverse.reset();
});
after(function() {
reverse = originalReverse;
});
it('should return a string', function() {
expect(reverse('traf')).to.be.a('string');
});
it('should return a string in reverse', function() {
var input = 'All my base are belong to you.';
var tupni = '.uoy ot gnoleb era esab ym llA';
expect(reverse(input)).to.equal(tupni);
});
it('should not use native reverse method', function() {
// Spying on Array.prototype.reverse in testSupport.js
reverse('traf');
expect(Array.prototype.reverse.called).to.be.false;
});
it('should use recursion by calling self', function() {
reverse('orangutan');
expect(reverse.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
reverse('orangutan');
reverse.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('10. Palindrome', function() {
var originalPalindrome;
before(function() {
originalPalindrome = palindrome;
palindrome = sinon.spy(palindrome);
});
afterEach(function() {
palindrome.reset();
});
after(function() {
palindrome = originalPalindrome;
});
it('should return a boolean', function() {
expect(palindrome('rotor')).to.be.a('boolean');
expect(palindrome('motor')).to.be.a('boolean');
});
it('should not use native reverse method', function() {
palindrome('hannah');
expect(Array.prototype.reverse.called).to.be.false;
});
it('should return true for palindromes', function() {
expect(palindrome('o')).to.be.true;
expect(palindrome('racecar')).to.be.true;
expect(palindrome('saippuakivikauppias')).to.be.true;
});
it('should return false for non-palindromes', function() {
expect(palindrome('hi')).to.be.false;
expect(palindrome('orangutan')).to.be.false;
});
it('should ignore spaces and capital letters', function() {
expect(palindrome('Rotor')).to.be.true;
expect(palindrome('sAip puaki v iKaup Pias')).to.be.true;
});
it('should use recursion by calling self', function() {
palindrome('racecar');
expect(palindrome.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
palindrome('racecar');
palindrome.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('11. Modulo', function() {
var originalModulo;
before(function() {
originalModulo = modulo;
modulo = sinon.spy(modulo);
});
afterEach(function() {
modulo.reset();
});
after(function() {
modulo = originalModulo;
});
it('should return a number', function() {
expect(modulo(5,2)).to.be.a('number');
expect(modulo(8,4)).to.be.a('number');
});
it("should not use complex math", function() {
var stringified = originalModulo.toString();
expect(stringified).to.not.contain('*');
expect(stringified).to.not.contain('/');
expect(stringified).to.not.contain('%');
expect(stringified).to.not.contain('Math');
});
it('should return the remainder of two integers', function() {
expect(modulo(2, 1)).to.equal(2 % 1);
expect(modulo(17, 5)).to.equal(17 % 5);
expect(modulo(78, 453)).to.equal(78 % 453);
expect(modulo(0, 32)).to.equal(0 % 32);
expect(modulo(0, 0)).to.be.NaN;
});
it('should accept negative integers', function() {
expect(modulo(-79, 82)).to.equal(-79 % 82);
expect(modulo(-275, -502)).to.equal(-275 % -502);
expect(modulo(-275, -274)).to.equal(-275 % -274);
expect(modulo(-4, 2)).to.equal(-4 % 2);
});
it('should use recursion by calling self', function() {
modulo(5,2);
expect(modulo.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
modulo(5,2);
modulo.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('12. Multiply', function() {
var originalMultiply;
before(function() {
originalMultiply = multiply;
multiply = sinon.spy(multiply);
});
afterEach(function() {
multiply.reset();
});
after(function() {
multiply = originalMultiply;
});
it('should return a number', function() {
expect(multiply(5,2)).to.be.a('number');
expect(multiply(8,4)).to.be.a('number');
});
it("should not use complex math", function() {
var stringified = originalMultiply.toString();
expect(stringified).to.not.contain('*');
expect(stringified).to.not.contain('/');
expect(stringified).to.not.contain('%');
expect(stringified).to.not.contain('Math');
var originalModulo = modulo;
modulo = sinon.spy(modulo);
multiply(8,4);
expect(modulo.called).to.be.false;
modulo = originalModulo;
});
it('should return the product of two positive integers', function() {
expect(multiply(1, 2)).to.equal(1 * 2);
expect(multiply(17, 5)).to.equal(17 * 5);
expect(multiply(0, 32)).to.equal(0 * 32);
expect(multiply(0, 0)).to.equal(0 * 0);
// expect(multiply(78, 453)).to.equal(78 * 453);
});
it('should return the product of two negative integers', function() {
expect(multiply(-2, -2)).to.equal(-2 * -2);
expect(multiply(-8, -3)).to.equal(-8 * -3);
expect(multiply(-5, -27)).to.equal(-5 * -27);
// expect(multiply(-79, -82)).to.equal(-79 * -82);
// expect(multiply(-275, -502)).to.equal(-275 * -502);
// expect(multiply(-12, -10)).to.equal(-12 * -10);
// expect(multiply(-22, -3)).to.equal(-22 * -3);
});
it('should return the product of mixed positive and negative integers', function() {
expect(multiply(-79, 82)).to.equal(-79 * 82);
expect(multiply(79, -82)).to.equal(79 * -82);
expect(multiply(2, -2)).to.equal(2 * -2);
expect(multiply(5, -27)).to.equal(5 * -27);
// expect(multiply(-275, 502)).to.equal(-275 * 502);
// expect(multiply(275, -502)).to.equal(275 * -502);
// expect(multiply(-8, 3)).to.equal(-8 * 3);
// expect(multiply(12, -10)).to.equal(12 * -10);
// expect(multiply(-22, 3)).to.equal(-22 * 3);
});
it('should accept parameters in any order', function() {
expect(multiply(2, 1)).to.equal(1 * 2);
expect(multiply(5, 17)).to.equal(5 * 17);
expect(multiply(32, 0)).to.equal(0 * 32);
// expect(multiply(453, 78)).to.equal(78 * 453);
});
it('should use recursion by calling self', function() {
multiply(8,4);
expect(multiply.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
multiply(8,4);
multiply.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('13. Divide', function() {
var originalDivide;
before(function() {
originalDivide = divide;
divide = sinon.spy(divide);
});
afterEach(function() {
divide.reset();
});
after(function() {
divide = originalDivide;
});
it('should return a number', function() {
expect(divide(5,2)).to.be.a('number');
expect(divide(8,4)).to.be.a('number');
});
it("should not use complex math", function() {
var stringified = originalDivide.toString();
expect(stringified).to.not.contain('*');
expect(stringified).to.not.contain('/');
expect(stringified).to.not.contain('%');
expect(stringified).to.not.contain('Math');
var originalModulo = modulo;
modulo = sinon.spy(modulo);
divide(8,4);
expect(modulo.called).to.be.false;
modulo = originalModulo;
});
it('should return the quotient of two integers', function() {
expect(divide(2, 1)).to.equal(~~(2 / 1));
expect(divide(17, 5)).to.equal(~~(17 / 5));
expect(divide(78, 453)).to.equal(~~(78 / 453));
expect(divide(-79, 82)).to.equal(~~(-79 / 82));
expect(divide(-275, -582)).to.equal(~~(-275 / -582));
expect(divide(0, 32)).to.equal(~~(0 / 32));
expect(divide(0, 0)).to.be.NaN;
});
it('should use recursion by calling self', function() {
divide(17, 5);
expect(divide.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
divide(8,4);
divide.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('14. Greatest Common Divisor', function() {
var originalGcd;
before(function() {
originalGcd = gcd;
gcd = sinon.spy(gcd);
});
afterEach(function() {
gcd.reset();
});
after(function() {
gcd = originalGcd;
});
it('should return a number', function() {
expect(gcd(4,36)).to.be.a('number');
});
it('should return greatest common divisor of two positive integers', function() {
expect(gcd(4,36)).to.equal(4);
expect(gcd(24,88)).to.equal(8);
expect(gcd(339,17)).to.equal(1);
expect(gcd(126,900)).to.equal(18);
});
it('should return null for negative integers', function() {
expect(gcd(-4, 2)).to.be.null;
expect(gcd(-5, 5)).to.be.null;
expect(gcd(5, -5)).to.be.null;
expect(gcd(7, -36)).to.be.null;
expect(gcd(-10, -58)).to.be.null;
expect(gcd(-92, -5)).to.be.null;
// expect(gcd(0, 0)).to.be.null;
// expect(gcd(0, 5)).to.be.null;
// expect(gcd(5, 0)).to.be.null;
// expect(gcd(-5, 0)).to.be.null;
// expect(gcd(0, -5)).to.be.null;
});
it('should use recursion by calling self', function() {
gcd(17, 5);
expect(gcd.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
gcd(17,5);
gcd.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('15. Compare Strings', function() {
var originalCompareStr;
before(function() {
originalCompareStr = compareStr;
compareStr = sinon.spy(compareStr);
});
afterEach(function() {
compareStr.reset();
});
after(function() {
compareStr = originalCompareStr;
});
it('should return a boolean', function() {
expect(compareStr('house', 'houses')).to.be.a('boolean');
expect(compareStr('', '')).to.be.a('boolean');
expect(compareStr('tomato', 'tomato')).to.be.a('boolean');
});
it('should return true for identical strings', function() {
expect(compareStr('house', 'houses')).to.be.false;
expect(compareStr('', '')).to.be.true;
expect(compareStr('tomato', 'tomato')).to.be.true;
expect(compareStr('', 'pop')).to.be.false;
expect(compareStr('foot', '')).to.be.false;
expect(compareStr('big dog', 'big dog')).to.be.true;
});
it('should use recursion by calling self', function() {
compareStr('house', 'houses');
expect(compareStr.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
compareStr('house', 'houses');
compareStr.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('16. Create array from string', function() {
var originalCreateArray;
before(function() {
originalCreateArray = createArray;
createArray = sinon.spy(createArray);
});
afterEach(function() {
createArray.reset();
});
after(function() {
createArray = originalCreateArray;
});
it('should return an array', function() {
expect(createArray('hello')).to.be.an('array');
});
it('should return an array where each index is a letter of the string', function() {
expect(createArray('this is not a pipe')).to.eql(['t','h','i','s',' ','i','s',' ','n','o','t',' ','a',' ','p','i','p','e']);
expect(createArray('hologram')).to.eql(['h','o','l','o','g','r','a','m']);
expect(createArray('i')).to.eql(['i']);
});
it('should use recursion by calling self', function() {
createArray('hello');
expect(createArray.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
createArray('hello');
createArray.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('17. Reverse an array', function() {
var originalReverseArr;
before(function() {
originalReverseArr = reverseArr;
reverseArr = sinon.spy(reverseArr);
});
afterEach(function() {
reverseArr.reset();
});
after(function() {
reverseArr = originalReverseArr;
});
it('should return an array', function() {
expect(reverseArr([3,2,1])).to.be.an('array');
});
it('should return array in reversed order', function() {
expect(reverseArr([1,2,3,4,5])).to.eql([5,4,3,2,1]);
expect(reverseArr([8,6,4,2])).to.eql([2,4,6,8]);
});
it('should use recursion by calling self', function() {
reverseArr([3,2,1]);
expect(reverseArr.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
reverseArr([5,4,3]);
reverseArr.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('18. Build an array with a given value and length', function() {
var originalBuildList;
before(function() {
originalBuildList = buildList;
buildList = sinon.spy(buildList);
});
afterEach(function() {
buildList.reset();
});
after(function() {
buildList = originalBuildList;
});
it('should return an array', function() {
expect(buildList(0,5)).to.be.an('array');
});
it('should return array of given length with given value at each index', function() {
expect(buildList(0, 5)).to.eql([0,0,0,0,0]);
expect(buildList('banana', 3)).to.eql(['banana','banana','banana']);
expect(buildList(NaN, 4)).to.eql([NaN, NaN, NaN, NaN]);
expect(buildList(undefined, 1)).to.eql([undefined]);
expect(buildList([], 2)).to.eql([[],[]]);
expect(buildList({}, 4)).to.eql([{},{},{},{}]);
expect(buildList(true, 3)).to.eql([true,true,true]);
});
it('should use recursion by calling self', function() {
buildList(2,4);
expect(buildList.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
buildList('five',3);
buildList.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('19. FizzBuzz', function() {
var originalFizzBuzz, actualResult, expectedResult;
before(function() {
originalFizzBuzz = fizzBuzz;
fizzBuzz = sinon.spy(fizzBuzz);
});
afterEach(function() {
fizzBuzz.reset();
});
after(function() {
fizzBuzz = originalFizzBuzz;
});
it('should return an array', function() {
expect(fizzBuzz(3)).to.be.an('array');
});
it('should return string representations of numbers 1 to n', function() {
actualResult = fizzBuzz(10);
actualResult.forEach(function(value) {
expect(value).to.be.a('string');
});
});
it('should output "Fizz" for multiples of three', function() {
actualResult = fizzBuzz(3);
expectedResult = ['1','2','Fizz'];
expect(actualResult).to.eql(expectedResult);
});
it('should output "Buzz" for multiples of five', function() {
actualResult = fizzBuzz(12);
expectedResult = ['1','2','Fizz','4','Buzz','Fizz','7','8','Fizz','Buzz','11','Fizz'];
expect(actualResult).to.eql(expectedResult);
});
it('should output "FizzBuzz" for multiples of both three and five', function() {
actualResult = fizzBuzz(15);
expectedResult = ['1','2','Fizz','4','Buzz','Fizz','7','8','Fizz','Buzz','11','Fizz','13','14','FizzBuzz'];
expect(actualResult).to.eql(expectedResult);
});
it('should use recursion by calling self', function() {
fizzBuzz(5);
expect(fizzBuzz.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
fizzBuzz(5);
fizzBuzz.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('20. Count value in array', function() {
var originalCountOccurrence;
before(function() {
originalCountOccurrence = countOccurrence;
countOccurrence = sinon.spy(countOccurrence);
});
afterEach(function() {
countOccurrence.reset();
});
after(function() {
countOccurrence = originalCountOccurrence;
});
it('should return a number', function() {
expect(countOccurrence([2,7,4,4,1,4], 4)).to.be.a('number');
expect(countOccurrence([2,'banana',4,4,1,'banana'], 'banana')).to.be.a('number');
});
it('should return the number of occurrences of the value', function() {
expect(countOccurrence([2,7,4,4,1,4], 4)).to.equal(3);
expect(countOccurrence([2,'banana',4,4,1,'banana'], 'banana')).to.equal(2);
expect(countOccurrence([undefined,7,undefined,4,1,4], undefined)).to.equal(2);
expect(countOccurrence(['',null,0,'0',false], 0)).to.equal(1);
expect(countOccurrence(['',null,0,'false',false], false)).to.equal(1);
expect(countOccurrence(['',7,null,0,'0',false], null)).to.equal(1);
expect(countOccurrence(['',7,null,0,'0',false], '')).to.equal(1);
// expect(countOccurrence(['',7,null,0,NaN,'0',false], NaN)).to.equal(1);
});
it('should use recursion by calling self', function() {
countOccurrence([2,7,4,4,1,4], 4);
expect(countOccurrence.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
countOccurrence([2,7,4,4,1,4], 4);
countOccurrence.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('21. Recursive Map', function() {
var originalRMap, timesTwo, input, result;
before(function() {
originalRMap = rMap;
rMap = sinon.spy(rMap);
timesTwo = function(n) { return n * 2; };
});
beforeEach(function() {
input = [1,2,3,4,5];
});
afterEach(function() {
rMap.reset();
});
after(function() {
rMap = originalRMap;
});
it('should return an array', function() {
expect(rMap(input, timesTwo)).to.be.an('array');
});
it('should not use the native version of map', function() {
// Spying on Array.prototype.map in testSupport.js
rMap(input, timesTwo);
expect(Array.prototype.map.called).to.be.false;
});
it('should return new array without mutating the input array', function() {
result = rMap(input, num => num);
expect(input).to.eql([1,2,3,4,5]);
// should deeply equal input
expect(result).to.eql(input);
// should not be same array in memory
expect(result).to.not.equal(input);
});
it('should apply a function to every value in an array', function() {
result = rMap([1,2,3], timesTwo);
expect(result).to.eql([2,4,6]);
});
it('should use recursion by calling self', function() {
rMap([1,2,3,4], timesTwo);
expect(rMap.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
rMap([1,2,3,4], timesTwo);
rMap.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('22. Count key in object', function() {
var originalCountKeysInObj, input;
before(function() {
originalCountKeysInObj = countKeysInObj;
countKeysInObj = sinon.spy(countKeysInObj);
input = {e:{x:'y'},t:{r:{e:'r'},p:{y:'r'}},y:'e'};
});
afterEach(function() {
countKeysInObj.reset();
});
after(function() {
countKeysInObj = originalCountKeysInObj;
});
it('should return a number', function() {
expect(countKeysInObj(input, 'r')).to.be.a('number');
});
it('should return the number of occurrences of the property', function() {
expect(countKeysInObj(input, 'e')).to.equal(2);
expect(countKeysInObj(input, 'x')).to.equal(1);
expect(countKeysInObj(input, 'y')).to.equal(2);
expect(countKeysInObj(input, 't')).to.equal(1);
expect(countKeysInObj(input, 'r')).to.equal(1);
expect(countKeysInObj(input, 'p')).to.equal(1);
});
it('should use recursion by calling self', function() {
countKeysInObj(input, 'e');
expect(countKeysInObj.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
countKeysInObj(input, 'e');
countKeysInObj.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('23. Count value in object', function() {
var originalCountValuesInObj, input;
before(function() {
originalCountValuesInObj = countValuesInObj;
countValuesInObj = sinon.spy(countValuesInObj);
input = {e:{x:'y'},t:{r:{e:'r'},p:{y:'r'}},y:'e'};
});
afterEach(function() {
countValuesInObj.reset();
});
after(function() {
countValuesInObj = originalCountValuesInObj;
});
it('should return a number', function() {
expect(countValuesInObj(input, 'r')).to.be.a('number');
});
it('should return the count of the occurrences of the property', function() {
expect(countValuesInObj(input, 'e')).to.equal(1);
expect(countValuesInObj(input, 'x')).to.equal(0);
expect(countValuesInObj(input, 'y')).to.equal(1);
expect(countValuesInObj(input, 't')).to.equal(0);
expect(countValuesInObj(input, 'r')).to.equal(2);
expect(countValuesInObj(input, 'p')).to.equal(0);
});
it('should use recursion by calling self', function() {
countValuesInObj(input, 'r');
expect(countValuesInObj.callCount).to.be.above(1);
});
it('should be invoked with two arguments', function() {
countValuesInObj(input, 'r');
countValuesInObj.args.forEach(arg => {
expect(arg).to.have.length(2);
});
});
});
describe('24. Replace keys in object', function() {
var originalReplaceKeysInObj, input, output;
before(function() {
originalReplaceKeysInObj = replaceKeysInObj;
replaceKeysInObj = sinon.spy(replaceKeysInObj);
});
beforeEach(function() {
input = {e:{x:'y'},t:{r:{e:'r'},p:{y:'r'}},y:'e'};
});
afterEach(function() {
replaceKeysInObj.reset();
});
after(function() {
replaceKeysInObj = originalReplaceKeysInObj;
});
it('should return an object', function() {
output = replaceKeysInObj(input, 'r', 'a');
expect(output).to.be.an('object');
});
it('should mutate the input object', function() {
output = replaceKeysInObj(input, 'y', 'u');
expect(input).to.equal(output);
});
it('should return object containing renamed keys', function() {
replaceKeysInObj(input, 'e', 'f');
expect(input).to.have.all.keys('f','t','y');
expect(input.f).to.be.an('object');
expect(input.f).to.have.all.keys('x');
expect(input.f.x).to.be.a('string');
expect(input.f.x).to.equal('y');
expect(input.t).to.be.an('object');
expect(input.t).to.have.all.keys('r','p');
expect(input.t.r).to.be.an('object');
expect(input.t.r).to.have.all.keys('f');
expect(input.t.r.f).to.be.a('string');
expect(input.t.r.f).to.equal('r');
expect(input.t.p).to.be.an('object');
expect(input.t.p).to.have.all.keys('y');
expect(input.t.p.y).to.be.a('string');
expect(input.t.p.y).to.equal('r');
expect(input.y).to.be.a('string');
expect(input.y).to.equal('e');
expect(input).to.not.have.ownProperty('e');
expect(input.t.r).to.not.have.ownProperty('e');
});
it('should return object with same number of keys', function() {
expect(analyze(input)).to.equal(8);
output = replaceKeysInObj(input, 'e', 'f');
expect(analyze(output)).to.equal(8);
});
it('should use recursion by calling self', function() {
replaceKeysInObj(input, 'r', 'a');
expect(replaceKeysInObj.callCount).to.be.above(1);
});
it('should be invoked with three arguments', function() {
replaceKeysInObj(input, 'r', 'a');
replaceKeysInObj.args.forEach(arg => {
expect(arg).to.have.length(3);
});
});
});
describe('25. First n Fibonacci', function() {
var originalFibonacci;
before(function() {
originalFibonacci = fibonacci;
fibonacci = sinon.spy(fibonacci);
});
afterEach(function() {
fibonacci.reset();
});
after(function() {
fibonacci = originalFibonacci;
});
it('should return an array', function() {
expect(fibonacci(5)).to.be.an('array');
});
it('should return first n Fibonacci numbers where n starts at index 1', function() {
expect(fibonacci(1)).to.eql([0,1]);
expect(fibonacci(2)).to.eql([0,1,1]);
expect(fibonacci(3)).to.eql([0,1,1,2]);
expect(fibonacci(4)).to.eql([0,1,1,2,3]);
expect(fibonacci(5)).to.eql([0,1,1,2,3,5]);
expect(fibonacci(8)).to.eql([0,1,1,2,3,5,8,13,21]);
});
it('should return null for zero and negative integers', function() {
expect(fibonacci(0)).to.be.null;
expect(fibonacci(-7)).to.be.null;
});
it('should use recursion by calling self', function() {
fibonacci(5);
expect(fibonacci.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
fibonacci(5);
fibonacci.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('26. Return nth Fibonacci', function() {
var originalNthFibo;
before(function() {
originalNthFibo = nthFibo;
nthFibo = sinon.spy(nthFibo);
});
afterEach(function() {
nthFibo.reset();
});
after(function() {
nthFibo = originalNthFibo;
});
it('should return a number', function() {
expect(nthFibo(5)).to.be.a('number');
});
it('should return the nth nthFibo number', function() {
expect(nthFibo(0)).to.equal(0);
expect(nthFibo(1)).to.equal(1);
expect(nthFibo(2)).to.equal(1);
expect(nthFibo(3)).to.equal(2);
expect(nthFibo(4)).to.equal(3);
expect(nthFibo(5)).to.equal(5);
expect(nthFibo(8)).to.equal(21);
});
it('should return null for negative integers', function() {
expect(nthFibo(-5)).to.be.null;
expect(nthFibo(-7)).to.be.null;
});
it('should use recursion by calling self', function() {
nthFibo(5);
expect(nthFibo.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
nthFibo(5);
nthFibo.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('27. Capitalize words in array', function() {
var originalCapitalizeWords;
before(function() {
originalCapitalizeWords = capitalizeWords;
capitalizeWords = sinon.spy(capitalizeWords);
});
afterEach(function() {
capitalizeWords.reset();
});
after(function() {
capitalizeWords = originalCapitalizeWords;
});
it('should return an array', function() {
expect(capitalizeWords(['recursion'])).to.be.an('array');
});
it('should capitalize all words in array', function() {
expect(capitalizeWords(["ceci","n'est","pas","une","pipe"])).to.eql(["CECI", "N'EST", "PAS", "UNE", "PIPE"]);
});
it('should use recursion by calling self', function() {
capitalizeWords(['i','am','learning','recursion']);
expect(capitalizeWords.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
capitalizeWords(['you','got','this']);
capitalizeWords.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('28. Capitalize first letter of words in array', function() {
var originalCapitalizeFirst;
before(function() {
originalCapitalizeFirst = capitalizeFirst;
capitalizeFirst = sinon.spy(capitalizeFirst);
});
afterEach(function() {
capitalizeFirst.reset();
});
after(function() {
capitalizeFirst = originalCapitalizeFirst;
});
it('should return an array', function() {
expect(capitalizeFirst(['recursion'])).to.be.an('array');
});
it('should capitalize first letter of each word in array', function() {
expect(capitalizeFirst(["ceci","n'est","pas","une","pipe"])).to.eql(["Ceci", "N'est", "Pas", "Une", "Pipe"]);
});
it('should use recursion by calling self', function() {
capitalizeFirst(["ceci","n'est","pas","une","pipe"]);
expect(capitalizeFirst.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
capitalizeFirst(['you','got','this']);
capitalizeFirst.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('29. Sum even numbers in nested objects', function() {
var originalNestedEvenSum, input;
before(function() {
originalNestedEvenSum = nestedEvenSum;
nestedEvenSum = sinon.spy(nestedEvenSum);
input = {
a: 2,
b: {b: 2, bb: {b: 3, bb: {b: 2}}},
c: {c: {c: 2}, cc: 'ball', ccc: 5},
d: 1,
e: {e: {e: 2}, ee: 'car'}
};
});
afterEach(function() {
nestedEvenSum.reset();
});
after(function() {
nestedEvenSum = originalNestedEvenSum;
});
it('should return a number', function() {
expect(nestedEvenSum(input)).to.be.a('number');
});
it('should sum even numbers', function() {
expect(nestedEvenSum(input)).to.equal(10);
});
it('should use recursion by calling self', function() {
nestedEvenSum(input);
expect(nestedEvenSum.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
nestedEvenSum(input);
nestedEvenSum.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('30. Flatten nested arrays', function() {
var originalFlatten;
before(function() {
originalFlatten = flatten;
flatten = sinon.spy(flatten);
});
afterEach(function() {
flatten.reset();
});
after(function() {
flatten = originalFlatten;
});
it('should return an array', function() {
expect(flatten([1,[2],[[3]]])).to.be.an('array');
});
it('should return flattened array', function() {
expect(flatten([[1],[2,3],[[4]],5])).to.eql([1,2,3,4,5]);
expect(flatten([3,[0,[34,[7,[18]]]]])).to.eql([3,0,34,7,18]);
expect(flatten([[[[[3],0],34],7],18])).to.eql([3,0,34,7,18]);
expect(flatten([[1],[2,[],3],[],[[4]],5])).to.eql([1,2,3,4,5]);
});
it('should use recursion by calling self', function() {
flatten([3,[0,[34]]]);
expect(flatten.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
flatten([3,[0,[34]]]);
flatten.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('31. Tally letters in string', function() {
var originalLetterTally;
before(function() {
originalLetterTally = letterTally;
letterTally = sinon.spy(letterTally);
});
afterEach(function() {
letterTally.reset();
});
after(function() {
letterTally = originalLetterTally;
});
it('should return an object', function() {
expect(letterTally('orangutan')).to.be.an('object');
});
it('should return object containing tallies of unique letters', function() {
var output = letterTally('potato');
expect(output.p).to.equal(1);
expect(output.o).to.equal(2);
expect(output.t).to.equal(2);
expect(output.a).to.equal(1);
});
it('should return object containing the number of keys corresponding to unique letters', function() {
var output = letterTally('mississippi');
var keyCount = Object.keys(output).length;
expect(keyCount).to.equal(4);
});
it('should use recursion by calling self', function() {
letterTally('invasion');
expect(letterTally.callCount).to.be.above(1);
});
it('should be invoked with at most two arguments', function() {
letterTally('invasion');
letterTally.args.forEach(arg => {
expect(arg).to.have.length.of.at.most(2);
});
});
});
describe('32. Eliminate consecutive duplicates', function() {
var originalCompress, input1, input2;
before(function() {
originalCompress = compress;
compress = sinon.spy(compress);
input1 = [1,2,2,3,4,4,5,5,5];
input2 = [1,2,2,3,4,4,2,5,5,5,4,4];
});
afterEach(function() {
compress.reset();
});
after(function() {
compress = originalCompress;
});
it('should return an array', function() {
expect(compress(input1)).to.be.an('array');
});
it('should not mutate the input array', function() {
var result = compress(input1);
expect(input1).to.eql([1,2,2,3,4,4,5,5,5]);
expect(input1).to.not.equal(result);
});
it('should remove consecutive duplicates', function() {
expect(compress(input1)).to.eql([1,2,3,4,5]);
expect(compress(input2)).to.eql([1,2,3,4,2,5,4]);
});
it('should use recursion by calling self', function() {
compress(input1);
expect(compress.callCount).to.be.above(1);
});
it('should be invoked with one argument', function() {
compress(input1);
compress.args.forEach(arg => {
expect(arg).to.have.length(1);
});
});
});
describe('33. Augment each element in nested arrays', function() {
var originalAugmentElements;