Introduction
On this post, I'll explain and help you understand in detail how to solve every FreeCodeCamp JavaScript Algorithms and Data Structures Project in a simple way. I have found that many of the code explanations I came across are somewhat weird or complicated, especially for problems 2
and 5
.
Table Of Contents
Problem N°1 : Palindrome Checker
DESCRIPTION
Return true
if the given string is a palindrome. Otherwise, return false
.
A palindrome is a word or sentence that's spelled the same way both forward and backward, ignoring punctuation, case, and spacing.
Note: You'll need to remove all nonalphanumeric characters (punctuation, spaces and symbols) and turn everything into the same case (lower or upper case) in order to check for palindromes.
We'll pass strings with varying formats, such as racecar
, RaceCar
, and race CAR
among others.
We'll also pass strings with special symbols, such as 2A3*3a2
, 2A3 3a2
, and 2_A3*3#A2
.
Solution ✅
function palindrome(str) {
const filteredStr = str.toLowerCase().replace(/[^az09]/g, "");
const reversedStr = filteredStr.split("").reverse().join("");
return filteredStr === reversedStr;
}
palindrome("eye");
Explanation ℹ
This problem is direct and simple :
 The first thing to do is to convert our string to
lower case
usingtoLowerCase()
method.
 We need to because
"eyE" !== "Eye"
console.log("TesT".toLowerCase());
//output : "test"
 After that we filter any nonalphanumeric characters with the help of
regEx
andreplace()
method.
console.log("_test55^".replace(/[^az09]/g, ""));
//output : "test55"
 Now we need to reverse our string so we can compare it to the original :

split()
our string so we have an array of strings. 
reverse()
the array. 
join()
to return a string from the reversed array.
let str = "happy";
console.log(str.split(""));
//output : [ 'h', 'a', 'p', 'p', 'y' ]
console.log(str.split("").reverse());
//output : [ 'y', 'p', 'p', 'a', 'h' ]
console.log(str.split("").reverse().join(""));
//output : 'yppah'
 The last step is to compare the original string to the reversed one, if returned
true
, the latter is a palindrome
return str === strReversed;
Problem N°2 : Roman Numeral Converter
Solution ✅
const table = {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1,
};
function convertToRoman(num) {
let roman = "";
for (const key in table) {
const value = table[key]; // ex 1000
while (num >= value) {
num = value;
roman += key;
}
}
return roman;
}
convertToRoman(1234);
Explanation ℹ
This problem is very simple if you just know the rules
to follow.
To convert a number to its Roman equivalent, you need to do some basic maths with the help of the table above.
Let's imagine we have number = 999
 From the table (cf. Problem description), we search for the number that's (
≥
) greater than or equal to999
. This number would be
900
.
 This number would be
 We subtract
999
from900
and save the Roman letter. On this case, we save :
CM
 On this case, we save :
 We repeat the same procedure but with the result of the subtraction.
 The result is :
99
 The result is :
999 >= 900, so :
we sub : 999  900, save CM,

99 >= 90, so :
we sub : 99  90, save XC,

9 >= 9, so
we sub : 9  9, save IX

Result : CMXCIX
Full Examples
✳ Now, let's write some JavaScript.
 We need to create an
object
starting from the table above.
const table = {
M: 1000,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1,
};
 After that we need an empty
string
so we can save ourRoman
letters to it.
let roman = "";
 And finally we loop thought the
object
with the help offor in
and :
 While
number >= (object value)
we subtractnumber  (object value)
, and save theobject key
to the empty string we just created.
for (const key in table) { // We loop
const value = table[key]; // (Object value), ex : 1000
while (num >= value) { // number >= (Object value)
num = value; // number  (Object value)
roman += key; // saving the (Object key), ex : IX
 ⚠ Note :
object = {
name: "Doe", // name is the Object `key` , "Doe" is the Object `value`
};
Pretty simple huh ?
Problem N°3 : Caesars Cipher
DESCRIPTION
One of the simplest and most widely known ciphers is a Caesar cipher, also known as a shift cipher. In a shift cipher the meanings of the letters are shifted by some set amount.
A common modern use is the ROT13
cipher, where the values of the letters are shifted by 13 places. Thus A ↔ N
, B ↔ O
and so on.
Write a function which takes a ROT13
encoded string as input and returns a decoded string.
All letters will be uppercase. Do not transform any nonalphabetic character (i.e. spaces, punctuation), but do pass them on.
Solution ✅
function rot13(str) {
const max = 90; // decimal value of Z
return str
.split("")
.map((el) => {
if (el.match(/[AZ]/g)) {
let charCode = el.charCodeAt() + 13;
return charCode > max
? String.fromCharCode((charCode = 26))
: String.fromCharCode(charCode);
} else {
return el;
}
})
.join("");
}
rot13("SERR PBQR PNZC");
Explanation ℹ
To decode ROT13
encoded strings, we need to move every letter by 13 places.
ROT13(a) = n
// 1 + 13 = 14, from the table, 14 is n
ROT13(c) = p
// 3 + 13 = 16, from the table, 16 is p
❓ What if the letter is for example w
?
23 + 13 = 36 // no letter is 36th of the alphabet
 We exceeded the number of the English alphabet letters, the maximum is
26
.
ℹ To solve this, every time the total is greater (>
) than 26
, we need to subtract 26
so we start counting from the start again.
ROT13(w) = j
//23 + 13 = 36 ➡ (36>26)
//36  26 = 10, from the table, 10 is j
Thats it.
✳ How can we do that with JavaScript
?
 In
JavaScript
we have amethod
that returns an integer representing the UTF16 code unit of a string.
console.log("a".charCodeAt()); // 97
console.log("A".charCodeAt()); // 65
console.log("z".charCodeAt()); // 122
console.log("Z".charCodeAt()); // 90
 Unlike the English alphabet,
A
is number65
, andZ
is number90
.
⚠ Note : All letters will be uppercase [Cf. Problem description].
 So, like we explained above, to decode
ROT13
encoded strings, we need to move every letter by13
places, but this time we won't follow the alphabet table, but this table :
Like mentioned above, A
is 65
, and Z
is 90
.
 Now, because we are using the
charCodeAt()
method, we need to convert back the result to a string usingString.fromCharCode()
method.
let charCode = "A".charCodeAt() + 13;
console.log(charCode);
// output : 78
let str = String.fromCharCode(charCode);
console.log(str);
// output : N
❓ What if the letter is for example W
?
ℹ Like before, if the number after adding 13
is greater (>
) than the decimal value of Z
(in this case 90
), we subtract 26
(the total number the English Alphabet) so we start counting from the start again.
let charCode = "W".charCodeAt() + 13;
console.log(charCode);
// output : 100 > 90, so we subtract 26 :
charCode = 26;
let str = String.fromCharCode(charCode);
console.log(str);
// output : J
✳ Now, lemme explain my code a lil bit.
First, we
split()
our string so we have an array of strings, after that wemap()
this array.We convert each element of this array to its decimal equivalent using
charCodeAt()
and we add13
to it if the latter is alphabetic, if not, wereturn
it without touching it [Cf. problem description].If after adding
13
the result exceeds the decimal value ofZ
, we subtract26
.We convert back to a string with the help of
String.fromCharCode()
method.
Problem N°4 : Telephone Number Validator
DESCRIPTION
Return true
if the passed string looks like a valid US phone number.
The user may fill out the form field any way they choose as long as it has the format of a valid US number. The following are examples of valid formats for US numbers (refer to the tests below for other variants):
5555555555
(555)5555555
(555) 5555555
555 555 5555
5555555555
1 555 555 5555
For this challenge you will be presented with a string such as 8006927753
or 8oosix427676;laskdjf
. Your job is to validate or reject the US phone number based on any combination of the formats provided above. The area code is required. If the country code is provided, you must confirm that the country code is 1
. Return true
if the string is a valid US phone number; otherwise return false
.
Solution ✅
function telephoneCheck(str) {
const reg = /^(1\s?)?(\d{3}\(\d{3}\))[\s\]?\d{3}[\s\]?\d{4}$/gm;
return reg.test(str);
}
telephoneCheck("1 5555555555");
Explanation ℹ
Explaining Regular expressions
is another topic beyond this post.
Problem N°5 : Cash Register
DESCRIPTION
Design a cash register drawer function checkCashRegister()
that accepts purchase price as the first argument (price
), payment as the second argument (cash
), and cashindrawer (cid
) as the third argument.
cid
is a 2D array listing available currency.
The checkCashRegister()
function should always return an object with a status
key and a change
key.
Return{status: "INSUFFICIENT_FUNDS", change: []}
if cashindrawer is less than the change due, or if you cannot return the exact change.
Return {status: "CLOSED", change: [...]}
with cashindrawer as the value for the key change
if it is equal to the change due.
Otherwise, return {status: "OPEN", change: [...]}
, with the change due in coins and bills, sorted in highest to lowest order, as the value of the change
key.
Currency Unit  Amount 

Penny  $0.01 (PENNY) 
Nickel  $0.05 (NICKEL) 
Dime  $0.1 (DIME) 
Quarter  $0.25 (QUARTER) 
Dollar  $1 (ONE) 
Five Dollars  $5 (FIVE) 
Ten Dollars  $10 (TEN) 
Twenty Dollars  $20 (TWENTY) 
Onehundred Dollars  $100 (ONE HUNDRED) 
See below for an example of a cashindrawer array:
[
["PENNY", 1.01],
["NICKEL", 2.05],
["DIME", 3.1],
["QUARTER", 4.25],
["ONE", 90],
["FIVE", 55],
["TEN", 20],
["TWENTY", 60],
["ONE HUNDRED", 100],
];
Solution ✅
const rate = {
PENNY: 0.01,
NICKEL: 0.05,
DIME: 0.1,
QUARTER: 0.25,
ONE: 1,
FIVE: 5,
TEN: 10,
TWENTY: 20,
"ONE HUNDRED": 100,
};
function checkCashRegister(price, cash, cid) {
const totalCid = Number(cid.reduce((sum, el) => sum + el[1], 0).toFixed(2));
let change = Number((cash  price).toFixed(2));
if (totalCid === change) {
return { status: "CLOSED", change: cid };
} else if (totalCid < change) {
return { status: "INSUFFICIENT_FUNDS", change: [] };
} else {
let changeArray = [];
cid.reverse().forEach((el) => {
const coinName = el[0];
const coinTotalValueInDollars = Number(el[1]);
const selectedCurrency = Number(rate[coinName]);
let coinsAvailable = Number(
(coinTotalValueInDollars / selectedCurrency).toFixed(2)
);
let coinsToReturn = 0;
while (change >= selectedCurrency && coinsAvailable > 0) {
change = Number((change  selectedCurrency).toFixed(2));
coinsAvailable;
++coinsToReturn;
}
if (coinsToReturn > 0) {
changeArray.push([coinName, coinsToReturn * selectedCurrency]);
}
});
if (change === 0) {
return { status: "OPEN", change: changeArray };
} else {
return { status: "INSUFFICIENT_FUNDS", change: [] };
}
}
}
checkCashRegister(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],
]);
Explanation ℹ
Ahh! The famous Cash Register
problem. It's actually quite easy to understand once you grasp it correctly. Trust me, once you get it, you'll see how straightforward it is.
✳ First of all, I need to explain to you the cashindrawer array
[
["PENNY", 1.01],
["NICKEL", 2.05],
["DIME", 3.1],
["QUARTER", 4.25],
["ONE", 90],
["FIVE", 55],
["TEN", 20],
["TWENTY", 60],
["ONE HUNDRED", 100],
];
Take a look at the
PENNY
element.1.01
is the total value in dollars worth ofPENNIES
, in other words, the drawer have$1.01
worth of pennies, this means we have101 PENNY
, because everyPENNY
is worth$0.01
We also have
$2.05
worth ofNICKELS
, this means the drawer have41 NICKEL
, because everyNICKEL
is worth$0.05
, and so on...
✳ That in mind, let's have a real example first so we can move forward.
Imagine you buy something that costs $5
, and you give the seller $20
. The change would be $15
.
Technically, the seller could return the change amount in dimes
, nickels
, or pennies
if they have enough, but ideally they would return one $10 bill and one $5 bill (from the highest value bill to the lowest).
✳ Now, let's write some JavaScript.
 We need first to calculate the change.
 The change is the amount of
cash
the buyer gave minus theprice
of the purchased item.
let change = Number((cash  price).toFixed(2));
// we limited the decimals to two, using toFixed() method, it will also round the number
// ex `5.1799.toFixed(2)` will output : 5.18
// and converted the result to a number using `Number()` method
 We need to calculate the total value of the money we have the drawer in dollars.
const totalCid = Number(cid.reduce((sum, el) => sum + el[1], 0).toFixed(2));
// cid is the `cashindrawer array`
// to understand the `reduce()` method, watch : https://youtu.be/g1C40tDP0Bk
 We need to know the value or the equivalent of each bill/coin in dollars.
const rate = {
PENNY: 0.01, // this means a PENNY is worth $0.01
NICKEL: 0.05,
DIME: 0.1,
QUARTER: 0.25,
ONE: 1,
FIVE: 5,
TEN: 10,
TWENTY: 20,
"ONE HUNDRED": 100,
};
 Now, we need to check :
 If
totalCid === change
, we returnCLOSED
.  If
totalCid < change
, we returnINSUFFICIENT_FUNDS
.  If
totalCid > change
, we returnOPEN
and calculate the correct amount of the change to be returned.
if (totalCid === change) {
return { status: "CLOSED", change: cid };
} else if (totalCid < change) {
return { status: "INSUFFICIENT_FUNDS", change: [] };
} else {
// here, we calculate the correct amount of the change to be returned.
// check step 5
}
 Now, inside the last else block, we need to :
 Create a variable in which we will store the change to return.
 Reverse the
cid
array so that it is in descending order (from the highest value to the lowest). See the real example I explained above for why this is necessary. 
Loop the array.
let changeArray = []; cid.reverse().forEach((el) => { // check step 6 });
 Now inside the loop, for each element of the array, we set some variables for :
 The name of the coin, for example
PENNY
.  The total value of the coins we have in the drawer in dollars, for example
1.01
.  The value or the equivalent of the coin in dollars, we pick it from the rates object we created in step 3, for example
20
. 
How many coins I have available, for example we have
$1.01
worth ofPENNIES
, and eachPENNY
is worth$0.01
, this means we have101
PENNY
.
cid.reverse().forEach((el) => { const coinName = el[0]; // PENNY const coinTotalValueInDollars = Number(el[1]); // 1.01 const selectedCurrency = Number(rate[coinName]); // rate['PENNY'] > output : 0.01 let coinsAvailable = Number( (coinTotalValueInDollars / selectedCurrency).toFixed(2) ); // 1.01 / 0.01 > output : 101 });
✳ Before continuing, let's first have another real example on how we calculate the right amount of change to be returned using the following cashindrawer array
:
[
["PENNY", 1.01],
["NICKEL", 2.05],
["DIME", 3.1],
["QUARTER", 4.25],
["ONE", 90],
["FIVE", 55],
["TEN", 20],
["TWENTY", 60],
["ONE HUNDRED", 100],
];
 From the array, and with the help of the rates object, we have in the drawer :
Currency  Number of bills/coins 

ONE HUNDRED  1 
TWENTY  3 
TEN  2 
FIVE  11 
ONE  90 
QUARTER  17 
DIME  31 
NICKEL  41 
PENNY  101 
ℹ You read the table this way : We have 3 twentydollar bills

Now, if you buy something worth
$9.50
and you give the seller$100
, the change would be$90.5
. To calculate the amount to return : From the currencies we have in the cash drawer (picture above), we search for the one that's (
≤
) less than or equal to the change :  Can't be
$100
because$90.5
>$100
.  The next currency is
$20
, and that's the right one.  If we have enough bills/coins of the selected currency, we subtract, if not, we move to the next currency and so on.
 From the currencies we have in the cash drawer (picture above), we search for the one that's (
Now, to implement this with code, we use the while
statement :
 While change
≥
selected currencyand
we have enough bills/coins, we subtract and save how many bills/coins we gave.
let coinsToReturn = 0; // we didn't give any bills/coins yet
while (change >= selectedCurrency && coinsAvailable > 0) {
change = Number((change  selectedCurrency).toFixed(2));
coinsAvailable; // decrement the total by 1 (1)
++coinsToReturn; // increment the total by 1 (+1)
}
 If
coinsToReturn > 0
, it means we gave some bills/coins, so we push to the empty array we created on step 5 the currency name and the total in dollars we gave worth of this currency.
if (coinsToReturn > 0) {
changeArray.push([coinName, coinsToReturn * selectedCurrency]);
}
 Finally, we need to check, if
change = 0
, this means we can return the amount of change, if not, we have insufficient funds.
if (change === 0) {
return { status: "OPEN", change: changeArray };
} else {
return { status: "INSUFFICIENT_FUNDS", change: [] };
}
✅ Thats it, we finished 🎉🎉.
Note: I have tried to make this as beginnerfriendly as possible, which may result in some repetition and potentially boring sections.
You can check the solutions on my github.
Top comments (0)