Merge branch 'main' into fix_typo_readme
This commit is contained in:
		
						commit
						614b8b8c35
					
				| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					version: 2.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					orbs:
 | 
				
			||||||
 | 
					  node: circleci/node@5.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  test-solutions:
 | 
				
			||||||
 | 
					    executor: node/default
 | 
				
			||||||
 | 
					    steps: 
 | 
				
			||||||
 | 
					      - checkout
 | 
				
			||||||
 | 
					      - node/install-packages:
 | 
				
			||||||
 | 
					          pkg-manager: npm
 | 
				
			||||||
 | 
					      - run:
 | 
				
			||||||
 | 
					          command: npm run test solution
 | 
				
			||||||
 | 
					          name: Run tests in **/solution/*.spec.js
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					workflows:
 | 
				
			||||||
 | 
					  test-solutions:
 | 
				
			||||||
 | 
					    jobs:
 | 
				
			||||||
 | 
					      - test-solutions
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					const helloWorld = function () {
 | 
				
			||||||
 | 
					  return "Hello, World!";
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = helloWorld;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					const helloWorld = require('./helloWorld-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('Hello World', function () {
 | 
				
			||||||
 | 
					  test('says "Hello, World!"', function () {
 | 
				
			||||||
 | 
					    expect(helloWorld()).toEqual('Hello, World!');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					const repeatString = function (word, times) {
 | 
				
			||||||
 | 
					  if (times < 0) return "ERROR";
 | 
				
			||||||
 | 
					  let string = "";
 | 
				
			||||||
 | 
					  for (let i = 0; i < times; i++) {
 | 
				
			||||||
 | 
					    string += word;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return string;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = repeatString;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,39 @@
 | 
				
			||||||
 | 
					const repeatString = require('./repeatString-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('repeatString', () => {
 | 
				
			||||||
 | 
					  test('repeats the string', () => {
 | 
				
			||||||
 | 
					    expect(repeatString('hey', 3)).toEqual('heyheyhey');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('repeats the string many times', () => {
 | 
				
			||||||
 | 
					    expect(repeatString('hey', 10)).toEqual('heyheyheyheyheyheyheyheyheyhey');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('repeats the string 1 times', () => {
 | 
				
			||||||
 | 
					    expect(repeatString('hey', 1)).toEqual('hey');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('repeats the string 0 times', () => {
 | 
				
			||||||
 | 
					    expect(repeatString('hey', 0)).toEqual('');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('returns ERROR with negative numbers', () => {
 | 
				
			||||||
 | 
					    expect(repeatString('hey', -1)).toEqual('ERROR');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('repeats the string a random amount of times', function () {
 | 
				
			||||||
 | 
					    /*The number is generated by using Math.random to get a value from between
 | 
				
			||||||
 | 
					    0 to 1, when this is multiplied by 1000 and rounded down with Math.floor it 
 | 
				
			||||||
 | 
					    equals a number between 0 to 999 (this number will change everytime you run
 | 
				
			||||||
 | 
					    the test).*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // DO NOT use Math.floor(Math.random() * 1000) in your code,
 | 
				
			||||||
 | 
					    // this test generates a random number, then passes it into your code with a function parameter.
 | 
				
			||||||
 | 
					    // If this doesn't make sense, you should go read about functions here: https://www.theodinproject.com/paths/foundations/courses/foundations/lessons/fundamentals-part-3
 | 
				
			||||||
 | 
					    const number = Math.floor(Math.random() * 1000);
 | 
				
			||||||
 | 
					    /*The .match(/((hey))/g).length is a regex that will count the number of heys
 | 
				
			||||||
 | 
					    in the result, which if your function works correctly will equal the number that
 | 
				
			||||||
 | 
					    was randomaly generated. */
 | 
				
			||||||
 | 
					    expect(repeatString('hey', number).match(/((hey))/g).length).toEqual(
 | 
				
			||||||
 | 
					      number
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with blank strings', () => {
 | 
				
			||||||
 | 
					    expect(repeatString('', 10)).toEqual('');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,5 @@ Pretty simple, write a function called `reverseString` that returns its input, r
 | 
				
			||||||
reverseString('hello there') // returns 'ereht olleh'
 | 
					reverseString('hello there') // returns 'ereht olleh'
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You will notice in this exercise that there are multiple tests, after making the first one pass, enable the others one by one by deleting the `.skip` in front the `test.skip()` function.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## Hints
 | 
					## Hints
 | 
				
			||||||
Strings in JavaScript cannot be reversed directly so you're going to have to split it into something else first.. do the reversal and then join it back together into a string.
 | 
					Strings in JavaScript cannot be reversed directly so you're going to have to split it into something else first.. do the reversal and then join it back together into a string.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					const reverseString = function (string) {
 | 
				
			||||||
 | 
					  return string.split("").reverse().join("");
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = reverseString;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					const reverseString = require('./reverseString-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('reverseString', () => {
 | 
				
			||||||
 | 
					  test('reverses single word', () => {
 | 
				
			||||||
 | 
					    expect(reverseString('hello')).toEqual('olleh');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('reverses multiple words', () => {
 | 
				
			||||||
 | 
					    expect(reverseString('hello there')).toEqual('ereht olleh');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('works with numbers and punctuation', () => {
 | 
				
			||||||
 | 
					    expect(reverseString('123! abc!')).toEqual('!cba !321');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with blank strings', () => {
 | 
				
			||||||
 | 
					    expect(reverseString('')).toEqual('');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					// we have 2 solutions here, an easier one and a more advanced one.
 | 
				
			||||||
 | 
					// The easiest way to get an array of the rest of the arguments that are passed to a function
 | 
				
			||||||
 | 
					// is using the rest operator. If this is unfamiliar to you look it up!
 | 
				
			||||||
 | 
					const removeFromArray = function (array, ...args) {
 | 
				
			||||||
 | 
					  // create a new empty array
 | 
				
			||||||
 | 
					  const newArray = [];
 | 
				
			||||||
 | 
					  // use forEach to go through the array
 | 
				
			||||||
 | 
					  array.forEach((item) => {
 | 
				
			||||||
 | 
					    // push every element into the new array
 | 
				
			||||||
 | 
					    // UNLESS it is included in the function arguments
 | 
				
			||||||
 | 
					    // so we create a new array with every item, except those that should be removed
 | 
				
			||||||
 | 
					    if (!args.includes(item)) {
 | 
				
			||||||
 | 
					      newArray.push(item);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  // and return that array
 | 
				
			||||||
 | 
					  return newArray;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A simpler, but more advanced way to do it is to use the 'filter' function,
 | 
				
			||||||
 | 
					// which basically does what we did with the forEach above.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// var removeFromArray = function(array, ...args) {
 | 
				
			||||||
 | 
					//   return array.filter(val => !args.includes(val))
 | 
				
			||||||
 | 
					// }
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = removeFromArray;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					const removeFromArray = require('./removeFromArray-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('removeFromArray', () => {
 | 
				
			||||||
 | 
					  test('removes a single value', () => {
 | 
				
			||||||
 | 
					    expect(removeFromArray([1, 2, 3, 4], 3)).toEqual([1, 2, 4]);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('removes multiple values', () => {
 | 
				
			||||||
 | 
					    expect(removeFromArray([1, 2, 3, 4], 3, 2)).toEqual([1, 4]);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('ignores non present values', () => {
 | 
				
			||||||
 | 
					    expect(removeFromArray([1, 2, 3, 4], 7, 'tacos')).toEqual([1, 2, 3, 4]);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('ignores non present values, but still works', () => {
 | 
				
			||||||
 | 
					    expect(removeFromArray([1, 2, 3, 4], 7, 2)).toEqual([1, 3, 4]);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('can remove all values', () => {
 | 
				
			||||||
 | 
					    expect(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)).toEqual([]);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with strings', () => {
 | 
				
			||||||
 | 
					    expect(removeFromArray(['hey', 2, 3, 'ho'], 'hey', 3)).toEqual([2, 'ho']);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('only removes same type', () => {
 | 
				
			||||||
 | 
					    expect(removeFromArray([1, 2, 3], '1', 3)).toEqual([1, 2]);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					const sumAll = function (min, max) {
 | 
				
			||||||
 | 
					  if (!Number.isInteger(min) || !Number.isInteger(max)) return "ERROR";
 | 
				
			||||||
 | 
					  if (min < 0 || max < 0) return "ERROR";
 | 
				
			||||||
 | 
					  if (min > max) {
 | 
				
			||||||
 | 
					    const temp = min;
 | 
				
			||||||
 | 
					    min = max;
 | 
				
			||||||
 | 
					    max = temp;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let sum = 0;
 | 
				
			||||||
 | 
					  for (let i = min; i < max + 1; i++) {
 | 
				
			||||||
 | 
					    sum += i;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return sum;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = sumAll;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					const sumAll = require('./sumAll-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('sumAll', () => {
 | 
				
			||||||
 | 
					  test('sums numbers within the range', () => {
 | 
				
			||||||
 | 
					    expect(sumAll(1, 4)).toEqual(10);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with large numbers', () => {
 | 
				
			||||||
 | 
					    expect(sumAll(1, 4000)).toEqual(8002000);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with larger number first', () => {
 | 
				
			||||||
 | 
					    expect(sumAll(123, 1)).toEqual(7626);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('returns ERROR with negative numbers', () => {
 | 
				
			||||||
 | 
					    expect(sumAll(-10, 4)).toEqual('ERROR');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('returns ERROR with non-integer parameters', () => {
 | 
				
			||||||
 | 
					    expect(sumAll(2.5, 4)).toEqual('ERROR');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('returns ERROR with non-number parameters', () => {
 | 
				
			||||||
 | 
					    expect(sumAll(10, '90')).toEqual('ERROR');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('returns ERROR with non-number parameters', () => {
 | 
				
			||||||
 | 
					    expect(sumAll(10, [90, 1])).toEqual('ERROR');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					const leapYears = function (year) {
 | 
				
			||||||
 | 
					  return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = leapYears;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					const leapYears = require('./leapYears-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('leapYears', () => {
 | 
				
			||||||
 | 
					  test('works with non century years', () => {
 | 
				
			||||||
 | 
					    expect(leapYears(1996)).toBe(true);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with non century years', () => {
 | 
				
			||||||
 | 
					    expect(leapYears(1997)).toBe(false);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with ridiculously futuristic non century years', () => {
 | 
				
			||||||
 | 
					    expect(leapYears(34992)).toBe(true);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with century years', () => {
 | 
				
			||||||
 | 
					    expect(leapYears(1900)).toBe(false);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with century years', () => {
 | 
				
			||||||
 | 
					    expect(leapYears(1600)).toBe(true);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with century years', () => {
 | 
				
			||||||
 | 
					    expect(leapYears(700)).toBe(false);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					const convertToCelsius = function (fahrenheit) {
 | 
				
			||||||
 | 
					  return Math.round((fahrenheit - 32) * (5 / 9) * 10) / 10;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const convertToFahrenheit = function (celsius) {
 | 
				
			||||||
 | 
					  return Math.round(((celsius * 9) / 5 + 32) * 10) / 10;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  convertToCelsius,
 | 
				
			||||||
 | 
					  convertToFahrenheit,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					const {
 | 
				
			||||||
 | 
					  convertToCelsius,
 | 
				
			||||||
 | 
					  convertToFahrenheit,
 | 
				
			||||||
 | 
					} = require('./tempConversion-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('convertToCelsius', () => {
 | 
				
			||||||
 | 
					  test('works', () => {
 | 
				
			||||||
 | 
					    expect(convertToCelsius(32)).toEqual(0);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('rounds to 1 decimal', () => {
 | 
				
			||||||
 | 
					    expect(convertToCelsius(100)).toEqual(37.8);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with negatives', () => {
 | 
				
			||||||
 | 
					    expect(convertToCelsius(-100)).toEqual(-73.3);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('convertToFahrenheit', () => {
 | 
				
			||||||
 | 
					  test('works', () => {
 | 
				
			||||||
 | 
					    expect(convertToFahrenheit(0)).toEqual(32);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('rounds to 1 decimal', () => {
 | 
				
			||||||
 | 
					    expect(convertToFahrenheit(73.2)).toEqual(163.8);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with negatives', () => {
 | 
				
			||||||
 | 
					    expect(convertToFahrenheit(-10)).toEqual(14);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					const add = function (a, b) {
 | 
				
			||||||
 | 
					  return a + b;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const subtract = function (a, b) {
 | 
				
			||||||
 | 
					  return a - b;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const sum = function (array) {
 | 
				
			||||||
 | 
					  return array.reduce((total, current) => total + current, 0);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const multiply = function (array) {
 | 
				
			||||||
 | 
					  return array.length
 | 
				
			||||||
 | 
					    ? array.reduce((accumulator, nextItem) => accumulator * nextItem)
 | 
				
			||||||
 | 
					    : 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const power = function (a, b) {
 | 
				
			||||||
 | 
					  return Math.pow(a, b);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const factorial = function (n) {
 | 
				
			||||||
 | 
					  if (n === 0) return 1;
 | 
				
			||||||
 | 
					  let product = 1;
 | 
				
			||||||
 | 
					  for (let i = n; i > 0; i--) {
 | 
				
			||||||
 | 
					    product *= i;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return product;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is another implementation of Factorial that uses recursion
 | 
				
			||||||
 | 
					// THANKS to @ThirtyThreeB!
 | 
				
			||||||
 | 
					const recursiveFactorial = function (n) {
 | 
				
			||||||
 | 
					  if (n === 0) {
 | 
				
			||||||
 | 
					    return 1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return n * recursiveFactorial(n - 1);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  add,
 | 
				
			||||||
 | 
					  subtract,
 | 
				
			||||||
 | 
					  sum,
 | 
				
			||||||
 | 
					  multiply,
 | 
				
			||||||
 | 
					  power,
 | 
				
			||||||
 | 
					  factorial,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,77 @@
 | 
				
			||||||
 | 
					const calculator = require('./calculator-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('add', () => {
 | 
				
			||||||
 | 
					  test('adds 0 and 0', () => {
 | 
				
			||||||
 | 
					    expect(calculator.add(0, 0)).toBe(0);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('adds 2 and 2', () => {
 | 
				
			||||||
 | 
					    expect(calculator.add(2, 2)).toBe(4);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('adds positive numbers', () => {
 | 
				
			||||||
 | 
					    expect(calculator.add(2, 6)).toBe(8);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('subtract', () => {
 | 
				
			||||||
 | 
					  test('subtracts numbers', () => {
 | 
				
			||||||
 | 
					    expect(calculator.subtract(10, 4)).toBe(6);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('sum', () => {
 | 
				
			||||||
 | 
					  test('computes the sum of an empty array', () => {
 | 
				
			||||||
 | 
					    expect(calculator.sum([])).toBe(0);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('computes the sum of an array of one number', () => {
 | 
				
			||||||
 | 
					    expect(calculator.sum([7])).toBe(7);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('computes the sum of an array of two numbers', () => {
 | 
				
			||||||
 | 
					    expect(calculator.sum([7, 11])).toBe(18);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('computes the sum of an array of many numbers', () => {
 | 
				
			||||||
 | 
					    expect(calculator.sum([1, 3, 5, 7, 9])).toBe(25);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('multiply', () => {
 | 
				
			||||||
 | 
					  test('multiplies two numbers', () => {
 | 
				
			||||||
 | 
					    expect(calculator.multiply([2, 4])).toBe(8);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('multiplies several numbers', () => {
 | 
				
			||||||
 | 
					    expect(calculator.multiply([2, 4, 6, 8, 10, 12, 14])).toBe(645120);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('power', () => {
 | 
				
			||||||
 | 
					  test('raises one number to the power of another number', () => {
 | 
				
			||||||
 | 
					    expect(calculator.power(4, 3)).toBe(64); // 4 to third power is 64
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('factorial', () => {
 | 
				
			||||||
 | 
					  test('computes the factorial of 0', () => {
 | 
				
			||||||
 | 
					    expect(calculator.factorial(0)).toBe(1); // 0! = 1
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('computes the factorial of 1', () => {
 | 
				
			||||||
 | 
					    expect(calculator.factorial(1)).toBe(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('computes the factorial of 2', () => {
 | 
				
			||||||
 | 
					    expect(calculator.factorial(2)).toBe(2);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('computes the factorial of 5', () => {
 | 
				
			||||||
 | 
					    expect(calculator.factorial(5)).toBe(120);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('computes the factorial of 10', () => {
 | 
				
			||||||
 | 
					    expect(calculator.factorial(10)).toBe(3628800);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					const palindromes = function (string) {
 | 
				
			||||||
 | 
					  const processedString = string.toLowerCase().replace(/[^a-z]/g, "");
 | 
				
			||||||
 | 
					  return processedString.split("").reverse().join("") == processedString;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = palindromes;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					const palindromes = require('./palindromes-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('palindromes', () => {
 | 
				
			||||||
 | 
					  test('works with single words', () => {
 | 
				
			||||||
 | 
					    expect(palindromes('racecar')).toBe(true);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with punctuation ', () => {
 | 
				
			||||||
 | 
					    expect(palindromes('racecar!')).toBe(true);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with upper-case letters ', () => {
 | 
				
			||||||
 | 
					    expect(palindromes('Racecar!')).toBe(true);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with multiple words', () => {
 | 
				
			||||||
 | 
					    expect(palindromes('A car, a man, a maraca.')).toBe(true);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with multiple words', () => {
 | 
				
			||||||
 | 
					    expect(palindromes('Animal loots foliated detail of stool lamina.')).toBe(
 | 
				
			||||||
 | 
					      true
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test("doesn't just always return true", () => {
 | 
				
			||||||
 | 
					    expect(palindromes('ZZZZ car, a man, a maraca.')).toBe(false);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					const fibonacci = function (count) {
 | 
				
			||||||
 | 
					  if (count < 0) return "OOPS";
 | 
				
			||||||
 | 
					  if (count === 0) return 0;
 | 
				
			||||||
 | 
					  let a = 0;
 | 
				
			||||||
 | 
					  let b = 1;
 | 
				
			||||||
 | 
					  for (let i = 1; i < count; i++) {
 | 
				
			||||||
 | 
					    const temp = b;
 | 
				
			||||||
 | 
					    b = a + b;
 | 
				
			||||||
 | 
					    a = temp;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return b;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = fibonacci;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					const fibonacci = require('./fibonacci-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('fibonacci', () => {
 | 
				
			||||||
 | 
					  test('4th fibonacci number is 3', () => {
 | 
				
			||||||
 | 
					    expect(fibonacci(4)).toBe(3);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('6th fibonacci number is 8', () => {
 | 
				
			||||||
 | 
					    expect(fibonacci(6)).toBe(8);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('10th fibonacci number is 55', () => {
 | 
				
			||||||
 | 
					    expect(fibonacci(10)).toBe(55);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('15th fibonacci number is 610', () => {
 | 
				
			||||||
 | 
					    expect(fibonacci(15)).toBe(610);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('25th fibonacci number is 75025', () => {
 | 
				
			||||||
 | 
					    expect(fibonacci(25)).toBe(75025);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test("doesn't accept negatives", () => {
 | 
				
			||||||
 | 
					    expect(fibonacci(-25)).toBe('OOPS');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('DOES accept strings', () => {
 | 
				
			||||||
 | 
					    expect(fibonacci('1')).toBe(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('DOES accept strings', () => {
 | 
				
			||||||
 | 
					    expect(fibonacci('2')).toBe(1);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('DOES accept strings', () => {
 | 
				
			||||||
 | 
					    expect(fibonacci('8')).toBe(21);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					const getTheTitles = function (array) {
 | 
				
			||||||
 | 
					  return array.map((book) => book.title);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = getTheTitles;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					const getTheTitles = require('./getTheTitles-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('getTheTitles', () => {
 | 
				
			||||||
 | 
					  const books = [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      title: 'Book',
 | 
				
			||||||
 | 
					      author: 'Name',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      title: 'Book2',
 | 
				
			||||||
 | 
					      author: 'Name2',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('gets titles', () => {
 | 
				
			||||||
 | 
					    expect(getTheTitles(books)).toEqual(['Book', 'Book2']);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					const findTheOldest = function (array) {
 | 
				
			||||||
 | 
					  return array.reduce((oldest, currentPerson) => {
 | 
				
			||||||
 | 
					    const oldestAge = getAge(oldest.yearOfBirth, oldest.yearOfDeath);
 | 
				
			||||||
 | 
					    const currentAge = getAge(
 | 
				
			||||||
 | 
					      currentPerson.yearOfBirth,
 | 
				
			||||||
 | 
					      currentPerson.yearOfDeath
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    return oldestAge < currentAge ? currentPerson : oldest;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const getAge = function (birth, death) {
 | 
				
			||||||
 | 
					  if (!death) {
 | 
				
			||||||
 | 
					    death = new Date().getFullYear();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return death - birth;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = findTheOldest;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,62 @@
 | 
				
			||||||
 | 
					const findTheOldest = require('./findTheOldest-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('findTheOldest', () => {
 | 
				
			||||||
 | 
					  test('finds the oldest person!', () => {
 | 
				
			||||||
 | 
					    const people = [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'Carly',
 | 
				
			||||||
 | 
					        yearOfBirth: 1942,
 | 
				
			||||||
 | 
					        yearOfDeath: 1970,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'Ray',
 | 
				
			||||||
 | 
					        yearOfBirth: 1962,
 | 
				
			||||||
 | 
					        yearOfDeath: 2011,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'Jane',
 | 
				
			||||||
 | 
					        yearOfBirth: 1912,
 | 
				
			||||||
 | 
					        yearOfDeath: 1941,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    expect(findTheOldest(people).name).toBe('Ray');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('finds the oldest person if someone is still living', () => {
 | 
				
			||||||
 | 
					    const people = [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'Carly',
 | 
				
			||||||
 | 
					        yearOfBirth: 2018,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'Ray',
 | 
				
			||||||
 | 
					        yearOfBirth: 1962,
 | 
				
			||||||
 | 
					        yearOfDeath: 2011,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'Jane',
 | 
				
			||||||
 | 
					        yearOfBirth: 1912,
 | 
				
			||||||
 | 
					        yearOfDeath: 1941,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    expect(findTheOldest(people).name).toBe('Ray');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('finds the oldest person if the OLDEST is still living', () => {
 | 
				
			||||||
 | 
					    const people = [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'Carly',
 | 
				
			||||||
 | 
					        yearOfBirth: 1066,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'Ray',
 | 
				
			||||||
 | 
					        yearOfBirth: 1962,
 | 
				
			||||||
 | 
					        yearOfDeath: 2011,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'Jane',
 | 
				
			||||||
 | 
					        yearOfBirth: 1912,
 | 
				
			||||||
 | 
					        yearOfDeath: 1941,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    ];
 | 
				
			||||||
 | 
					    expect(findTheOldest(people).name).toBe('Carly');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										21
									
								
								README.md
								
								
								
								
							
							
						
						
									
										21
									
								
								README.md
								
								
								
								
							| 
						 | 
					@ -10,17 +10,22 @@ If you have a suggestion to improve an exercise, an idea for a new exercise, or
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## How To Use These Exercises
 | 
					## How To Use These Exercises
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
1. Fork and clone this repository. To learn how to fork a repository, see the GitHub documentation on how to [fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
 | 
					1. Fork and clone this repository. To learn how to fork a repository, see the GitHub documentation on how to [fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo).
 | 
				
			||||||
    * Copies of repositories on your machine are called clones. If you need help cloning to your local environment you can learn how from the GitHub documentation on [cloning a repository](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository).
 | 
					   - Copies of repositories on your machine are called clones. If you need help cloning to your local environment you can learn how from the GitHub documentation on [cloning a repository](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository).
 | 
				
			||||||
2. Before you start working on any exercises, you should first ensure you have the following installed:
 | 
					2. Before you start working on any exercises, you should first ensure you have the following installed:
 | 
				
			||||||
    * **NPM**. You should have installed NPM already in our [Installing Node.js](https://www.theodinproject.com/lessons/foundations-installing-node-js) lesson. Just in case you need to check, type `npm --version` in your terminal. If you get back `Command 'npm' not found, but can be installed with:`, **do not follow the instructions in the terminal** to install with `apt-get` as this causes permission issues. Instead, go back to the installation lesson and install Node with NVM by following the instructions there.
 | 
					   - **NPM**. You should have installed NPM already in our [Installing Node.js](https://www.theodinproject.com/paths/foundations/courses/foundations/lessons/installing-node-js) lesson. Just in case you need to check, type `npm --version` in your terminal. If you get back `Command 'npm' not found, but can be installed with:`, **do not follow the instructions in the terminal** to install with `apt-get` as this causes permission issues. Instead, go back to the installation lesson and install Node with NVM by following the instructions there.
 | 
				
			||||||
    * **Jest**. After cloning this repository to your local machine and installing NPM, go into the newly created directory (`cd javascript-exercises`) and run `npm install`. This will install Jest and set up the testing platform based on our preconfigured settings.
 | 
					   - **Jest**. After cloning this repository to your local machine and installing NPM, go into the newly created directory (`cd javascript-exercises`) and run `npm install`. This will install Jest and set up the testing platform based on our preconfigured settings.
 | 
				
			||||||
3. Each exercise includes 3 files: a markdown file with a description of the task, an empty (or mostly empty) JavaScript file, and a set of tests. To complete an exercise, you'll need to go to the exercise directory with `cd exerciseName` in the terminal and run `npm test exerciseName.spec.js`. This should run the test file and show you the output.
 | 
					3. Each exercise includes the following:
 | 
				
			||||||
    * When you first run a test, it will fail. This is by design! You must open the exercise file and write the code needed to get the test to pass. 
 | 
					
 | 
				
			||||||
 | 
					   - A markdown file with a description of the task, an empty (or mostly empty) JavaScript file, and a set of tests.
 | 
				
			||||||
 | 
					   - A `solutions` directory that contains a solution and the same test file with all of the tests unskipped.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   To complete an exercise, you'll need to go to the exercise directory with `cd exerciseName` in the terminal and run `npm test exerciseName.spec.js`. This should run the test file and show you the output. When you first run a test, it will fail. This is by design! You must open the exercise file and write the code needed to get the test to pass.
 | 
				
			||||||
4. Some of the exercises have test conditions defined in their spec file as `test.skip` compared to `test`. This is purposeful. After you pass one `test`, you will change the next `test.skip` to `test` and test your code again. You'll do this until all conditions are satisfied. **All tests must pass at the same time**, and you should not have any `test.skip` instances by the time you finish an exercise.
 | 
					4. Some of the exercises have test conditions defined in their spec file as `test.skip` compared to `test`. This is purposeful. After you pass one `test`, you will change the next `test.skip` to `test` and test your code again. You'll do this until all conditions are satisfied. **All tests must pass at the same time**, and you should not have any `test.skip` instances by the time you finish an exercise.
 | 
				
			||||||
5. Once you successfully finish an exercise, check TOP's `solutions` branch to compare it with yours. 
 | 
					5. Once you successfully finish an exercise, check the `solutions` directory within each exercise to compare it with yours.
 | 
				
			||||||
   * You should not be checking the solution for an exercise until you finish it!
 | 
					   - You should not be checking the solution for an exercise until you finish it!
 | 
				
			||||||
   * Keep in mind that TOP's solution is not the only solution. Generally as long as all of the tests pass, your solution should be fine.
 | 
					   - Keep in mind that TOP's solution is not the only solution. Generally as long as all of the tests pass, your solution should be fine.
 | 
				
			||||||
6. Do not submit your solutions to this repo, as any PRs that do so will be closed without merging.
 | 
					6. Do not submit your solutions to this repo, as any PRs that do so will be closed without merging.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Note**: Due to the way Jest handles failed tests, it may return an exit code of 1 if any tests fail. NPM will interpret this as an error and you may see some `npm ERR!` messages after Jest runs. You can ignore these, or run your test with `npm test exerciseName.spec.js --silent` to supress the errors.
 | 
					**Note**: Due to the way Jest handles failed tests, it may return an exit code of 1 if any tests fail. NPM will interpret this as an error and you may see some `npm ERR!` messages after Jest runs. You can ignore these, or run your test with `npm test exerciseName.spec.js --silent` to supress the errors.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					const caesar = function (string, shift) {
 | 
				
			||||||
 | 
					  return string
 | 
				
			||||||
 | 
					    .split("")
 | 
				
			||||||
 | 
					    .map((char) => shiftChar(char, shift))
 | 
				
			||||||
 | 
					    .join("");
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const codeSet = (code) => (code < 97 ? 65 : 97);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// this function is just a fancy way of doing % so that it works with negative numbers
 | 
				
			||||||
 | 
					// see this link for details:
 | 
				
			||||||
 | 
					// https://stackoverflow.com/questions/4467539/javascript-modulo-gives-a-negative-result-for-negative-numbers
 | 
				
			||||||
 | 
					const mod = (n, m) => ((n % m) + m) % m;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const shiftChar = (char, shift) => {
 | 
				
			||||||
 | 
					  const code = char.charCodeAt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122)) {
 | 
				
			||||||
 | 
					    return String.fromCharCode(
 | 
				
			||||||
 | 
					      mod(code + shift - codeSet(code), 26) + codeSet(code)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return char;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = caesar;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					const caesar = require('./caesar-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('works with single letters', () => {
 | 
				
			||||||
 | 
					  expect(caesar('A', 1)).toBe('B');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					test('works with words', () => {
 | 
				
			||||||
 | 
					  expect(caesar('Aaa', 1)).toBe('Bbb');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					test('works with phrases', () => {
 | 
				
			||||||
 | 
					  expect(caesar('Hello, World!', 5)).toBe('Mjqqt, Btwqi!');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					test('works with negative shift', () => {
 | 
				
			||||||
 | 
					  expect(caesar('Mjqqt, Btwqi!', -5)).toBe('Hello, World!');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					test('wraps', () => {
 | 
				
			||||||
 | 
					  expect(caesar('Z', 1)).toBe('A');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					test('works with large shift factors', () => {
 | 
				
			||||||
 | 
					  expect(caesar('Hello, World!', 75)).toBe('Ebiil, Tloia!');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					test('works with large negative shift factors', () => {
 | 
				
			||||||
 | 
					  expect(caesar('Hello, World!', -29)).toBe('Ebiil, Tloia!');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					const pigLatin = function (string) {
 | 
				
			||||||
 | 
					  return string
 | 
				
			||||||
 | 
					    .split(" ")
 | 
				
			||||||
 | 
					    .map((word) => {
 | 
				
			||||||
 | 
					      const index = firstVowelIndex(word);
 | 
				
			||||||
 | 
					      const beginning = word.slice(0, index);
 | 
				
			||||||
 | 
					      const ending = word.slice(index);
 | 
				
			||||||
 | 
					      return `${ending}${beginning}ay`;
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    .join(" ");
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const firstVowelIndex = function (string) {
 | 
				
			||||||
 | 
					  const vowels = string.match(/[aeiou]/g);
 | 
				
			||||||
 | 
					  if (vowels[0] == "u" && string[string.indexOf(vowels[0]) - 1] == "q") {
 | 
				
			||||||
 | 
					    return string.indexOf(vowels[1]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return string.indexOf(vowels[0]);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = pigLatin;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,56 @@
 | 
				
			||||||
 | 
					const pigLatin = require('./pigLatin-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//  Topics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//  * modules
 | 
				
			||||||
 | 
					//  * strings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//  Pig Latin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Pig Latin is a made-up children's language that's intended to be confusing. test obeys a few simple rules (below) but when test's spoken quickly test's really difficult for non-children (and non-native speakers) to understand.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Rule 1: If a word begins with a vowel sound, add an "ay" sound to the end of the word.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Rule 2: If a word begins with a consonant sound, move test to the end of the word, and then add an "ay" sound to the end of the word.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// (There are a few more rules for edge cases, and there are regional variants too, but that should be enough to understand the tests.)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// See https://en.wikipedia.org/wiki/Pig_Latin for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('translate', () => {
 | 
				
			||||||
 | 
					  test('translates a word beginning with a vowel', () => {
 | 
				
			||||||
 | 
					    expect(pigLatin('apple')).toBe('appleay');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('translates a word beginning with a consonant', () => {
 | 
				
			||||||
 | 
					    expect(pigLatin('banana')).toBe('ananabay');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('translates a word beginning with two consonants', () => {
 | 
				
			||||||
 | 
					    expect(pigLatin('cherry')).toBe('errychay');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('translates two words', () => {
 | 
				
			||||||
 | 
					    expect(pigLatin('eat pie')).toBe('eatay iepay');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('translates a word beginning with three consonants', () => {
 | 
				
			||||||
 | 
					    expect(pigLatin('three')).toBe('eethray');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('counts "sch" as a single phoneme', () => {
 | 
				
			||||||
 | 
					    expect(pigLatin('school')).toBe('oolschay');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('counts "qu" as a single phoneme', () => {
 | 
				
			||||||
 | 
					    expect(pigLatin('quiet')).toBe('ietquay');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('counts "qu" as a consonant even when its preceded by a consonant', () => {
 | 
				
			||||||
 | 
					    expect(pigLatin('square')).toBe('aresquay');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test('translates many words', () => {
 | 
				
			||||||
 | 
					    expect(pigLatin('the quick brown fox')).toBe('ethay ickquay ownbray oxfay');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					const snakeCase = function (string) {
 | 
				
			||||||
 | 
					  // wtf case
 | 
				
			||||||
 | 
					  string = string.replace(/\.\./g, " ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // this splits up camelcase IF there are no spaces in the word
 | 
				
			||||||
 | 
					  if (string.indexOf(" ") < 0) {
 | 
				
			||||||
 | 
					    string = string.replace(/([A-Z])/g, " $1");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return string
 | 
				
			||||||
 | 
					    .trim()
 | 
				
			||||||
 | 
					    .toLowerCase()
 | 
				
			||||||
 | 
					    .replace(/[,\?\.]/g, "")
 | 
				
			||||||
 | 
					    .replace(/\-/g, " ")
 | 
				
			||||||
 | 
					    .split(" ")
 | 
				
			||||||
 | 
					    .join("_");
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = snakeCase;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					const snakeCase = require('./snakeCase-solution');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('snakeCase', () => {
 | 
				
			||||||
 | 
					  test('works with simple lowercased phrases', () => {
 | 
				
			||||||
 | 
					    expect(snakeCase('hello world')).toEqual('hello_world');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with Caps and punctuation', () => {
 | 
				
			||||||
 | 
					    expect(snakeCase('Hello, World???')).toEqual('hello_world');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with longer phrases', () => {
 | 
				
			||||||
 | 
					    expect(snakeCase('This is the song that never ends....')).toEqual(
 | 
				
			||||||
 | 
					      'this_is_the_song_that_never_ends'
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with camel case', () => {
 | 
				
			||||||
 | 
					    expect(snakeCase('snakeCase')).toEqual('snake_case');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with kebab case', () => {
 | 
				
			||||||
 | 
					    expect(snakeCase('snake-case')).toEqual('snake_case');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					  test('works with WTF case', () => {
 | 
				
			||||||
 | 
					    expect(snakeCase('SnAkE..CaSe..Is..AwEsOmE')).toEqual(
 | 
				
			||||||
 | 
					      'snake_case_is_awesome'
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
		Loading…
	
		Reference in New Issue