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
Return 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 non-alphanumeric 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 We'll also pass strings with special symbols, such as DESCRIPTION
true
if the given string is a palindrome. Otherwise, return false
.racecar
, RaceCar
, and race CAR
among others.2A3*3a2
, 2A3 3a2
, and 2_A3*3#A2
.
Solution ✅
function palindrome(str) {
const filteredStr = str.toLowerCase().replace(/[^a-z0-9]/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 non-alphanumeric characters with the help of
regEx
andreplace()
method.
console.log("_test55^".replace(/[^a-z0-9]/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
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 Write a function which takes a All letters will be uppercase. Do not transform any non-alphabetic character (i.e. spaces, punctuation), but do pass them on.DESCRIPTION
ROT13
cipher, where the values of the letters are shifted by 13 places. Thus A ↔ N
, B ↔ O
and so on.ROT13
encoded string as input and returns a decoded string.
Solution ✅
function rot13(str) {
const max = 90; // decimal value of Z
return str
.split("")
.map((el) => {
if (el.match(/[A-Z]/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 UTF-16 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
Return 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): For this challenge you will be presented with a string such as DESCRIPTION
true
if the passed string looks like a valid US phone number.
555-555-5555
(555)555-5555
(555) 555-5555
555 555 5555
5555555555
1 555 555 5555
800-692-7753
or 8oo-six427676;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 555-555-5555");
Explanation ℹ
Explaining Regular expressions
is another topic beyond this post.
Problem N°5 : Cash Register
Design a cash register drawer function The Return Return Otherwise, return See below for an example of a cash-in-drawer array:DESCRIPTION
checkCashRegister()
that accepts purchase price as the first argument (price
), payment as the second argument (cash
), and cash-in-drawer (cid
) as the third argument.cid
is a 2D array listing available currency.checkCashRegister()
function should always return an object with a status
key and a change
key.{status: "INSUFFICIENT_FUNDS", change: []}
if cash-in-drawer is less than the change due, or if you cannot return the exact change.{status: "CLOSED", change: [...]}
with cash-in-drawer as the value for the key change
if it is equal to the change due.{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)
One-hundred Dollars
$100 (ONE HUNDRED)
[
["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 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],
];
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 `cash-in-drawer 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 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],
];
- 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 twenty-dollar 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 beginner-friendly as possible, which may result in some repetition and potentially boring sections.
You can check the solutions on my github.
Top comments (0)