├── .eslintignore
├── generator-exercise
├── .gitattributes
├── .eslintignore
├── .gitignore
├── generators
│ └── app
│ │ ├── templates
│ │ ├── dummyfile.txt
│ │ ├── README.md
│ │ ├── title.js
│ │ └── title.spec.js
│ │ └── index.js
├── .travis.yml
├── .yo-rc.json
├── .editorconfig
├── LICENSE
├── package.json
└── README.md
├── .gitignore
├── 01_helloWorld
├── helloWorld.js
├── solution
│ ├── helloWorld-solution.js
│ └── helloWorld-solution.spec.js
├── helloWorld.spec.js
└── README.md
├── 05_sumAll
├── sumAll.js
├── README.md
├── solution
│ ├── sumAll-solution.js
│ └── sumAll-solution.spec.js
└── sumAll.spec.js
├── 06_leapYears
├── leapYears.js
├── solution
│ ├── leapYears-solution.js
│ └── leapYears-solution.spec.js
├── leapYears.spec.js
└── README.md
├── 10_fibonacci
├── fibonacci.js
├── solution
│ ├── fibonacci-solution.js
│ └── fibonacci-solution.spec.js
├── README.md
└── fibonacci.spec.js
├── archive
├── archived_caesar
│ ├── caesar.js
│ ├── caesar.spec.js
│ ├── solution
│ │ ├── caesar-solution.spec.js
│ │ └── caesar-solution.js
│ └── README.md
├── archived_pigLatin
│ ├── pigLatin.js
│ ├── solution
│ │ ├── pigLatin-solution.js
│ │ └── pigLatin-solution.spec.js
│ ├── README.md
│ └── pigLatin.spec.js
└── archived_snakeCase
│ ├── snakeCase.js
│ ├── solution
│ ├── snakeCase-solution.js
│ └── snakeCase-solution.spec.js
│ ├── README.md
│ └── snakeCase.spec.js
├── 09_palindromes
├── palindromes.js
├── solution
│ ├── palindromes-solution.js
│ └── palindromes-solution.spec.js
├── README.md
└── palindromes.spec.js
├── 02_repeatString
├── repeatString.js
├── solution
│ ├── repeatString-solution.js
│ └── repeatString-solution.spec.js
├── README.md
└── repeatString.spec.js
├── 11_getTheTitles
├── getTheTitles.js
├── solution
│ ├── getTheTitles-solution.js
│ └── getTheTitles-solution.spec.js
├── getTheTitles.spec.js
└── README.md
├── 03_reverseString
├── reverseString.js
├── solution
│ ├── reverseString-solution.js
│ └── reverseString-solution.spec.js
├── reverseString.spec.js
└── README.md
├── 12_findTheOldest
├── findTheOldest.js
├── solution
│ ├── findTheOldest-solution.js
│ └── findTheOldest-solution.spec.js
├── README.md
└── findTheOldest.spec.js
├── 04_removeFromArray
├── removeFromArray.js
├── README.md
├── removeFromArray.spec.js
└── solution
│ ├── removeFromArray-solution.spec.js
│ └── removeFromArray-solution.js
├── .eslintrc.json
├── 07_tempConversion
├── tempConversion.js
├── solution
│ ├── tempConversion-solution.js
│ └── tempConversion-solution.spec.js
├── tempConversion.spec.js
└── README.md
├── 08_calculator
├── README.md
├── calculator.js
├── solution
│ ├── calculator-solution.js
│ └── calculator-solution.spec.js
└── calculator.spec.js
├── .circleci
└── config.yml
├── package.json
├── LICENSE
├── .github
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── PULL_REQUEST_TEMPLATE.md
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | generator-exercise/
2 |
--------------------------------------------------------------------------------
/generator-exercise/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | node_modules/
3 | .DS_Store
4 |
--------------------------------------------------------------------------------
/generator-exercise/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage
2 | **/templates
3 |
--------------------------------------------------------------------------------
/generator-exercise/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 |
--------------------------------------------------------------------------------
/generator-exercise/generators/app/templates/dummyfile.txt:
--------------------------------------------------------------------------------
1 | <%= title %>
2 |
--------------------------------------------------------------------------------
/generator-exercise/generators/app/templates/README.md:
--------------------------------------------------------------------------------
1 | # Exercise XX - <%= title %>
2 |
3 |
--------------------------------------------------------------------------------
/generator-exercise/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - 7
4 | - 6
5 | - 4
6 |
--------------------------------------------------------------------------------
/01_helloWorld/helloWorld.js:
--------------------------------------------------------------------------------
1 | const helloWorld = function() {
2 | return ''
3 | };
4 |
5 | module.exports = helloWorld;
6 |
--------------------------------------------------------------------------------
/05_sumAll/sumAll.js:
--------------------------------------------------------------------------------
1 | const sumAll = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = sumAll;
7 |
--------------------------------------------------------------------------------
/generator-exercise/generators/app/templates/title.js:
--------------------------------------------------------------------------------
1 | let <%= title %> = function() {
2 |
3 | };
4 |
5 | module.exports = <%= title %>;
6 |
--------------------------------------------------------------------------------
/06_leapYears/leapYears.js:
--------------------------------------------------------------------------------
1 | const leapYears = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = leapYears;
7 |
--------------------------------------------------------------------------------
/10_fibonacci/fibonacci.js:
--------------------------------------------------------------------------------
1 | const fibonacci = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = fibonacci;
7 |
--------------------------------------------------------------------------------
/archive/archived_caesar/caesar.js:
--------------------------------------------------------------------------------
1 | const caesar = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = caesar;
7 |
--------------------------------------------------------------------------------
/09_palindromes/palindromes.js:
--------------------------------------------------------------------------------
1 | const palindromes = function () {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = palindromes;
7 |
--------------------------------------------------------------------------------
/01_helloWorld/solution/helloWorld-solution.js:
--------------------------------------------------------------------------------
1 | const helloWorld = function () {
2 | return "Hello, World!";
3 | };
4 |
5 | module.exports = helloWorld;
6 |
--------------------------------------------------------------------------------
/02_repeatString/repeatString.js:
--------------------------------------------------------------------------------
1 | const repeatString = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = repeatString;
7 |
--------------------------------------------------------------------------------
/11_getTheTitles/getTheTitles.js:
--------------------------------------------------------------------------------
1 | const getTheTitles = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = getTheTitles;
7 |
--------------------------------------------------------------------------------
/archive/archived_pigLatin/pigLatin.js:
--------------------------------------------------------------------------------
1 | function pigLatin(string) {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = pigLatin;
7 |
--------------------------------------------------------------------------------
/archive/archived_snakeCase/snakeCase.js:
--------------------------------------------------------------------------------
1 | const snakeCase = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = snakeCase;
7 |
--------------------------------------------------------------------------------
/03_reverseString/reverseString.js:
--------------------------------------------------------------------------------
1 | const reverseString = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = reverseString;
7 |
--------------------------------------------------------------------------------
/12_findTheOldest/findTheOldest.js:
--------------------------------------------------------------------------------
1 | const findTheOldest = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = findTheOldest;
7 |
--------------------------------------------------------------------------------
/04_removeFromArray/removeFromArray.js:
--------------------------------------------------------------------------------
1 | const removeFromArray = function() {
2 |
3 | };
4 |
5 | // Do not edit below this line
6 | module.exports = removeFromArray;
7 |
--------------------------------------------------------------------------------
/11_getTheTitles/solution/getTheTitles-solution.js:
--------------------------------------------------------------------------------
1 | const getTheTitles = function (array) {
2 | return array.map((book) => book.title);
3 | };
4 |
5 | module.exports = getTheTitles;
6 |
--------------------------------------------------------------------------------
/03_reverseString/solution/reverseString-solution.js:
--------------------------------------------------------------------------------
1 | const reverseString = function (string) {
2 | return string.split("").reverse().join("");
3 | };
4 |
5 | module.exports = reverseString;
6 |
--------------------------------------------------------------------------------
/06_leapYears/solution/leapYears-solution.js:
--------------------------------------------------------------------------------
1 | const leapYears = function (year) {
2 | return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
3 | };
4 |
5 | module.exports = leapYears;
6 |
--------------------------------------------------------------------------------
/generator-exercise/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-node": {
3 | "promptValues": {
4 | "authorName": "Cody Loyd",
5 | "authorEmail": "codyloyd@gmail.com",
6 | "authorUrl": "codyloyd.com"
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/01_helloWorld/helloWorld.spec.js:
--------------------------------------------------------------------------------
1 | const helloWorld = require('./helloWorld');
2 |
3 | describe('Hello World', function() {
4 | test('says "Hello, World!"', function() {
5 | expect(helloWorld()).toEqual('Hello, World!');
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/generator-exercise/generators/app/templates/title.spec.js:
--------------------------------------------------------------------------------
1 | let <%= title %> = require('./<%=title%>')
2 |
3 | describe('<%=title%>', function() {
4 | test('EDITME', function() {
5 | expect(<%=title%>()).toEqual(' ');
6 | });
7 |
8 | });
9 |
--------------------------------------------------------------------------------
/generator-exercise/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 |
10 | [*.md]
11 | trim_trailing_whitespace = false
12 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "import"
4 | ],
5 | "parserOptions": {
6 | "ecmaVersion": 12,
7 | "sourceType": "module"
8 | },
9 | "rules": {
10 | "eol-last": ["error", "always"]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/07_tempConversion/tempConversion.js:
--------------------------------------------------------------------------------
1 | const convertToCelsius = function() {
2 | };
3 |
4 | const convertToFahrenheit = function() {
5 | };
6 |
7 | // Do not edit below this line
8 | module.exports = {
9 | convertToCelsius,
10 | convertToFahrenheit
11 | };
12 |
--------------------------------------------------------------------------------
/01_helloWorld/solution/helloWorld-solution.spec.js:
--------------------------------------------------------------------------------
1 | const helloWorld = require('./helloWorld-solution');
2 |
3 | describe('Hello World', function () {
4 | test('says "Hello, World!"', function () {
5 | expect(helloWorld()).toEqual('Hello, World!');
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/09_palindromes/solution/palindromes-solution.js:
--------------------------------------------------------------------------------
1 | const palindromes = function (string) {
2 | const processedString = string.toLowerCase().replace(/[^a-z0-9]/g, "");
3 | return processedString.split("").reverse().join("") == processedString;
4 | };
5 |
6 | module.exports = palindromes;
7 |
--------------------------------------------------------------------------------
/02_repeatString/solution/repeatString-solution.js:
--------------------------------------------------------------------------------
1 | const repeatString = function (word, times) {
2 | if (times < 0) return "ERROR";
3 | let string = "";
4 | for (let i = 0; i < times; i++) {
5 | string += word;
6 | }
7 | return string;
8 | };
9 |
10 | module.exports = repeatString;
11 |
--------------------------------------------------------------------------------
/07_tempConversion/solution/tempConversion-solution.js:
--------------------------------------------------------------------------------
1 | const convertToCelsius = function (fahrenheit) {
2 | return Math.round((fahrenheit - 32) * (5 / 9) * 10) / 10;
3 | };
4 |
5 | const convertToFahrenheit = function (celsius) {
6 | return Math.round(((celsius * 9) / 5 + 32) * 10) / 10;
7 | };
8 |
9 | module.exports = {
10 | convertToCelsius,
11 | convertToFahrenheit,
12 | };
13 |
--------------------------------------------------------------------------------
/08_calculator/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 08 - Calculator
2 |
3 | The goal for this exercise is to create a calculator that does the following:
4 |
5 | add, subtract, get the sum, multiply, get the power, and find the factorial
6 |
7 | In order to do this please fill out each function with your solution. Make sure to return the value so you can test it in Jest! To see the expected value
8 | take a look at the spec file that houses the Jest test cases.
--------------------------------------------------------------------------------
/11_getTheTitles/getTheTitles.spec.js:
--------------------------------------------------------------------------------
1 | const getTheTitles = require('./getTheTitles')
2 |
3 | describe('getTheTitles', () => {
4 | const books = [
5 | {
6 | title: 'Book',
7 | author: 'Name'
8 | },
9 | {
10 | title: 'Book2',
11 | author: 'Name2'
12 | }
13 | ]
14 |
15 | test('gets titles', () => {
16 | expect(getTheTitles(books)).toEqual(['Book','Book2']);
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | orbs:
4 | node: circleci/node@5.1.0
5 |
6 | jobs:
7 | test-solutions:
8 | executor: node/default
9 | steps:
10 | - checkout
11 | - node/install-packages:
12 | pkg-manager: npm
13 | - run:
14 | command: npm run test solution
15 | name: Run tests in **/solution/*.spec.js
16 |
17 | workflows:
18 | test-solutions:
19 | jobs:
20 | - test-solutions
21 |
--------------------------------------------------------------------------------
/11_getTheTitles/solution/getTheTitles-solution.spec.js:
--------------------------------------------------------------------------------
1 | const getTheTitles = require('./getTheTitles-solution');
2 |
3 | describe('getTheTitles', () => {
4 | const books = [
5 | {
6 | title: 'Book',
7 | author: 'Name',
8 | },
9 | {
10 | title: 'Book2',
11 | author: 'Name2',
12 | },
13 | ];
14 |
15 | test('gets titles', () => {
16 | expect(getTheTitles(books)).toEqual(['Book', 'Book2']);
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/10_fibonacci/solution/fibonacci-solution.js:
--------------------------------------------------------------------------------
1 | const fibonacci = function(count) {
2 | if (count < 0) return "OOPS";
3 | if (count === 0) return 0;
4 |
5 | let firstPrev = 1;
6 | let secondPrev = 0;
7 |
8 | for (let i = 2; i <= count; i++) {
9 | let current = firstPrev + secondPrev;
10 | secondPrev = firstPrev;
11 | firstPrev = current;
12 | }
13 |
14 | return firstPrev;
15 | };
16 |
17 | module.exports = fibonacci;
18 |
--------------------------------------------------------------------------------
/08_calculator/calculator.js:
--------------------------------------------------------------------------------
1 | const add = function() {
2 |
3 | };
4 |
5 | const subtract = function() {
6 |
7 | };
8 |
9 | const sum = function() {
10 |
11 | };
12 |
13 | const multiply = function() {
14 |
15 | };
16 |
17 | const power = function() {
18 |
19 | };
20 |
21 | const factorial = function() {
22 |
23 | };
24 |
25 | // Do not edit below this line
26 | module.exports = {
27 | add,
28 | subtract,
29 | sum,
30 | multiply,
31 | power,
32 | factorial
33 | };
34 |
--------------------------------------------------------------------------------
/archive/archived_snakeCase/solution/snakeCase-solution.js:
--------------------------------------------------------------------------------
1 | const snakeCase = function (string) {
2 | // wtf case
3 | string = string.replace(/\.\./g, " ");
4 |
5 | // this splits up camelcase IF there are no spaces in the word
6 | if (string.indexOf(" ") < 0) {
7 | string = string.replace(/([A-Z])/g, " $1");
8 | }
9 |
10 | return string
11 | .trim()
12 | .toLowerCase()
13 | .replace(/[,\?\.]/g, "")
14 | .replace(/\-/g, " ")
15 | .split(" ")
16 | .join("_");
17 | };
18 |
19 | module.exports = snakeCase;
20 |
--------------------------------------------------------------------------------
/10_fibonacci/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 10 - Fibonacci
2 |
3 | Create a function that returns a specific member of the Fibonacci sequence:
4 |
5 | > A series of numbers in which each number ( Fibonacci number ) is the sum of the two preceding numbers.
6 | > In this exercise, the Fibonacci sequence used is 1, 1, 2, 3, 5, 8, etc.
7 | > To learn more about Fibonacci sequences, go to: https://en.wikipedia.org/wiki/Fibonacci_sequence
8 |
9 | ```javascript
10 | fibonacci(4); // returns the 4th member of the series: 3 (1, 1, 2, 3)
11 | fibonacci(6); // returns 8
12 | ```
13 |
--------------------------------------------------------------------------------
/09_palindromes/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 09 - Palindromes
2 |
3 | Write a function that determines whether or not a given string is a palindrome.
4 |
5 | A palindrome is a string that is spelled the same both forwards and backwards, usually without considering punctuation or word breaks:
6 |
7 | ### some palindromes:
8 | - A car, a man, a maraca.
9 | - Rats live on no evil star.
10 | - Lid off a daffodil.
11 | - Animal loots foliated detail of stool lamina.
12 | - A nut for a jar of tuna.
13 |
14 | ```javascript
15 | palindromes('racecar') // true
16 | palindromes('tacos') // false
17 | ```
18 |
19 |
20 |
--------------------------------------------------------------------------------
/12_findTheOldest/solution/findTheOldest-solution.js:
--------------------------------------------------------------------------------
1 | const findTheOldest = function (array) {
2 | return array.reduce((oldest, currentPerson) => {
3 | const oldestAge = getAge(oldest.yearOfBirth, oldest.yearOfDeath);
4 | const currentAge = getAge(
5 | currentPerson.yearOfBirth,
6 | currentPerson.yearOfDeath
7 | );
8 | return oldestAge < currentAge ? currentPerson : oldest;
9 | });
10 | };
11 |
12 | const getAge = function (birth, death) {
13 | if (!death) {
14 | death = new Date().getFullYear();
15 | }
16 | return death - birth;
17 | };
18 |
19 | module.exports = findTheOldest;
20 |
--------------------------------------------------------------------------------
/03_reverseString/reverseString.spec.js:
--------------------------------------------------------------------------------
1 | const reverseString = require('./reverseString')
2 |
3 | describe('reverseString', () => {
4 | test('reverses single word', () => {
5 | expect(reverseString('hello')).toEqual('olleh');
6 | });
7 |
8 | test.skip('reverses multiple words', () => {
9 | expect(reverseString('hello there')).toEqual('ereht olleh')
10 | })
11 |
12 | test.skip('works with numbers and punctuation', () => {
13 | expect(reverseString('123! abc!')).toEqual('!cba !321')
14 | })
15 | test.skip('works with blank strings', () => {
16 | expect(reverseString('')).toEqual('')
17 | })
18 | });
19 |
--------------------------------------------------------------------------------
/11_getTheTitles/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 11 - Get the Titles!
2 |
3 | You are given an array of objects that represent books with an author and a title that looks like this:
4 |
5 | ```javascript
6 | const books = [
7 | {
8 | title: 'Book',
9 | author: 'Name'
10 | },
11 | {
12 | title: 'Book2',
13 | author: 'Name2'
14 | }
15 | ]
16 | ```
17 |
18 | Your job is to write a function that takes the array and returns an array of titles:
19 |
20 | ```javascript
21 | getTheTitles(books) // ['Book','Book2']
22 | ```
23 |
24 | ## Hints
25 |
26 | - You should use a built-in javascript method to do most of the work for you!
27 |
--------------------------------------------------------------------------------
/03_reverseString/solution/reverseString-solution.spec.js:
--------------------------------------------------------------------------------
1 | const reverseString = require('./reverseString-solution');
2 |
3 | describe('reverseString', () => {
4 | test('reverses single word', () => {
5 | expect(reverseString('hello')).toEqual('olleh');
6 | });
7 |
8 | test('reverses multiple words', () => {
9 | expect(reverseString('hello there')).toEqual('ereht olleh');
10 | });
11 |
12 | test('works with numbers and punctuation', () => {
13 | expect(reverseString('123! abc!')).toEqual('!cba !321');
14 | });
15 | test('works with blank strings', () => {
16 | expect(reverseString('')).toEqual('');
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/archive/archived_pigLatin/solution/pigLatin-solution.js:
--------------------------------------------------------------------------------
1 | const pigLatin = function (string) {
2 | return string
3 | .split(" ")
4 | .map((word) => {
5 | const index = firstVowelIndex(word);
6 | const beginning = word.slice(0, index);
7 | const ending = word.slice(index);
8 | return `${ending}${beginning}ay`;
9 | })
10 | .join(" ");
11 | };
12 |
13 | const firstVowelIndex = function (string) {
14 | const vowels = string.match(/[aeiou]/g);
15 | if (vowels[0] == "u" && string[string.indexOf(vowels[0]) - 1] == "q") {
16 | return string.indexOf(vowels[1]);
17 | }
18 | return string.indexOf(vowels[0]);
19 | };
20 |
21 | module.exports = pigLatin;
22 |
--------------------------------------------------------------------------------
/05_sumAll/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 05 - sumAll
2 |
3 | Implement a function that takes 2 integers and returns the sum of every number between(and including) them:
4 |
5 | ```javascript
6 | sumAll(1, 4) // returns the sum of 1 + 2 + 3 + 4 which is 10
7 | ```
8 |
9 |
10 | ## Hints
11 |
12 | Think about how you would do this on pen and paper and then how you might translate that process into code:
13 | - make sure you pay attention to the function parameters
14 | - create a variable to hold the final sum
15 | - loop through the given numbers ([link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration))
16 | - on each iteration add the number to the sum
17 | - return the sum after finishing the loop
18 |
--------------------------------------------------------------------------------
/05_sumAll/solution/sumAll-solution.js:
--------------------------------------------------------------------------------
1 | const sumAll = function (min, max) {
2 | if (!Number.isInteger(min) || !Number.isInteger(max)) return "ERROR";
3 | if (min < 0 || max < 0) return "ERROR";
4 | if (min > max) {
5 | const temp = min;
6 | min = max;
7 | max = temp;
8 | }
9 |
10 | // An alternative way to swap the values of min and max like above is to use the array destructuring syntax.
11 | // Here's an optional article on it: https://www.freecodecamp.org/news/array-destructuring-in-es6-30e398f21d10/
12 | // if (min > max) [min, max] = [max, min];
13 |
14 | let sum = 0;
15 | for (let i = min; i <= max; i++) {
16 | sum += i;
17 | }
18 | return sum;
19 | };
20 |
21 | module.exports = sumAll;
22 |
--------------------------------------------------------------------------------
/03_reverseString/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 03 - Reverse a String
2 |
3 | Pretty simple, write a function called `reverseString` that returns its input, reversed!
4 |
5 | ```javascript
6 | reverseString('hello there') // returns 'ereht olleh'
7 | ```
8 |
9 | You will notice in this exercise that there are multiple tests (in the file `reverseString.spec.js`). Currently, only the first test is enabled. After ensuring that the first test passes, enable the remaining tests one by one by removing the `.skip` from the `test.skip()` function.
10 |
11 |
12 | ## Hints
13 | 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.
14 |
--------------------------------------------------------------------------------
/12_findTheOldest/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 12 - Find the Oldest
2 |
3 | Given an array of objects representing people with a birth and death year, return the oldest person.
4 |
5 | Now that you've reached the final exercise, you should be fairly comfortable getting the information you need from test case(s). Take a look at how the array of objects is constructed in this exercise's test cases to help you write your function.
6 |
7 | ## Hints
8 | - You should return the whole person object, but the tests mostly just check to make sure the name is correct.
9 | - This can be done with a couple of chained array methods, or by using `reduce`.
10 | - One of the tests checks for people with no death-date.. use JavaScript's Date function to get their age as of today.
11 |
--------------------------------------------------------------------------------
/archive/archived_pigLatin/README.md:
--------------------------------------------------------------------------------
1 | This exercise is tricky and was removed from our recommendations because it mostly leverages regular expressions for the solution, and those aren't really taught at this point in our curriculum.
2 |
3 | Leaving it here for posterity, or a good challenge for anyone that wants to give it a shot.
4 |
5 | Pig Latin is a children's language that is intended to be confusing when spoken quickly. Your job for this exercise is to create a solution that takes the words given and
6 | turns them into pig latin. Please see the following wikipedia page for details regarding the rules of Pig Latin:
7 |
8 | https://en.wikipedia.org/wiki/Pig_Latin
9 |
10 | The rules section will give the rules and the examples that are required to complete this exercise.
11 |
--------------------------------------------------------------------------------
/archive/archived_snakeCase/README.md:
--------------------------------------------------------------------------------
1 | This exercise is tricky and was removed from our recommendations because it mostly leverages regular expressions for the solution, and those aren't really taught at this point in our curriculum.
2 |
3 | Leaving it here for posterity, or a good challenge for anyone that wants to give it a shot.
4 |
5 | # Exercise XX - snakeCase
6 |
7 | Convert phrases and words into snake case
8 |
9 | > Snake case (or snake\_case) is the practice of writing compound words or phrases in which the elements are separated with one underscore character (\_) and no spaces, with each element's initial letter usually lowercased as in "foo\_bar"
10 |
11 | ```javascript
12 | snakeCase('Hello, World!') // hello_world
13 | snakeCase('snakeCase') // snake_case
14 | ```
15 |
--------------------------------------------------------------------------------
/06_leapYears/leapYears.spec.js:
--------------------------------------------------------------------------------
1 | const leapYears = require('./leapYears')
2 |
3 | describe('leapYears', () => {
4 | test('works with non century years', () => {
5 | expect(leapYears(1996)).toBe(true);
6 | });
7 | test.skip('works with non century years', () => {
8 | expect(leapYears(1997)).toBe(false);
9 | });
10 | test.skip('works with ridiculously futuristic non century years', () => {
11 | expect(leapYears(34992)).toBe(true);
12 | });
13 | test.skip('works with century years', () => {
14 | expect(leapYears(1900)).toBe(false);
15 | });
16 | test.skip('works with century years', () => {
17 | expect(leapYears(1600)).toBe(true);
18 | });
19 | test.skip('works with century years', () => {
20 | expect(leapYears(700)).toBe(false);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/06_leapYears/solution/leapYears-solution.spec.js:
--------------------------------------------------------------------------------
1 | const leapYears = require('./leapYears-solution');
2 |
3 | describe('leapYears', () => {
4 | test('works with non century years', () => {
5 | expect(leapYears(1996)).toBe(true);
6 | });
7 | test('works with non century years', () => {
8 | expect(leapYears(1997)).toBe(false);
9 | });
10 | test('works with ridiculously futuristic non century years', () => {
11 | expect(leapYears(34992)).toBe(true);
12 | });
13 | test('works with century years', () => {
14 | expect(leapYears(1900)).toBe(false);
15 | });
16 | test('works with century years', () => {
17 | expect(leapYears(1600)).toBe(true);
18 | });
19 | test('works with century years', () => {
20 | expect(leapYears(700)).toBe(false);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/06_leapYears/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 06 - leapYears
2 |
3 | Create a function that determines whether or not a given year is a leap year. Leap years are determined by the following rules:
4 |
5 | > Leap years are years divisible by four (like 1984 and 2004). However, years divisible by 100 are not leap years (such as 1800 and 1900) unless they are divisible by 400 (like 1600 and 2000, which were in fact leap years). (Yes, it's all pretty confusing)
6 | >
7 | > -- [Learn to Program](https://pine.fm/LearnToProgram/chap_06.html) by Chris Pine
8 |
9 | ```javascript
10 | leapYears(2000) // is a leap year: returns true
11 | leapYears(1985) // is not a leap year: returns false
12 | ```
13 |
14 |
15 | ## Hints
16 | - use an `if` statement and `&&` to make sure all the conditions are met properly
17 |
--------------------------------------------------------------------------------
/05_sumAll/sumAll.spec.js:
--------------------------------------------------------------------------------
1 | const sumAll = require('./sumAll')
2 |
3 | describe('sumAll', () => {
4 | test('sums numbers within the range', () => {
5 | expect(sumAll(1, 4)).toEqual(10);
6 | });
7 | test.skip('works with large numbers', () => {
8 | expect(sumAll(1, 4000)).toEqual(8002000);
9 | });
10 | test.skip('works with larger number first', () => {
11 | expect(sumAll(123, 1)).toEqual(7626);
12 | });
13 | test.skip('returns ERROR with negative numbers', () => {
14 | expect(sumAll(-10, 4)).toEqual('ERROR');
15 | });
16 | test.skip('returns ERROR with non-number parameters', () => {
17 | expect(sumAll(10, "90")).toEqual('ERROR');
18 | });
19 | test.skip('returns ERROR with non-number parameters', () => {
20 | expect(sumAll(10, [90, 1])).toEqual('ERROR');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/archive/archived_caesar/caesar.spec.js:
--------------------------------------------------------------------------------
1 | const caesar = require('./caesar')
2 |
3 | test('works with single letters', () => {
4 | expect(caesar('A', 1)).toBe('B');
5 | });
6 | test.skip('works with words', () => {
7 | expect(caesar('Aaa', 1)).toBe('Bbb');
8 | });
9 | test.skip('works with phrases', () => {
10 | expect(caesar('Hello, World!', 5)).toBe('Mjqqt, Btwqi!');
11 | });
12 | test.skip('works with negative shift', () => {
13 | expect(caesar('Mjqqt, Btwqi!', -5)).toBe('Hello, World!');
14 | });
15 | test.skip('wraps', () => {
16 | expect(caesar('Z', 1)).toBe('A');
17 | });
18 | test.skip('works with large shift factors', () => {
19 | expect(caesar('Hello, World!', 75)).toBe('Ebiil, Tloia!');
20 | });
21 | test.skip('works with large negative shift factors', () => {
22 | expect(caesar('Hello, World!', -29)).toBe('Ebiil, Tloia!');
23 | });
24 |
--------------------------------------------------------------------------------
/archive/archived_caesar/solution/caesar-solution.spec.js:
--------------------------------------------------------------------------------
1 | const caesar = require('./caesar-solution');
2 |
3 | test('works with single letters', () => {
4 | expect(caesar('A', 1)).toBe('B');
5 | });
6 | test('works with words', () => {
7 | expect(caesar('Aaa', 1)).toBe('Bbb');
8 | });
9 | test('works with phrases', () => {
10 | expect(caesar('Hello, World!', 5)).toBe('Mjqqt, Btwqi!');
11 | });
12 | test('works with negative shift', () => {
13 | expect(caesar('Mjqqt, Btwqi!', -5)).toBe('Hello, World!');
14 | });
15 | test('wraps', () => {
16 | expect(caesar('Z', 1)).toBe('A');
17 | });
18 | test('works with large shift factors', () => {
19 | expect(caesar('Hello, World!', 75)).toBe('Ebiil, Tloia!');
20 | });
21 | test('works with large negative shift factors', () => {
22 | expect(caesar('Hello, World!', -29)).toBe('Ebiil, Tloia!');
23 | });
24 |
--------------------------------------------------------------------------------
/04_removeFromArray/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 04 - removeFromArray
2 |
3 | Implement a function that takes an array and some other arguments then removes the other arguments from that array:
4 |
5 | ```javascript
6 | removeFromArray([1, 2, 3, 4], 3); // should remove 3 and return [1,2,4]
7 | ```
8 |
9 | ## Hints
10 |
11 | The first test on this one is fairly easy, but there are a few things to think about(or google) here for the later tests:
12 |
13 | - how to remove a single element from an array
14 | - how to deal with multiple optional arguments in a javascript function
15 | - [Check this link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments). Scroll down to the bit about `Array.from` or the spread operator. - [Or this link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters).
16 |
--------------------------------------------------------------------------------
/07_tempConversion/tempConversion.spec.js:
--------------------------------------------------------------------------------
1 | const {convertToCelsius, convertToFahrenheit} = require('./tempConversion')
2 |
3 | describe('convertToCelsius', () => {
4 | test('works', () => {
5 | expect(convertToCelsius(32)).toEqual(0);
6 | });
7 | test.skip('rounds to 1 decimal', () => {
8 | expect(convertToCelsius(100)).toEqual(37.8);
9 | });
10 | test.skip('works with negatives', () => {
11 | expect(convertToCelsius(-100)).toEqual(-73.3);
12 | });
13 | });
14 |
15 | describe('convertToFahrenheit', () => {
16 | test.skip('works', () => {
17 | expect(convertToFahrenheit(0)).toEqual(32);
18 | });
19 | test.skip('rounds to 1 decimal', () => {
20 | expect(convertToFahrenheit(73.2)).toEqual(163.8);
21 | });
22 | test.skip('works with negatives', () => {
23 | expect(convertToFahrenheit(-10)).toEqual(14);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/07_tempConversion/solution/tempConversion-solution.spec.js:
--------------------------------------------------------------------------------
1 | const {
2 | convertToCelsius,
3 | convertToFahrenheit,
4 | } = require('./tempConversion-solution');
5 |
6 | describe('convertToCelsius', () => {
7 | test('works', () => {
8 | expect(convertToCelsius(32)).toEqual(0);
9 | });
10 | test('rounds to 1 decimal', () => {
11 | expect(convertToCelsius(100)).toEqual(37.8);
12 | });
13 | test('works with negatives', () => {
14 | expect(convertToCelsius(-100)).toEqual(-73.3);
15 | });
16 | });
17 |
18 | describe('convertToFahrenheit', () => {
19 | test('works', () => {
20 | expect(convertToFahrenheit(0)).toEqual(32);
21 | });
22 | test('rounds to 1 decimal', () => {
23 | expect(convertToFahrenheit(73.2)).toEqual(163.8);
24 | });
25 | test('works with negatives', () => {
26 | expect(convertToFahrenheit(-10)).toEqual(14);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/archive/archived_caesar/solution/caesar-solution.js:
--------------------------------------------------------------------------------
1 | const caesar = function (string, shift) {
2 | return string
3 | .split("")
4 | .map((char) => shiftChar(char, shift))
5 | .join("");
6 | };
7 |
8 | const codeSet = (code) => (code < 97 ? 65 : 97);
9 |
10 | // this function is just a fancy way of doing % so that it works with negative numbers
11 | // see this link for details:
12 | // https://stackoverflow.com/questions/4467539/javascript-modulo-gives-a-negative-result-for-negative-numbers
13 | const mod = (n, m) => ((n % m) + m) % m;
14 |
15 | const shiftChar = (char, shift) => {
16 | const code = char.charCodeAt();
17 |
18 | if ((code >= 65 && code <= 90) || (code >= 97 && code <= 122)) {
19 | return String.fromCharCode(
20 | mod(code + shift - codeSet(code), 26) + codeSet(code)
21 | );
22 | }
23 | return char;
24 | };
25 |
26 | module.exports = caesar;
27 |
--------------------------------------------------------------------------------
/05_sumAll/solution/sumAll-solution.spec.js:
--------------------------------------------------------------------------------
1 | const sumAll = require('./sumAll-solution');
2 |
3 | describe('sumAll', () => {
4 | test('sums numbers within the range', () => {
5 | expect(sumAll(1, 4)).toEqual(10);
6 | });
7 | test('works with large numbers', () => {
8 | expect(sumAll(1, 4000)).toEqual(8002000);
9 | });
10 | test('works with larger number first', () => {
11 | expect(sumAll(123, 1)).toEqual(7626);
12 | });
13 | test('returns ERROR with negative numbers', () => {
14 | expect(sumAll(-10, 4)).toEqual('ERROR');
15 | });
16 | test('returns ERROR with non-integer parameters', () => {
17 | expect(sumAll(2.5, 4)).toEqual('ERROR');
18 | });
19 | test('returns ERROR with non-number parameters', () => {
20 | expect(sumAll(10, '90')).toEqual('ERROR');
21 | });
22 | test('returns ERROR with non-number parameters', () => {
23 | expect(sumAll(10, [90, 1])).toEqual('ERROR');
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/archive/archived_snakeCase/snakeCase.spec.js:
--------------------------------------------------------------------------------
1 | const snakeCase = require('./snakeCase')
2 |
3 | describe('snakeCase', () => {
4 | test('works with simple lowercased phrases', () => {
5 | expect(snakeCase('hello world')).toEqual('hello_world');
6 | });
7 | test.skip('works with Caps and punctuation', () => {
8 | expect(snakeCase('Hello, World???')).toEqual('hello_world');
9 | });
10 | test.skip('works with longer phrases', () => {
11 | expect(snakeCase('This is the song that never ends....')).toEqual('this_is_the_song_that_never_ends');
12 | });
13 | test.skip('works with camel case', () => {
14 | expect(snakeCase('snakeCase')).toEqual('snake_case');
15 | });
16 | test.skip('works with kebab case', () => {
17 | expect(snakeCase('snake-case')).toEqual('snake_case');
18 | });
19 | test.skip('works with WTF case', () => {
20 | expect(snakeCase('SnAkE..CaSe..Is..AwEsOmE')).toEqual('snake_case_is_awesome');
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/archive/archived_snakeCase/solution/snakeCase-solution.spec.js:
--------------------------------------------------------------------------------
1 | const snakeCase = require('./snakeCase-solution');
2 |
3 | describe('snakeCase', () => {
4 | test('works with simple lowercased phrases', () => {
5 | expect(snakeCase('hello world')).toEqual('hello_world');
6 | });
7 | test('works with Caps and punctuation', () => {
8 | expect(snakeCase('Hello, World???')).toEqual('hello_world');
9 | });
10 | test('works with longer phrases', () => {
11 | expect(snakeCase('This is the song that never ends....')).toEqual(
12 | 'this_is_the_song_that_never_ends'
13 | );
14 | });
15 | test('works with camel case', () => {
16 | expect(snakeCase('snakeCase')).toEqual('snake_case');
17 | });
18 | test('works with kebab case', () => {
19 | expect(snakeCase('snake-case')).toEqual('snake_case');
20 | });
21 | test('works with WTF case', () => {
22 | expect(snakeCase('SnAkE..CaSe..Is..AwEsOmE')).toEqual(
23 | 'snake_case_is_awesome'
24 | );
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/10_fibonacci/fibonacci.spec.js:
--------------------------------------------------------------------------------
1 | const fibonacci = require('./fibonacci')
2 |
3 | describe('fibonacci', () => {
4 | test('4th fibonacci number is 3', () => {
5 | expect(fibonacci(4)).toBe(3);
6 | });
7 | test.skip('6th fibonacci number is 8', () => {
8 | expect(fibonacci(6)).toBe(8);
9 | });
10 | test.skip('10th fibonacci number is 55', () => {
11 | expect(fibonacci(10)).toBe(55);
12 | });
13 | test.skip('15th fibonacci number is 610', () => {
14 | expect(fibonacci(15)).toBe(610);
15 | });
16 | test.skip('25th fibonacci number is 75025', () => {
17 | expect(fibonacci(25)).toBe(75025);
18 | });
19 | test.skip('doesn\'t accept negatives', () => {
20 | expect(fibonacci(-25)).toBe("OOPS");
21 | });
22 | test.skip('DOES accept strings', () => {
23 | expect(fibonacci("1")).toBe(1);
24 | });
25 | test.skip('DOES accept strings', () => {
26 | expect(fibonacci("2")).toBe(1);
27 | });
28 | test.skip('DOES accept strings', () => {
29 | expect(fibonacci("8")).toBe(21);
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/07_tempConversion/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 07 - tempConversion
2 |
3 | Write two functions that convert temperatures from Fahrenheit to Celsius, and vice versa:
4 | ```
5 | convertToCelsius(32) // fahrenheit to celsius, should return 0
6 |
7 | convertToFahrenheit(0) // celsius to fahrenheit, should return 32
8 | ```
9 |
10 | Because we are human, we want the result temperature to be rounded to one decimal place: i.e., `convertToCelsius(100)` should return `37.8` and not `37.77777777777778`.
11 |
12 | This exercise asks you to create more than one function so the `module.exports` section of the spec file looks a little different this time. Nothing to worry about, we're just packaging both functions into a single object to be exported.
13 |
14 | ## Hints
15 | - You can find the relevant formulae on [Wikipedia](https://en.wikipedia.org/wiki/Conversion_of_units_of_temperature).
16 |
17 | - Try to find by yourself on the Internet how to round a number to 1 decimal place in JavaScript. If you struggle, have a look [here](https://stackoverflow.com/q/7342957/5433628).
18 |
--------------------------------------------------------------------------------
/08_calculator/solution/calculator-solution.js:
--------------------------------------------------------------------------------
1 | const add = function (a, b) {
2 | return a + b;
3 | };
4 |
5 | const subtract = function (a, b) {
6 | return a - b;
7 | };
8 |
9 | const sum = function (array) {
10 | return array.reduce((total, current) => total + current, 0);
11 | };
12 |
13 | const multiply = function (array) {
14 | return array.reduce((product, current) => product * current)
15 | };
16 |
17 | const power = function (a, b) {
18 | return Math.pow(a, b);
19 | };
20 |
21 | const factorial = function (n) {
22 | if (n === 0) return 1;
23 | let product = 1;
24 | for (let i = n; i > 0; i--) {
25 | product *= i;
26 | }
27 | return product;
28 | };
29 |
30 | // This is another implementation of Factorial that uses recursion
31 | // THANKS to @ThirtyThreeB!
32 | const recursiveFactorial = function (n) {
33 | if (n === 0) {
34 | return 1;
35 | }
36 | return n * recursiveFactorial(n - 1);
37 | };
38 |
39 | module.exports = {
40 | add,
41 | subtract,
42 | sum,
43 | multiply,
44 | power,
45 | factorial,
46 | };
47 |
--------------------------------------------------------------------------------
/04_removeFromArray/removeFromArray.spec.js:
--------------------------------------------------------------------------------
1 | const removeFromArray = require('./removeFromArray')
2 |
3 | describe('removeFromArray', () => {
4 | test('removes a single value', () => {
5 | expect(removeFromArray([1, 2, 3, 4], 3)).toEqual([1, 2, 4]);
6 | });
7 | test.skip('removes multiple values', () => {
8 | expect(removeFromArray([1, 2, 3, 4], 3, 2)).toEqual([1, 4]);
9 | });
10 | test.skip('ignores non present values', () => {
11 | expect(removeFromArray([1, 2, 3, 4], 7, "tacos")).toEqual([1, 2, 3, 4]);
12 | });
13 | test.skip('ignores non present values, but still works', () => {
14 | expect(removeFromArray([1, 2, 3, 4], 7, 2)).toEqual([1, 3, 4]);
15 | });
16 | test.skip('can remove all values', () => {
17 | expect(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)).toEqual([]);
18 | });
19 | test.skip('works with strings', () => {
20 | expect(removeFromArray(["hey", 2, 3, "ho"], "hey", 3)).toEqual([2, "ho"]);
21 | });
22 | test.skip('only removes same type', () => {
23 | expect(removeFromArray([1, 2, 3], "1", 3)).toEqual([1, 2]);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/04_removeFromArray/solution/removeFromArray-solution.spec.js:
--------------------------------------------------------------------------------
1 | const removeFromArray = require('./removeFromArray-solution');
2 |
3 | describe('removeFromArray', () => {
4 | test('removes a single value', () => {
5 | expect(removeFromArray([1, 2, 3, 4], 3)).toEqual([1, 2, 4]);
6 | });
7 | test('removes multiple values', () => {
8 | expect(removeFromArray([1, 2, 3, 4], 3, 2)).toEqual([1, 4]);
9 | });
10 | test('ignores non present values', () => {
11 | expect(removeFromArray([1, 2, 3, 4], 7, 'tacos')).toEqual([1, 2, 3, 4]);
12 | });
13 | test('ignores non present values, but still works', () => {
14 | expect(removeFromArray([1, 2, 3, 4], 7, 2)).toEqual([1, 3, 4]);
15 | });
16 | test('can remove all values', () => {
17 | expect(removeFromArray([1, 2, 3, 4], 1, 2, 3, 4)).toEqual([]);
18 | });
19 | test('works with strings', () => {
20 | expect(removeFromArray(['hey', 2, 3, 'ho'], 'hey', 3)).toEqual([2, 'ho']);
21 | });
22 | test('only removes same type', () => {
23 | expect(removeFromArray([1, 2, 3], '1', 3)).toEqual([1, 2]);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "javascript-exercises",
3 | "version": "1.0.0",
4 | "description": "These are a series of javascript exercises intended to be used alongside the curriculum at 'The Odin Project' They start off nice and easy, but get more involved as you progress through them.",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/TheOdinProject/javascript-exercises.git"
9 | },
10 | "author": "",
11 | "license": "ISC",
12 | "bugs": {
13 | "url": "https://github.com/TheOdinProject/javascript-exercises/issues"
14 | },
15 | "homepage": "https://github.com/TheOdinProject/javascript-exercises#readme",
16 | "devDependencies": {
17 | "eslint": "^8.47.0",
18 | "eslint-config-airbnb-base": "^15.0.0",
19 | "eslint-plugin-import": "^2.28.1",
20 | "jest": "^29.6.4",
21 | "jest-cli": "^29.6.4"
22 | },
23 | "scripts": {
24 | "test": "jest"
25 | },
26 | "eslintConfig": {
27 | "root": true
28 | },
29 | "jest": {
30 | "testPathIgnorePatterns": [
31 | "generator-exercise/"
32 | ]
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/10_fibonacci/solution/fibonacci-solution.spec.js:
--------------------------------------------------------------------------------
1 | const fibonacci = require('./fibonacci-solution');
2 |
3 | describe('fibonacci', () => {
4 | test('4th fibonacci number is 3', () => {
5 | expect(fibonacci(4)).toBe(3);
6 | });
7 | test('6th fibonacci number is 8', () => {
8 | expect(fibonacci(6)).toBe(8);
9 | });
10 | test('10th fibonacci number is 55', () => {
11 | expect(fibonacci(10)).toBe(55);
12 | });
13 | test('15th fibonacci number is 610', () => {
14 | expect(fibonacci(15)).toBe(610);
15 | });
16 | test('25th fibonacci number is 75025', () => {
17 | expect(fibonacci(25)).toBe(75025);
18 | });
19 | test('0th fibonacci number is o', () => {
20 | expect(fibonacci(0)).toBe(0);
21 | });
22 | test("doesn't accept negatives", () => {
23 | expect(fibonacci(-25)).toBe('OOPS');
24 | });
25 | test('DOES accept strings', () => {
26 | expect(fibonacci('1')).toBe(1);
27 | });
28 | test('DOES accept strings', () => {
29 | expect(fibonacci('2')).toBe(1);
30 | });
31 | test('DOES accept strings', () => {
32 | expect(fibonacci('8')).toBe(21);
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/09_palindromes/palindromes.spec.js:
--------------------------------------------------------------------------------
1 | const palindromes = require('./palindromes')
2 |
3 | describe('palindromes', () => {
4 | test('works with single words', () => {
5 | expect(palindromes('racecar')).toBe(true);
6 | });
7 | test.skip('works with punctuation ', () => {
8 | expect(palindromes('racecar!')).toBe(true);
9 | });
10 | test.skip('works with upper-case letters ', () => {
11 | expect(palindromes('Racecar!')).toBe(true);
12 | });
13 | test.skip('works with multiple words', () => {
14 | expect(palindromes('A car, a man, a maraca.')).toBe(true);
15 | });
16 | test.skip('works with multiple words', () => {
17 | expect(palindromes('Animal loots foliated detail of stool lamina.')).toBe(true);
18 | });
19 | test.skip('doesn\'t just always return true', () => {
20 | expect(palindromes('ZZZZ car, a man, a maracaz.')).toBe(false);
21 | });
22 | test.skip('works with numbers in a string', () => {
23 | expect(palindromes('rac3e3car')).toBe(true);
24 | });
25 | test.skip('works with unevenly spaced numbers in a string', () => {
26 | expect(palindromes('r3ace3car')).toBe(false);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 The Odin Project
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/04_removeFromArray/solution/removeFromArray-solution.js:
--------------------------------------------------------------------------------
1 | // we have 2 solutions here, an easier one and a more advanced one.
2 | // The easiest way to get an array of the rest of the arguments that are passed to a function
3 | // is using the rest operator. If this is unfamiliar to you look it up!
4 | const removeFromArray = function (array, ...args) {
5 | // create a new empty array
6 | const newArray = [];
7 | // use forEach to go through the array
8 | array.forEach((item) => {
9 | // push every element into the new array
10 | // UNLESS it is included in the function arguments
11 | // so we create a new array with every item, except those that should be removed
12 | if (!args.includes(item)) {
13 | newArray.push(item);
14 | }
15 | });
16 | // and return that array
17 | return newArray;
18 | };
19 |
20 | // A simpler, but more advanced way to do it is to use the 'filter' function,
21 | // which basically does what we did with the forEach above.
22 |
23 | // var removeFromArray = function(array, ...args) {
24 | // return array.filter(val => !args.includes(val))
25 | // }
26 | //
27 |
28 | module.exports = removeFromArray;
29 |
--------------------------------------------------------------------------------
/09_palindromes/solution/palindromes-solution.spec.js:
--------------------------------------------------------------------------------
1 | const palindromes = require('./palindromes-solution');
2 |
3 | describe('palindromes', () => {
4 | test('works with single words', () => {
5 | expect(palindromes('racecar')).toBe(true);
6 | });
7 | test('works with punctuation ', () => {
8 | expect(palindromes('racecar!')).toBe(true);
9 | });
10 | test('works with upper-case letters ', () => {
11 | expect(palindromes('Racecar!')).toBe(true);
12 | });
13 | test('works with multiple words', () => {
14 | expect(palindromes('A car, a man, a maraca.')).toBe(true);
15 | });
16 | test('works with multiple words', () => {
17 | expect(palindromes('Animal loots foliated detail of stool lamina.')).toBe(
18 | true
19 | );
20 | });
21 | test("doesn't just always return true", () => {
22 | expect(palindromes('ZZZZ car, a man, a maraca.')).toBe(false);
23 | });
24 | test('works with numbers in a string', () => {
25 | expect(palindromes('rac3e3car')).toBe(true);
26 | });
27 | test('works with unevenly spaced numbers in a string', () => {
28 | expect(palindromes('r3ace3car')).toBe(false);
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/generator-exercise/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Cody Loyd (codyloyd.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/generator-exercise/generators/app/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const Generator = require('yeoman-generator');
3 | const chalk = require('chalk');
4 | const yosay = require('yosay');
5 |
6 | module.exports = class extends Generator {
7 | prompting() {
8 | // Have Yeoman greet the user.
9 | this.log(chalk.red('Let\'s do this'));
10 |
11 | const prompts = [{
12 | type: 'input',
13 | name: 'title',
14 | message: 'Enter the exercise title',
15 | default: 'title'
16 | }];
17 |
18 | return this.prompt(prompts).then(props => {
19 | // To access props later use this.props.someAnswer;
20 | this.props = props;
21 | });
22 | }
23 |
24 | writing() {
25 | this.fs.copyTpl(
26 | this.templatePath(`title.js`),
27 | this.destinationPath(`${this.props.title}.js`),
28 | {title: this.props.title}
29 | );
30 | this.fs.copyTpl(
31 | this.templatePath(`title.spec.js`),
32 | this.destinationPath(`${this.props.title}.spec.js`),
33 | {title: this.props.title}
34 | );
35 | this.fs.copyTpl(
36 | this.templatePath(`README.md`),
37 | this.destinationPath(`README.md`),
38 | {title: this.props.title}
39 | );
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/generator-exercise/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "generator-exercise",
3 | "version": "0.0.0",
4 | "description": "generates boilerplate for The Odin Project exercises",
5 | "homepage": "theodinproject.com",
6 | "author": {
7 | "name": "Cody Loyd",
8 | "email": "codyloyd@gmail.com",
9 | "url": "codyloyd.com"
10 | },
11 | "files": [
12 | "generators"
13 | ],
14 | "main": "generators/index.js",
15 | "keywords": [
16 | "lame",
17 | "yeoman-generator"
18 | ],
19 | "devDependencies": {
20 | "yeoman-test": "^1.6.0",
21 | "yeoman-assert": "^3.0.0",
22 | "nsp": "^3.2.1",
23 | "eslint": "^4.1.0",
24 | "eslint-config-xo-space": "^0.16.0",
25 | "jest": "^19.0.2",
26 | "jest-cli": "^20.0.0"
27 | },
28 | "dependencies": {
29 | "chalk": "^1.1.3",
30 | "extend": "^3.0.2",
31 | "yeoman-generator": "^1.0.0",
32 | "yosay": "^2.0.0"
33 | },
34 | "jest": {
35 | "testEnvironment": "node"
36 | },
37 | "scripts": {
38 | "prepublish": "nsp check",
39 | "pretest": "eslint . --fix",
40 | "test": "jest"
41 | },
42 | "eslintConfig": {
43 | "extends": "xo-space",
44 | "env": {
45 | "jest": true,
46 | "node": true
47 | }
48 | },
49 | "repository": "git@github.com:TheOdinProject/javascript-exercises.git",
50 | "license": "MIT"
51 | }
52 |
--------------------------------------------------------------------------------
/archive/archived_caesar/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 13 - Caesar cipher
2 |
3 | Implement the legendary Caesar cipher:
4 |
5 | > In cryptography, a Caesar cipher, also known as Caesar's cipher, the shift cipher, Caesar's code or Caesar shift, is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a left shift of 3, D would be replaced by A, E would become B, and so on. The method is named after Julius Caesar, who used it in his private correspondence.
6 |
7 | Hint: You may need to convert letters to their unicode values. Be sure to read the documentation!
8 |
9 | write a function that takes a string to be encoded and a shift factor and then returns the encoded string:
10 |
11 | ```javascript
12 | caesar('A', 1) // simply shifts the letter by 1: returns 'B'
13 | ```
14 |
15 | the cipher should retain capitalization:
16 | ```javascript
17 | caesar('Hey', 5) // returns 'Mjd'
18 | ```
19 |
20 | should _not_ shift punctuation:
21 | ```javascript
22 | caesar('Hello, World!', 5) //returns 'Mjqqt, Btwqi!'
23 | ```
24 |
25 | the shift should wrap around the alphabet:
26 | ```javascript
27 | caesar('Z', 1) // returns 'A'
28 | ```
29 |
30 | negative numbers should work as well:
31 | ```javascript
32 | caesar('Mjqqt, Btwqi!', -5) // returns 'Hello, World!'
33 | ```
34 |
--------------------------------------------------------------------------------
/generator-exercise/README.md:
--------------------------------------------------------------------------------
1 | # generator-exercise [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url]
2 | > generates boilerplate for The Odin Project exercises
3 |
4 | ## Installation
5 |
6 | First, install [Yeoman](http://yeoman.io) and generator-exercise using [npm](https://www.npmjs.com/) (we assume you have pre-installed [node.js](https://nodejs.org/)).
7 |
8 | ```bash
9 | npm install -g yo
10 | npm install -g generator-exercise
11 | ```
12 |
13 | Then generate your new project:
14 |
15 | ```bash
16 | yo exercise
17 | ```
18 |
19 | ## Getting To Know Yeoman
20 |
21 | * Yeoman has a heart of gold.
22 | * Yeoman is a person with feelings and opinions, but is very easy to work with.
23 | * Yeoman can be too opinionated at times but is easily convinced not to be.
24 | * Feel free to [learn more about Yeoman](http://yeoman.io/).
25 |
26 | ## License
27 |
28 | MIT © [Cody Loyd](codyloyd.com)
29 |
30 |
31 | [npm-image]: https://badge.fury.io/js/generator-exercise.svg
32 | [npm-url]: https://npmjs.org/package/generator-exercise
33 | [travis-image]: https://travis-ci.org/codyloyd/generator-exercise.svg?branch=master
34 | [travis-url]: https://travis-ci.org/codyloyd/generator-exercise
35 | [daviddm-image]: https://david-dm.org/codyloyd/generator-exercise.svg?theme=shields.io
36 | [daviddm-url]: https://david-dm.org/codyloyd/generator-exercise
37 |
--------------------------------------------------------------------------------
/02_repeatString/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 02 - repeatString
2 |
3 | Write a function that simply repeats the string a given number of times:
4 |
5 | ```javascript
6 | repeatString('hey', 3) // returns 'heyheyhey'
7 | ```
8 |
9 | This function will take two arguments, `string` and `num`.
10 |
11 | *Note:* The exercises after this one will not have arguments provided as this one does - you will need to provide them yourself from now on. So read each exercise's README carefully to see what kinds of arguments will be expected.
12 |
13 | You will notice in this exercise that there are multiple tests (see in file `repeatString.spec.js`). Only the first test is currently enabled. So after making sure that this first one passes, enable the others one by one by deleting the `.skip` from the `test.skip()` function.
14 |
15 |
16 | ## Hints
17 |
18 | - Take note of the above function call- how exactly is it being called?
19 |
20 | - You're going to want to use a loop for this one.
21 |
22 | - Create a variable to hold the string you're going to return, create a loop that repeats the given number of times and add the given string to the result on each loop.
23 |
24 | - If running `npm test repeatString.spec.js` returns results similar to the below, make sure you have enabled the rest of the tests, as described in the instructions above.
25 |
26 | ```
27 | Test Suites: 1 passed, 1 total
28 | Tests: 6 skipped, 1 passed, 7 total
29 | ```
30 |
--------------------------------------------------------------------------------
/12_findTheOldest/solution/findTheOldest-solution.spec.js:
--------------------------------------------------------------------------------
1 | const findTheOldest = require('./findTheOldest-solution');
2 |
3 | describe('findTheOldest', () => {
4 | test('finds the oldest person!', () => {
5 | const people = [
6 | {
7 | name: 'Carly',
8 | yearOfBirth: 1942,
9 | yearOfDeath: 1970,
10 | },
11 | {
12 | name: 'Ray',
13 | yearOfBirth: 1962,
14 | yearOfDeath: 2011,
15 | },
16 | {
17 | name: 'Jane',
18 | yearOfBirth: 1912,
19 | yearOfDeath: 1941,
20 | },
21 | ];
22 | expect(findTheOldest(people).name).toBe('Ray');
23 | });
24 | test('finds the oldest person if someone is still living', () => {
25 | const people = [
26 | {
27 | name: 'Carly',
28 | yearOfBirth: 2018,
29 | },
30 | {
31 | name: 'Ray',
32 | yearOfBirth: 1962,
33 | yearOfDeath: 2011,
34 | },
35 | {
36 | name: 'Jane',
37 | yearOfBirth: 1912,
38 | yearOfDeath: 1941,
39 | },
40 | ];
41 | expect(findTheOldest(people).name).toBe('Ray');
42 | });
43 | test('finds the oldest person if the OLDEST is still living', () => {
44 | const people = [
45 | {
46 | name: 'Carly',
47 | yearOfBirth: 1066,
48 | },
49 | {
50 | name: 'Ray',
51 | yearOfBirth: 1962,
52 | yearOfDeath: 2011,
53 | },
54 | {
55 | name: 'Jane',
56 | yearOfBirth: 1912,
57 | yearOfDeath: 1941,
58 | },
59 | ];
60 | expect(findTheOldest(people).name).toBe('Carly');
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/12_findTheOldest/findTheOldest.spec.js:
--------------------------------------------------------------------------------
1 | const findTheOldest = require('./findTheOldest')
2 |
3 | describe('findTheOldest', () => {
4 | test('finds the person with the greatest age!', () => {
5 | const people = [
6 | {
7 | name: "Carly",
8 | yearOfBirth: 1942,
9 | yearOfDeath: 1970,
10 | },
11 | {
12 | name: "Ray",
13 | yearOfBirth: 1962,
14 | yearOfDeath: 2011,
15 | },
16 | {
17 | name: "Jane",
18 | yearOfBirth: 1912,
19 | yearOfDeath: 1941,
20 | },
21 | ]
22 | expect(findTheOldest(people).name).toBe('Ray');
23 | });
24 | test.skip('finds the person with the greatest age if someone is still living', () => {
25 | const people = [
26 | {
27 | name: "Carly",
28 | yearOfBirth: 2018,
29 | },
30 | {
31 | name: "Ray",
32 | yearOfBirth: 1962,
33 | yearOfDeath: 2011,
34 | },
35 | {
36 | name: "Jane",
37 | yearOfBirth: 1912,
38 | yearOfDeath: 1941,
39 | },
40 | ]
41 | expect(findTheOldest(people).name).toBe('Ray');
42 | });
43 | test.skip('finds the person with the greatest age if the OLDEST is still living', () => {
44 | const people = [
45 | {
46 | name: "Carly",
47 | yearOfBirth: 1066,
48 | },
49 | {
50 | name: "Ray",
51 | yearOfBirth: 1962,
52 | yearOfDeath: 2011,
53 | },
54 | {
55 | name: "Jane",
56 | yearOfBirth: 1912,
57 | yearOfDeath: 1941,
58 | },
59 | ]
60 | expect(findTheOldest(people).name).toBe('Carly');
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/02_repeatString/repeatString.spec.js:
--------------------------------------------------------------------------------
1 | const repeatString = require('./repeatString')
2 |
3 | describe('repeatString', () => {
4 | test('repeats the string', () => {
5 | expect(repeatString('hey', 3)).toEqual('heyheyhey');
6 | });
7 | test.skip('repeats the string many times', () => {
8 | expect(repeatString('hey', 10)).toEqual('heyheyheyheyheyheyheyheyheyhey');
9 | });
10 | test.skip('repeats the string 1 times', () => {
11 | expect(repeatString('hey', 1)).toEqual('hey');
12 | });
13 | test.skip('repeats the string 0 times', () => {
14 | expect(repeatString('hey', 0)).toEqual('');
15 | });
16 | test.skip('returns ERROR with negative numbers', () => {
17 | expect(repeatString('hey', -1)).toEqual('ERROR');
18 | });
19 | test.skip('repeats the string a random amount of times', function () {
20 | /*The number is generated by using Math.random to get a value from between
21 | 0 to 1, when this is multiplied by 1000 and rounded down with Math.floor it
22 | equals a number between 0 to 999 (this number will change everytime you run
23 | the test).*/
24 |
25 | // DO NOT use Math.floor(Math.random() * 1000) in your code,
26 | // this test generates a random number, then passes it into your code with a function parameter.
27 | // 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
28 | const number = Math.floor(Math.random() * 1000)
29 | /*The .match(/((hey))/g).length is a regex that will count the number of heys
30 | in the result, which if your function works correctly will equal the number that
31 | was randomly generated. */
32 | expect(repeatString('hey', number).match(/((hey))/g).length).toEqual(number);
33 | });
34 | test.skip('works with blank strings', () => {
35 | expect(repeatString('', 10)).toEqual('');
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest a new feature or enhancement for this project
4 | title: ""
5 | labels: "Status: Needs Review"
6 | assignees: ""
7 | ---
8 |
9 |
10 |
11 | Complete the following REQUIRED checkboxes:
12 | - [ ] I have thoroughly read and understand [The Odin Project Contributing Guide](https://github.com/TheOdinProject/.github/blob/main/CONTRIBUTING.md)
13 | - [ ] The title of this issue follows the `location for request: brief description of request` format, e.g. `Exercises: Add exercise on XYZ`
14 |
15 | The following checkbox is OPTIONAL:
16 |
17 | - [ ] I would like to be assigned this issue to work on it
18 |
19 |
20 |
21 | **1. Description of the Feature Request:**
22 |
25 |
26 |
27 | **2. Acceptance Criteria:**
28 |
34 |
35 |
36 | **3. Additional Information:**
37 |
38 |
39 |
--------------------------------------------------------------------------------
/02_repeatString/solution/repeatString-solution.spec.js:
--------------------------------------------------------------------------------
1 | const repeatString = require('./repeatString-solution');
2 |
3 | describe('repeatString', () => {
4 | test('repeats the string', () => {
5 | expect(repeatString('hey', 3)).toEqual('heyheyhey');
6 | });
7 | test('repeats the string many times', () => {
8 | expect(repeatString('hey', 10)).toEqual('heyheyheyheyheyheyheyheyheyhey');
9 | });
10 | test('repeats the string 1 times', () => {
11 | expect(repeatString('hey', 1)).toEqual('hey');
12 | });
13 | test('repeats the string 0 times', () => {
14 | expect(repeatString('hey', 0)).toEqual('');
15 | });
16 | test('returns ERROR with negative numbers', () => {
17 | expect(repeatString('hey', -1)).toEqual('ERROR');
18 | });
19 | test('repeats the string a random amount of times', function () {
20 | /*The number is generated by using Math.random to get a value from between
21 | 0 to 1, when this is multiplied by 1000 and rounded down with Math.floor it
22 | equals a number between 0 to 999 (this number will change everytime you run
23 | the test).*/
24 |
25 | // DO NOT use Math.floor(Math.random() * 1000) in your code,
26 | // this test generates a random number, then passes it into your code with a function parameter.
27 | // 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
28 | const number = Math.floor(Math.random() * 1000);
29 | /*The .match(/((hey))/g).length is a regex that will count the number of heys
30 | in the result, which if your function works correctly will equal the number that
31 | was randomaly generated. */
32 | expect(repeatString('hey', number).match(/((hey))/g).length).toEqual(
33 | number
34 | );
35 | });
36 | test('works with blank strings', () => {
37 | expect(repeatString('', 10)).toEqual('');
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Because
4 |
5 |
6 |
7 | ## This PR
8 |
9 |
10 |
11 | ## Issue
12 |
19 | Closes #XXXXX
20 |
21 | ## Additional Information
22 |
23 |
24 |
25 | ## Pull Request Requirements
26 |
27 | - [ ] I have thoroughly read and understand [The Odin Project Contributing Guide](https://github.com/TheOdinProject/.github/blob/main/CONTRIBUTING.md)
28 | - [ ] The title of this PR follows the `location of change: brief description of change` format, e.g. `01_helloWorld: Update test cases`
29 | - [ ] The `Because` section summarizes the reason for this PR
30 | - [ ] The `This PR` section has a bullet point list describing the changes in this PR
31 | - [ ] If this PR addresses an open issue, it is linked in the `Issue` section
32 | - [ ] If this PR includes any changes that affect the solution of an exercise, I've also updated the solution in the `/solutions` folder
33 |
--------------------------------------------------------------------------------
/archive/archived_pigLatin/pigLatin.spec.js:
--------------------------------------------------------------------------------
1 | const pigLatin = require('./pigLatin')
2 |
3 | // Topics
4 |
5 | // * modules
6 | // * strings
7 |
8 | // Pig Latin
9 |
10 | // 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.
11 |
12 | // Rule 1: If a word begins with a vowel sound, add an "ay" sound to the end of the word.
13 |
14 | // 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.
15 |
16 | // (There are a few more rules for edge cases, and there are regional variants too, but that should be enough to understand the tests.)
17 |
18 | // See https://en.wikipedia.org/wiki/Pig_Latin for more details.
19 |
20 | describe('translate', () => {
21 | test('translates a word beginning with a vowel', () => {
22 | expect(pigLatin("apple")).toBe('appleay');
23 | });
24 |
25 | test.skip('translates a word beginning with a consonant', () => {
26 | expect(pigLatin("banana")).toBe("ananabay");
27 | });
28 |
29 | test.skip('translates a word beginning with two consonants', () => {
30 | expect(pigLatin("cherry")).toBe('errychay');
31 | });
32 |
33 | test.skip('translates two words', () => {
34 | expect(pigLatin("eat pie")).toBe('eatay iepay');
35 | });
36 |
37 | test.skip('translates a word beginning with three consonants', () => {
38 | expect(pigLatin("three")).toBe("eethray");
39 | });
40 |
41 | test.skip('counts "sch" as a single phoneme', () => {
42 | expect(pigLatin("school")).toBe("oolschay");
43 | });
44 |
45 | test.skip('counts "qu" as a single phoneme', () => {
46 | expect(pigLatin("quiet")).toBe("ietquay");
47 | });
48 |
49 | test.skip('counts "qu" as a consonant even when its preceded by a consonant', () => {
50 | expect(pigLatin("square")).toBe("aresquay");
51 | });
52 |
53 | test.skip('translates many words', () => {
54 | expect(pigLatin("the quick brown fox")).toBe("ethay ickquay ownbray oxfay");
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/archive/archived_pigLatin/solution/pigLatin-solution.spec.js:
--------------------------------------------------------------------------------
1 | const pigLatin = require('./pigLatin-solution');
2 |
3 | // Topics
4 |
5 | // * modules
6 | // * strings
7 |
8 | // Pig Latin
9 |
10 | // 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.
11 |
12 | // Rule 1: If a word begins with a vowel sound, add an "ay" sound to the end of the word.
13 |
14 | // 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.
15 |
16 | // (There are a few more rules for edge cases, and there are regional variants too, but that should be enough to understand the tests.)
17 |
18 | // See https://en.wikipedia.org/wiki/Pig_Latin for more details.
19 |
20 | describe('translate', () => {
21 | test('translates a word beginning with a vowel', () => {
22 | expect(pigLatin('apple')).toBe('appleay');
23 | });
24 |
25 | test('translates a word beginning with a consonant', () => {
26 | expect(pigLatin('banana')).toBe('ananabay');
27 | });
28 |
29 | test('translates a word beginning with two consonants', () => {
30 | expect(pigLatin('cherry')).toBe('errychay');
31 | });
32 |
33 | test('translates two words', () => {
34 | expect(pigLatin('eat pie')).toBe('eatay iepay');
35 | });
36 |
37 | test('translates a word beginning with three consonants', () => {
38 | expect(pigLatin('three')).toBe('eethray');
39 | });
40 |
41 | test('counts "sch" as a single phoneme', () => {
42 | expect(pigLatin('school')).toBe('oolschay');
43 | });
44 |
45 | test('counts "qu" as a single phoneme', () => {
46 | expect(pigLatin('quiet')).toBe('ietquay');
47 | });
48 |
49 | test('counts "qu" as a consonant even when its preceded by a consonant', () => {
50 | expect(pigLatin('square')).toBe('aresquay');
51 | });
52 |
53 | test('translates many words', () => {
54 | expect(pigLatin('the quick brown fox')).toBe('ethay ickquay ownbray oxfay');
55 | });
56 | });
57 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Create a report to help us improve something that is not working correctly
4 | title: "Bug - :"
5 | labels: "Status: Needs Review, Type: Bug"
6 | assignees: ""
7 | ---
8 |
9 |
10 |
11 | Complete the following REQUIRED checkboxes:
12 | - [ ] I have thoroughly read and understand [The Odin Project Contributing Guide](https://github.com/TheOdinProject/.github/blob/main/CONTRIBUTING.md)
13 | - [ ] The title of this issue follows the `Bug - location of bug: brief description of bug` format, e.g. `Bug - Exercises: File type incorrect for all test files`
14 |
15 | The following checkbox is OPTIONAL:
16 |
17 | - [ ] I would like to be assigned this issue to work on it
18 |
19 |
20 |
21 | **1. Description of the Bug:**
22 |
23 |
24 |
25 | **2. How To Reproduce:**
26 |
33 |
34 |
35 | **3. Expected Behavior:**
36 |
43 |
44 |
45 | **4. Desktop/Device:**
46 |
47 | - Device:
48 | - OS:
49 | - Browser:
50 | - Version:
51 |
52 | **5. Additional Information:**
53 |
54 |
55 |
--------------------------------------------------------------------------------
/08_calculator/solution/calculator-solution.spec.js:
--------------------------------------------------------------------------------
1 | const calculator = require('./calculator-solution');
2 |
3 | describe('add', () => {
4 | test('adds 0 and 0', () => {
5 | expect(calculator.add(0, 0)).toBe(0);
6 | });
7 |
8 | test('adds 2 and 2', () => {
9 | expect(calculator.add(2, 2)).toBe(4);
10 | });
11 |
12 | test('adds positive numbers', () => {
13 | expect(calculator.add(2, 6)).toBe(8);
14 | });
15 | });
16 |
17 | describe('subtract', () => {
18 | test('subtracts numbers', () => {
19 | expect(calculator.subtract(10, 4)).toBe(6);
20 | });
21 | });
22 |
23 | describe('sum', () => {
24 | test('computes the sum of an empty array', () => {
25 | expect(calculator.sum([])).toBe(0);
26 | });
27 |
28 | test('computes the sum of an array of one number', () => {
29 | expect(calculator.sum([7])).toBe(7);
30 | });
31 |
32 | test('computes the sum of an array of two numbers', () => {
33 | expect(calculator.sum([7, 11])).toBe(18);
34 | });
35 |
36 | test('computes the sum of an array of many numbers', () => {
37 | expect(calculator.sum([1, 3, 5, 7, 9])).toBe(25);
38 | });
39 | });
40 |
41 | describe('multiply', () => {
42 | test('multiplies two numbers', () => {
43 | expect(calculator.multiply([2, 4])).toBe(8);
44 | });
45 |
46 | test('multiplies several numbers', () => {
47 | expect(calculator.multiply([2, 4, 6, 8, 10, 12, 14])).toBe(645120);
48 | });
49 | });
50 |
51 | describe('power', () => {
52 | test('raises one number to the power of another number', () => {
53 | expect(calculator.power(4, 3)).toBe(64); // 4 to third power is 64
54 | });
55 | });
56 |
57 | describe('factorial', () => {
58 | test('computes the factorial of 0', () => {
59 | expect(calculator.factorial(0)).toBe(1); // 0! = 1
60 | });
61 |
62 | test('computes the factorial of 1', () => {
63 | expect(calculator.factorial(1)).toBe(1);
64 | });
65 |
66 | test('computes the factorial of 2', () => {
67 | expect(calculator.factorial(2)).toBe(2);
68 | });
69 |
70 | test('computes the factorial of 5', () => {
71 | expect(calculator.factorial(5)).toBe(120);
72 | });
73 |
74 | test('computes the factorial of 10', () => {
75 | expect(calculator.factorial(10)).toBe(3628800);
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/08_calculator/calculator.spec.js:
--------------------------------------------------------------------------------
1 | const calculator = require('./calculator');
2 |
3 | describe('add', () => {
4 | test('adds 0 and 0', () => {
5 | expect(calculator.add(0, 0)).toBe(0);
6 | });
7 |
8 | test.skip('adds 2 and 2', () => {
9 | expect(calculator.add(2, 2)).toBe(4);
10 | });
11 |
12 | test.skip('adds positive numbers', () => {
13 | expect(calculator.add(2, 6)).toBe(8);
14 | });
15 | });
16 |
17 | describe('subtract', () => {
18 | test.skip('subtracts numbers', () => {
19 | expect(calculator.subtract(10, 4)).toBe(6);
20 | });
21 | });
22 |
23 | describe('sum', () => {
24 | test.skip('computes the sum of an empty array', () => {
25 | expect(calculator.sum([])).toBe(0);
26 | });
27 |
28 | test.skip('computes the sum of an array of one number', () => {
29 | expect(calculator.sum([7])).toBe(7);
30 | });
31 |
32 | test.skip('computes the sum of an array of two numbers', () => {
33 | expect(calculator.sum([7, 11])).toBe(18);
34 | });
35 |
36 | test.skip('computes the sum of an array of many numbers', () => {
37 | expect(calculator.sum([1, 3, 5, 7, 9])).toBe(25);
38 | });
39 | });
40 |
41 | describe('multiply', () => {
42 | test.skip('multiplies two numbers', () => {
43 | expect(calculator.multiply([2, 4])).toBe(8);
44 | });
45 |
46 | test.skip('multiplies several numbers', () => {
47 | expect(calculator.multiply([2, 4, 6, 8, 10, 12, 14])).toBe(645120);
48 | });
49 | });
50 |
51 | describe('power', () => {
52 | test.skip('raises one number to the power of another number', () => {
53 | expect(calculator.power(4, 3)).toBe(64); // 4 to third power is 64
54 | });
55 | });
56 |
57 | describe('factorial', () => {
58 | test.skip('computes the factorial of 0', () => {
59 | expect(calculator.factorial(0)).toBe(1); // 0! = 1
60 | });
61 |
62 | test.skip('computes the factorial of 1', () => {
63 | expect(calculator.factorial(1)).toBe(1);
64 | });
65 |
66 | test.skip('computes the factorial of 2', () => {
67 | expect(calculator.factorial(2)).toBe(2);
68 | });
69 |
70 | test.skip('computes the factorial of 5', () => {
71 | expect(calculator.factorial(5)).toBe(120);
72 | });
73 |
74 | test.skip('computes the factorial of 10', () => {
75 | expect(calculator.factorial(10)).toBe(3628800);
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/01_helloWorld/README.md:
--------------------------------------------------------------------------------
1 | # Exercise 01 - Hello World
2 |
3 | The main purpose of this exercise is to walk you through the process of running the tests and make sure everything is set up and running correctly.
4 |
5 | In this directory you will find 2 other files:
6 | 1. `helloWorld.js`
7 | 2. `helloWorld.spec.js`
8 |
9 | This setup should be the same for all of the exercises. The plain javascript file is where you'll write your code, and the `spec` file contains the tests that verify your code is functional.
10 |
11 | Let's look at the spec file first:
12 | ```javascript
13 | const helloWorld = require('./helloWorld');
14 |
15 | describe('Hello World', function() {
16 | test('says "Hello, World!"', function() {
17 | expect(helloWorld()).toEqual('Hello, World!');
18 | });
19 | });
20 | ```
21 | At the very top of the file we use `require()` to import the code from the javascript file (`helloWorld.js`) so that we can test it.
22 |
23 | The next block (`describe()`) is the body of the test. Basically, all it's doing is running your code and testing to see if the output is correct. The `test()` function describes what should be happening in plain english and then includes the `expect()` function. For this simple example it should be pretty simple to read.
24 |
25 | For now you do not need to worry about how to write tests, but you should try to get comfortable enough with the syntax to figure out what the tests are asking you to do. Go ahead and run the tests by entering `npm test helloWorld.spec.js` in the terminal and watch it fail. The output from that command should tell you exactly what went wrong with your code. In this case, running the `helloWorld()` function should return the phrase 'Hello, World!' but instead it returns an empty string...
26 |
27 | so let's look at the javascript file:
28 | ```javascript
29 | const helloWorld = function() {
30 | return ''
31 | }
32 |
33 | module.exports = helloWorld
34 | ```
35 | In this file we have a simple function called helloWorld that returns an empty string... which is exactly what our test was complaining about. The `module.exports` on the last line is how we export the function so that it can be imported with `require()` in the spec file.
36 |
37 | Go ahead and see if you can make the test pass by editing the return value of the function, and then running the test file again.
38 |
39 | Just to make sure, in case you're confused at this point, the test is telling you that running the function `helloWorld` should return the phrase `Hello, World!`. Punctuation and capitalization definitely matter here, so double check that if the test still isn't passing.
40 |
41 | This is what the final function should look like:
42 | ```javascript
43 | const helloWorld = function() {
44 | return 'Hello, World!'
45 | }
46 |
47 | module.exports = helloWorld
48 | ```
49 |
50 | For the most part we've set up these tests in such a way that you only have to write the code being tested. You should not have to worry about importing or exporting anything at this stage.. so just work around that bit of the code and write what it takes to make them pass!
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript Exercises
2 |
3 | These JavaScript exercises are intended to complement the JavaScript content on The Odin Project (TOP). They should only be done when instructed during the course of the curriculum.
4 |
5 | **Note:** The `generator-exercise` file is not actually an exercise; it is a script that generates exercises. It was created to help efficiently write these exercises.
6 |
7 | ## Contributing
8 |
9 | If you have a suggestion to improve an exercise, an idea for a new exercise, or notice an issue with an exercise, please feel free to open an issue after thoroughly reading our [contributing guide](https://github.com/TheOdinProject/.github/blob/main/CONTRIBUTING.md).
10 |
11 | ## How To Use These Exercises
12 |
13 |
14 | 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).
15 | - 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).
16 | 2. Before you start working on any exercises, you should first ensure you have the following installed:
17 | - **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.
18 | - **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. (Note: if you get warnings that packages are out of date or contain vulnerabilities, you can safely ignore them for these exercises.)
19 |
20 | 3. Each exercise includes the following:
21 |
22 | - A markdown file with a description of the task, an empty (or mostly empty) JavaScript file, and a set of tests.
23 | - A `solutions` directory that contains a solution and the same test file with all of the tests unskipped.
24 |
25 | 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.
26 | 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.
27 | 5. Once you successfully finish an exercise, check the `solutions` directory within each exercise to compare it with yours.
28 | - You should not be checking the solution for an exercise until you finish it!
29 | - 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.
30 | 6. Do not submit your solutions to this repo, as any PRs that do so will be closed without merging.
31 |
32 | **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.
33 |
34 | The first exercise, `helloWorld`, will walk you through the process in-depth.
35 |
36 | ## Debugging
37 |
38 | To debug functions, you can run the tests in the Visual Studio Code debugger terminal. You can open this by clicking the "Run and Debug" icon on the left or pressing Ctrl + Shift + D, then clicking JavaScript Debug Terminal. You will be able to set breakpoints as you would in the Chrome DevTools debugger. You can run `npm test exerciseName.spec.js` to then execute your code up until your breakpoint and step through your code as necessary. **NOTE**: To take advantage of the debugger, you **MUST** run the script in the debugger terminal, not the bash or zsh terminal.
39 |
--------------------------------------------------------------------------------