FreeCodeCamp. JavaScript Algorithms and Data Structure.

JavaScript Algorithms and Data Structures is the second of six courses offered at FreeCodeCamp of which upon completion of its final projects you may earn a FreeCodeCamp Certificate.

Starting with the basic sintax of javascript, this course is a great introduction to JavaScript. I do recommend to find tutorials or books online, and read them while you're going through this course, it helped me have a better grasp on the several aspects of JavaScript.

By the end of the course you will find several exercises to put into practice everything you have learn so far, and 5 final projects to obtain the FreeCodeCamp Certificate.

I decided to create this page as a way to showcase my code answers to this project, if you are currently going through this part of the FCC curriculum I recommend that you don't see my answers and do the projects on your own, the most important part of doing the FCC is to learn of your mistakes and find the answers by yourself, the process of making mistakes and learning from them is the most important part of learning, not only how to code but learning anything in life, so if you truly want to learn, don't cheat, you will only be doing a disservice to yourself.

FreeCodeCamp JavaScript's final project

Palindrome Checker

A palindrome is a word or sentence that's spelled the same way both forward and backward, ignoring punctuation, case, and spacing.

In this project, you are tasked with creating and algorithm that can detect if a word (or phrase) is a palindrome or not.

Because we need to compare character by character, not including punctuation, case and spacing, we need to use regular expressions to match all the correct characters, and add all of this into an array for easy comparison later.

After having your array with all the correct characters, the rest is just an iteration where you compare each character in their respective position at the beginning and end of the array, stoping at the middle.

// FreeCodeCamp.
// Javascript algorithms and Data Structures
// Palindrome Checker
function isPalindrome(string) {
  // Evaluate if input string is a palindrome or not
  // returns either true or false

  let regex = /[^\W_]/g;

  if (string.match(regex) !== null) {
    // If the result of evaluating the regular expression into the string
    // is not null
    let charArr = string.match(regex).map(each => each.toLowerCase());
    for (let i = 0; i < Math.floor(charArr.length / 2); i++) {
      if (charArr[i] !== charArr[charArr.length - 1 - i]) {
        return false;
      }
    }
    return true;
  }
  // if the input is not a valid string return false
  return false;
}

Testing the code.

Heres a list of words and phrases you can use to test the algorithm.

  • "eye" should return positive.
  • "_eye" should return positive.
  • "race car" should return positive.
  • "not a palindrome" should return negative.
  • "A man, a plan, a canal. Panama" should return positive.
  • "never odd or even" should return positive.
  • "nope" should return negative.
  • "almostomla" should return negative.
  • "My age is 0, 0 si ega ym." should return positive.
  • "1 eye for of 1 eye." should return negative.
  • "0_0 (: /-\ :) 0-0" should return positive.
  • "five|\_/|four" should return negative.
Palindrome Test:

Roman Numeral Converter

For this project you are asked to create an algorithm that converts decimal numbers into the roman numeral system.

The first thing that we need to do, is understand how roman numerals are written. The numbers in this system are represented by a combination of letters from the Latin alphabet.

SymbolIVXLCDM
Value1510501005001000

Now that we now the symbols and their value in the decimal system, how do we used them?. The correct way to express quantities in the roman numeral system is to replace from the top down, find the highest valued symbol, use it and substract the value to the total and repeat until zero is reached.

For example, the correct way to express 1500 in the roman system would be MD, instead of DDD.

// FreeCodeCamp.
// JavaScript Algorithms and Data Structures Projects:
// Roman Numeral Converter

function convertToRoman(input) {
  // Converts decimal integers to roman numeral

  let arrayOfRelation = [
    [900, 'CM'],
    [500, 'D'],
    [400, 'CD'],
    [100, 'C'],
    [90, 'XC'],
    [50, 'L'],
    [40, 'XL'],
    [10, 'X'],
    [9, 'IX'],
    [5, 'V'],
    [4, 'IV'],
    [1, 'I'],
  ];
  let num = input;
  let result = [];

  if (input > 1000) {
    let overThousand = Math.floor(input / 1000);
    num = num - overThousand * 1000;
    result.push('M'.repeat(overThousand));
  }

  for (let valuePair of arrayOfRelation) {
    while (num >= valuePair[0]) {
      num -= valuePair[0];
      result.push(valuePair[1]);
    }
  }

  if (result.length > 0) {
    return result.reduce((total, value) => total + value);
  } else {
    return '';
  }
}

Testing the code.

Use the following values to test the algorithm.

  • Decimal 2, should return "II".
  • Decimal 4, should return "IV".
  • Decimal 29, should return "XXIX".
  • Decimal 83, should return "LXXXIII".
  • Decimal 97, should return "XCVII".
  • Decimal 649, should return "DCXLIX".
  • Decimal 798, should return "DCCXCVIII".
  • Decimal 891, should return "DCCCXCI".
  • Decimal 2014, should return "MMXIV".
  • Decimal 3999, should return "MMMCMXCIX".
Decimal:
Roman:

Caesars Cipher

In cryptography, a cipher is an algorithm for performing encryption or decryption, this algorithm substitutes the characters in a string to transform them into something else for the purpose of protecting the original string from being understood by unwanted people.

Caesars cipher is one of the simplest and most widely know encryption techniques. It is a simple substitution cipher in which each letter in a string is replaced by a letter some fixed number of positions down the alphabet.

For this project you are asked to write an algorithm capable of implementing ROT13, a type of caesar cipher that replaces every character in a string with the letter 13 positions up in the alphabet from the position of that character.

// FreeCodeCamp.
// javaScript Algorithms and Data Structures Projects:
// Caesars Cipher
function rot13(str) {
  // LBH QVQ VG!
  let abc = 'abcdefghijklmnopqrstuvwxyz'.toUpperCase();
  let result = [];
  let regex = /[a-zA-Z]/;

  for (let each of str) {
    let newLetter = each;
    if (regex.test(each)) {
      let pos = abc.indexOf(each);
      let newpos = pos + 13 >= 26 ? pos - 13 : pos + 13;
      newLetter = abc.charAt(newpos);
    }
    result.push(newLetter);
  }

  return result.reduce((total, value) => {
    return total + value;
  });
}

Testing the code.

Use the following to test the algorithm

  • "SERR PBQR PNZC" should decode to FREE CODE CAMP.
  • "SERR CVMMN!" should decode to FREE PIZZA!.
  • "SERR YBIR?" should decode to FREE LOVE?.
  • "GUR DHVPX OEBJA SBK WHZCF BIRE GUR YNML QBT." should decode to THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.
Encrypted:
Decrypted:

Telephone Number Validator

For this next project you are given a string representing a phone number and you should validate it.

The input string should be a valid US phone number, the following examples are valid formats for US numbers.

  • 555-555-5555
  • (555)555-5555
  • (555) 555-5555
  • 555 555 5555
  • 5555555555
  • 1 555 555 5555
// FreeCodeCamp.
// JavaScript Algorithms and Data Structures Projects:
// Telephone Number Validator
function telephoneCheck(str) {
  let regex = /^1{0,1}\s{0,1}(?:[0-9]{3}[\s\-]{0,1}){2}[0-9]{4}$|^1{0,1}\s{0,1}\([0-9]{3}\)[\s\-]{0,1}[[0-9]{3}[\s\-]{0,1}[0-9]{4}$/;
  return regex.test(str);
}

Testing the code.

Use the following to test the algorithm

  • "1 555-555-5555" should return true.
  • "1 (555) 555-5555" should return true.
  • "5555555555" should return true.
  • "555-555-5555" should return true.
  • "(555)555-5555" should return true.
  • "1(555)555-5555" should return true.
  • "555-5555" should return false.
  • "5555555" should return false.
  • "1 555)555-5555" should return false.
  • "1 555 555 5555" should return true.
  • "1 456 789 4444" should return true.
  • "123**&!!asdf#" should return false.
  • "55555555" should return false.
  • "(6054756961)" should return false
  • "2 (757) 622-7382" should return false.
  • "0 (757) 622-7382" should return false.
  • "-1 (757) 622-7382" should return false
  • "2 757 622-7382" should return false.
  • "10 (757) 622-7382" should return false.
  • "27576227382" should return false.
  • "(275)76227382" should return false.
  • "2(757)6227382" should return false.
  • "2(757)622-7382" should return false.
  • "555)-555-5555" should return false.
  • "(555-555-5555" should return false.
  • "(555)5(55?)-5555" should return false.
US Phone Validator:

Cash Register

For this last project you're tasked with creating a piece of program that will simulate a cash register.

You have to write a function that takes purchase price as the first argument (price), payment as the second argument (cash), and cash-in-drawer (cid) as the third argument.

After taking those inputs, you need to calculate how much change to return taking into account the price of the product, the amount of cash the client is giving you and the amount of cash the register has.

You should always return an object with a "status" and a "change" key. The posible outcomes are as follow:

  • If cash in the drawer is less than the change due, or if you cannot return the exact change, the output should be {status: "INSUFFICIENT_FUNDS", change: []}
  • If the amount of "change" to return is equal to the amount of cash in the register, the "status" key should return "CLOSED" and the change array sorted from highest to lowest with the amounts of bills and coins to return.
  • If the change to return is lower than the amount of cash in the register, and the exact amount can be returned, the "status" key should return with a value of "OPEN", and as in the last case, the change array sorted from highest to lowest.
// FreeCodeCamp.
// JavaScript Algorithms and Data Structures Projects:
// Cash Register

let testResults = [
  [
    [
      19.5,
      20,
      [
        ['PENNY', 1.01],
        ['NICKEL', 2.05],
        ['DIME', 3.1],
        ['QUARTER', 4.25],
        ['ONE', 90],
        ['FIVE', 55],
        ['TEN', 20],
        ['TWENTY', 60],
        ['ONE HUNDRED', 100],
      ],
    ],
    {},
  ],
  [
    [
      19.5,
      20,
      [
        ['PENNY', 1.01],
        ['NICKEL', 2.05],
        ['DIME', 3.1],
        ['QUARTER', 4.25],
        ['ONE', 90],
        ['FIVE', 55],
        ['TEN', 20],
        ['TWENTY', 60],
        ['ONE HUNDRED', 100],
      ],
    ],
    {status: 'OPEN', change: [['QUARTER', 0.5]]},
  ],
  [
    [
      3.26,
      100,
      [
        ['PENNY', 1.01],
        ['NICKEL', 2.05],
        ['DIME', 3.1],
        ['QUARTER', 4.25],
        ['ONE', 90],
        ['FIVE', 55],
        ['TEN', 20],
        ['TWENTY', 60],
        ['ONE HUNDRED', 100],
      ],
    ],
    {
      status: 'OPEN',
      change: [
        ['TWENTY', 60],
        ['TEN', 20],
        ['FIVE', 15],
        ['ONE', 1],
        ['QUARTER', 0.5],
        ['DIME', 0.2],
        ['PENNY', 0.04],
      ],
    },
  ],
  [
    [
      19.5,
      20,
      [
        ['PENNY', 0.01],
        ['NICKEL', 0],
        ['DIME', 0],
        ['QUARTER', 0],
        ['ONE', 0],
        ['FIVE', 0],
        ['TEN', 0],
        ['TWENTY', 0],
        ['ONE HUNDRED', 0],
      ],
    ],
    [{status: 'INSUFFICIENT_FUNDS', change: []}],
  ],
  [
    [
      19.5,
      20,
      [
        ['PENNY', 0.01],
        ['NICKEL', 0],
        ['DIME', 0],
        ['QUARTER', 0],
        ['ONE', 1],
        ['FIVE', 0],
        ['TEN', 0],
        ['TWENTY', 0],
        ['ONE HUNDRED', 0],
      ],
    ],
    [{status: 'INSUFFICIENT_FUNDS', change: []}],
  ],
  [
    [
      19.5,
      20,
      [
        ['PENNY', 0.5],
        ['NICKEL', 0],
        ['DIME', 0],
        ['QUARTER', 0],
        ['ONE', 0],
        ['FIVE', 0],
        ['TEN', 0],
        ['TWENTY', 0],
        ['ONE HUNDRED', 0],
      ],
    ],
    [
      {
        status: 'CLOSED',
        change: [
          ['PENNY', 0.5],
          ['NICKEL', 0],
          ['DIME', 0],
          ['QUARTER', 0],
          ['ONE', 0],
          ['FIVE', 0],
          ['TEN', 0],
          ['TWENTY', 0],
          ['ONE HUNDRED', 0],
        ],
      },
    ],
  ],
];

function dictFrom2dArr(arr) {
  let dict = {};

  for (let each of arr) {
    dict[each[0]] = each[1];
  }
  return dict;
}

function checkCashRegister(price, cash, cid) {
  function makeTransaction(num, amount) {
    change = Math.round((change - num) * 100) / 100;
    cidDict[amount] = Math.round((cidDict[amount] - num) * 100) / 100;
    changeDict[amount] = Math.round((changeDict[amount] + num) * 100) / 100;
  }

  let changeDict = {
    PENNY: 0,
    NICKEL: 0,
    DIME: 0,
    QUARTER: 0,
    ONE: 0,
    FIVE: 0,
    TEN: 0,
    TWENTY: 0,
    'ONE HUNDRED': 0,
  };
  let cidDict = dictFrom2dArr(cid);
  let changeArr = [];
  let registerStatus;
  var change = cash - price;
  let totalInRegister = 0;
  let foo = cid.forEach((money, index) => {
    totalInRegister += money[1];
  });
  totalInRegister = Math.round(totalInRegister * 100) / 100;

  let exitFlag = 0;
  if (change > totalInRegister) {
    return {status: 'INSUFFICIENT_FUNDS', change: changeArr};
  } else {
    while (change > 0) {
      change = Math.round(change * 100) / 100;
      exitFlag += 1;
      if (exitFlag > 100) {
        break;
      }
      if (change >= 100 && cidDict['ONE HUNDRED'] >= 100) {
        makeTransaction(100, 'ONE HUNDRED');
      } else if (change >= 20 && cidDict['TWENTY'] >= 20) {
        makeTransaction(20, 'TWENTY');
      } else if (change >= 10 && cidDict['TEN'] >= 10) {
        makeTransaction(10, 'TEN');
      } else if (change >= 5 && cidDict['FIVE'] >= 5) {
        makeTransaction(5, 'FIVE');
      } else if (change >= 1 && cidDict['ONE'] >= 1) {
        makeTransaction(1, 'ONE');
      } else if (change >= 0.25 && cidDict['QUARTER'] >= 0.25) {
        makeTransaction(0.25, 'QUARTER');
      } else if (change >= 0.1 && cidDict['DIME'] >= 0.1) {
        makeTransaction(0.1, 'DIME');
      } else if (change >= 0.05 && cidDict['NICKEL'] >= 0.05) {
        makeTransaction(0.05, 'NICKEL');
      } else if (change >= 0.01 && cidDict['PENNY'] >= 0.01) {
        makeTransaction(0.01, 'PENNY');
      } else {
        return {status: 'INSUFFICIENT_FUNDS', change: changeArr};
      }
    }

    // Formatting change array to be returned
    let order = [
      'PENNY',
      'NICKEL',
      'DIME',
      'QUARTER',
      'ONE',
      'FIVE',
      'TEN',
      'TWENTY',
      'ONE HUNDRED',
    ].reverse();
    for (let value of order) {
      if (changeDict[value] != 0) {
        let roundedCash = Math.round(changeDict[value] * 100) / 100;
        changeArr.push([value, roundedCash]);
      }
    }
    for (let cashInRegister in cidDict) {
      if (cidDict[cashInRegister] > 0) {
        // Return with open status
        return {
          status: 'OPEN',
          change: changeArr,
        };
      }
    }
    // Return with closed status
    // TODO: this return is hardcoded to return
    // the change array values even if they are 0
    // if not the last test of the exercise will not
    // pass.
    let tempReturnArr = [];
    for (let value of order) {
      if (changeDict[value] != 0) {
        let roundedCash = Math.round(changeDict[value] * 100) / 100;
        tempReturnArr.push([value, roundedCash]);
      } else {
        tempReturnArr.push([value, 0]);
      }
    }
    return {
      status: 'CLOSED',
      change: tempReturnArr.reverse(),
    };
  }
  // Here is your change, ma'am.
}

// Example cash-in-drawer array:
// [["PENNY", 1.01],
// ["NICKEL", 2.05],
// ["DIME", 3.1],
// ["QUARTER", 4.25],
// ["ONE", 90],
// ["FIVE", 55],
// ["TEN", 20],
// ["TWENTY", 60],
// ["ONE HUNDRED", 100]]

for (let test of testResults) {
  let foo = checkCashRegister(test[0][0], test[0][1], test[0][2]);
  console.log(foo);
  console.log(test[1]);
  console.log('*****');
}

Testing the code.

Here's a small app for testing the code, you just need to enter a price and payment and press the 'Calculate' button, there's already a predefined register state which you can modify if you want to.

Input:
Price:
Payment:

Cash in register: $682.51

Amount in

$100 bills

$:

Amount in

$50 bills

$:

Amount in

$20 bills

$:

Amount in

$10 bills

$:

Amount in

$5 bills

$:

Amount in

$1 bills

$:

Amount in

¢25 coins

$:

Amount in

¢10 coins

$:

Amount in

¢5 coins

$:

Amount in

¢1 coins

$:
Result:

Client change: $0

Amount in

$100 bills

$:

Amount in

$50 bills

$:

Amount in

$20 bills

$:

Amount in

$10 bills

$:

Amount in

$5 bills

$:

Amount in

$1 bills

$:

Amount in

¢25 coins

$:

Amount in

¢10 coins

$:

Amount in

¢5 coins

$:

Amount in

¢1 coins

$:

New cash in Register: $0

Amount in

$100 bills

$:

Amount in

$50 bills

$:

Amount in

$20 bills

$:

Amount in

$10 bills

$:

Amount in

$5 bills

$:

Amount in

$1 bills

$:

Amount in

¢25 coins

$:

Amount in

¢10 coins

$:

Amount in

¢5 coins

$:

Amount in

¢1 coins

$: