Challenges
Select a challenge to start coding.
The Default Greeting
Let's practice using the binary Logical OR || to provide a "fallback" value. This is its most common use case for this operator.
Write a function greet(name) that takes a single name string as an argument.
If a
nameis provided (e.g.,"Alice"), it should return"Hello, Alice!".If the
nameis not provided (it will beundefined) or if it's an empty string (""), it should return"Hello, Guest!".
Constraint: You cannot use an if statement for this.
Test Cases:
greet("Alice")should return"Hello, Alice!"greet("")should return"Hello, Guest!"greet(undefined)should return"Hello, Guest!"
💡 The 'Syntax Nudge'
This is the perfect place to use the Logical OR (||) operator.
Remember its core rule: valueA || valueB The JavaScript engine first looks at valueA.
If
valueAis "truthy" (any string with content, any number not 0,true, objects, arrays), it just returnsvalueAand stops.If
valueAis "falsy" (specificallyundefined,null,""(empty string),0,false,NaN), it discards it and returnsvalueBinstead.
We can think of operators in terms of how many "things" (we call them operands) they work on.
Unary Operator: Works on one thing.
!(Logical NOT):!isReady(flipsisReadyfrom true to false)typeof:typeof 10(tells you the type is "number")i++(Increment): Works on the single variablei.
Binary Operator: Works on two things. This is most of what you use every day.
1 + 2(The+works on1and2)x > y(The>works onxandy)a && b(Logical AND)a || b(Logical OR - the one that confused you!)
Ternary Operator: The one and only! It works on three things.
(condition ? valueIfTrue : valueIfFalse)
The Light Switch
Write a function toggleSwitch(isOn) that takes a single boolean value (true or false) representing the state of a light switch. Your function should return the new state of the switch.
Test Cases:
toggleSwitch(true)should returnfalsetoggleSwitch(false)should returntrue
💡 The 'Syntax Nudge'
This one is a direct application of the operator. How can you use ! on the isOn argument to return the opposite value?
The Logical NOT ! is a unary operator because it only works on one operand (the value right after it).
Its job is simple:
It takes any value.
It coerces (converts) that value into its "truthy" or "falsy" boolean equivalent.
It flips that boolean.
!truebecomesfalse!falsebecomestrue!"hello"(a "truthy" string) becomesfalse!0(a "falsy" number) becomestrue
The "Empty" Checker
Let's write a function isEmpty(value) that checks if a value is "empty." For our purposes, "empty" means it's one of the common falsy values:
undefinednull0""(empty string)false
Your function should return true if the value is falsy (empty) and false if the value is truthy (not empty).
Test Cases:
isEmpty("")should returntrueisEmpty(0)should returntrueisEmpty(undefined)should returntrueisEmpty("hello")should returnfalseisEmpty(42)should returnfalseisEmpty(true)should returnfalse
💡 The 'Syntax Nudge'
Think about the two-step process of the ! operator:
It coerces the value to its boolean equivalent. (
"hello"becomestrue,""becomesfalse).It flips that boolean.
How can you use ! to achieve the desired outcome? You might need to use it more than once. Think about what !value would give you, and what !!value would give you. Which one gets you closer to the goal?
The "Truthy" Checker
Write a function isTruthy(value) that takes any value.
It should return
trueif the value is truthy.It should return
falseif the value is falsy.
Constraint: You cannot use an if statement.
Test Cases:
isTruthy("hello")should returntrueisTruthy(42)should returntrueisTruthy(true)should returntrueisTruthy("")should returnfalseisTruthy(0)should returnfalseisTruthy(undefined)should returnfalse
💡 The 'Syntax Nudge'
This is the classic use case for the double bang (!!) operator. How can you use it to "normalize" the input value into its pure boolean form?
If ! means "take this value, find its truthiness, and flip it," then !! simply means "do that twice."
!!value=!(!value)
Why on earth would you do this? It's used to normalize any value into a "pure" boolean. It's a very common way to answer the question, "Does this value have anything in it?"
Let's trace it with "hello":
!"hello"(truthy) becomesfalse!false(the second!) becomestrueSo,!!"hello"=true
Let's trace it with 0:
!0(falsy) becomestrue!true(the second!) becomesfalseSo,!!0=false
The !! operator is a concise way of forcing any value to its simple true or false equivalent.
The Price Checker
Let's write a function getTicketPrice(age) that takes a person's age.
If the person is
18or older, the price is$20.If the person is younger than
18, the price is$10.
Constraint: You must use the ternary operator to solve this.
Test Cases:
getTicketPrice(30)should return$20getTicketPrice(18)should return$20getTicketPrice(10)should return$10
💡 The 'Syntax Nudge'
Your "condition" will be age >= 18. Your "valueIfTrue" will be $20. Your "valueIfFalse" will be $10.
How do you assemble these three parts using the ? : syntax?
The Ternary (? :)
This operator is a "ternary" because it works on three operands. It's our final operator in this bootcamp.
It's just a clean, one-line shortcut for a simple if...else statement.
The if...else way:
let message;
if (age >= 21) {
message = "You may enter.";
} else {
message = "You are too young.";
}
The Ternary way:
let message = (age >= 21) ? "You may enter." : "You are too young.";
The syntax is: (condition) ? valueIfTrue : valueIfFalse
The Key Collector
Write a function collectKeys(obj) that takes an object. Return an array of all the keys in that object, but converted to uppercase.
Input:
{ id: 1, city: "NY" }Step 1: Get the keys (array).
Step 2: Map over them to uppercase.
Return:
["ID", "CITY"]
Explanation of object Iteration tools:
Here is the problem: Arrays are easy to loop over. Objects are not.
const user = { name: "Alice", age: 25, role: "Admin" };
// user.map(...) // ❌ CRASH! Objects don't have .map()
// user.forEach(...) // ❌ CRASH!
To loop over an object, we use a "Static Method" to convert the Object into an Array. The first tool is: Object.keys(obj)
It returns an array of the property names (keys).
const keys = Object.keys(user);
// ["name", "age", "role"]
// NOW you can use .map() or .forEach() on 'keys'!
The Nested Ternary
Let's write a function getAccessLevel(role) that takes a role string.
If the
roleis"admin", return"Full Access".If the
roleis"editor", return"Limited Access (Editing)".For any other role (including
undefinedor""), return"Guest Access".
Constraint: You must solve this with a single return statement using only ternary operators. No if/else.
Test Cases:
getAccessLevel("admin")should return"Full Access"getAccessLevel("editor")should return"Limited Access (Editing)"getAccessLevel("user")should return"Guest Access"getAccessLevel("")should return"Guest Access"
💡 The 'Syntax Nudge'
This is what separates a novice from a master. The valueIfFalse part of a ternary operator doesn't just have to be a simple value... it can be another ternary operator.
Think about it like this: return (condition1) ? "valueA" : (condition2 ? "valueB" : "valueC")
How can you "nest" your logic to check for "admin", then "editor", then everything else?
Reverse a String
Write a function called reverseString that takes a single string as an argument and returns a new string with the characters in the reverse order.
For example:
reverseString("hello")should return"olleh"reverseString("JavaScript")should return"tpircSavaJ"
Now, for your first nudge... 💡
The 'Syntax Nudge'
A very clean way to solve this involves changing the string into a different data type that's easier to re-order, and then changing it back. I suggest you research these three highly useful JavaScript methods on MDN (the Mozilla Developer Network):
String.prototype.split()Array.prototype.reverse()Array.prototype.join()
Palindrome Checker
A palindrome is a word, phrase, or sequence that reads the same backward as forward. For example, "racecar" or "madam".
Write a function isPalindrome(str) that takes a string and returns true if the string is a palindrome and false if it is not.
For this challenge, we should ignore punctuation, spaces, and casing. So, "A man, a plan, a canal: Panama" should return true.
Here are some test cases:
isPalindrome("racecar")should returntrueisPalindrome("hello")should returnfalseisPalindrome("A man, a plan, a canal: Panama")should returntrue
You already know how to reverse a string, which is half the battle! The other half is cleaning up the input string first.
💡 The 'Syntax Nudge'
To solve this, you'll need to "sanitize" the input string before you check it. I suggest you research these two tools:
String.prototype.toLowerCase(): How can you make sure "T" is treated the same as "t"?String.prototype.replace(): This method is incredibly powerful. Specifically, look up how to use it with a bit of RegEx (Regular Expressions) to remove characters you don't want. A good RegEx to search for would be one that finds all non-alphanumeric characters.
Anagram Checker
An anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once. For example, "listen" is an anagram of "silent".
Write a function isAnagram(str1, str2) that takes two strings and returns true if the second string is an anagram of the first, and false otherwise.
Like before, the comparison should be case-insensitive.
Spaces and punctuation should be considered, so
"Dormitory"is an anagram of"dirty room", but you'll need to remove the space.
Test cases:
isAnagram("anagram", "nagaram")should returntrueisAnagram("rat", "car")should returnfalseisAnagram("Dormitory", "dirty room")should returntrue
💡 The 'Syntax Nudge'
You could solve this by sanitizing and sorting the strings, but let's try a more performant approach that doesn't require sorting. This introduces the Frequency Counter pattern.
The core idea is to count how many times each character appears in the first string, and then see if the second string matches that count exactly. An object {} is the perfect tool for this.
I suggest you research:
How to loop through a string using a for ... of loop.
How to add and access properties in a JavaScript object (e.g.,
myObject[key]).A clever trick for incrementing a count in an object:
charMap[char] = (charMap[char] || 0) + 1;. Think about why the|| 0part is necessary.
Ransom Note
Write a function canWriteRansomNote(note, magazine) that takes two strings, note and magazine. Your function should return true if the note can be constructed by cutting out letters from the magazine and false otherwise.
Each letter in the
magazinecan only be used once.The comparison is case-sensitive (this makes it a bit easier!).
Test Cases:
canWriteRansomNote("a", "b")should returnfalsecanWriteRansomNote("aa", "ab")should returnfalsecanWriteRansomNote("aa", "aab")should returntruecanWriteRansomNote("find me", "i need to find a new me")should returntrue
Think about the logic we just used. Which string should you build the 'Character Map' from? And which string should you check against the map?
💡 The 'Syntax Nudge'
You have all the tools you need! The logic is identical, but the order is important.
You'll need to build a frequency map (just like
charMap) of the letters you have available. Which string represents the letters you have?Then, you'll loop through the string you're trying to build and check against that map.
You'll use the same
for...ofloop and the same Bracket Notation (map[char]) to solve this.
The Inventory Aggregator
Write a function groupInventory(items) that takes an array of item objects. Each item has a name and a category. Your function should return a single object where the keys are the categories, and the values are arrays of item names belonging to that category.
Constraint: You must use the Array.prototype.reduce() method. Do not use a for loop or forEach.
Test Cases:
const items = [
{ name: "Apple", category: "Fruit" },
{ name: "Carrot", category: "Vegetable" },
{ name: "Banana", category: "Fruit" },
{ name: "Broccoli", category: "Vegetable" }
];
groupInventory(items);
Should return:
{
Fruit: ["Apple", "Banana"],
Vegetable: ["Carrot", "Broccoli"]
}
groupInventory([]); // Should return {}