The best way to learn to code in JavaScript is to practice, practice, practice. That’s why today we will go over 10 coding challenges to level up your JavaScript skills. These exercises are useful for any JavaScript developer. If you’re an absolute beginner, it will be helpful to check out our beginner’s guide before diving in.
JavaScript challenges can be difficult enough without us trying to make them harder for ourselves. One of the ways to make them easier is to follow principles outlined in a short book by George Polya called How To Solve It. These steps are as follows:
As we challenge ourselves in the next ten problems, keep these steps in mind. Keep in mind that there are several ways to solve these problems. As long as you can explain your thought-process in good detail, your answer is probably fine!
This course will teach you the fundamentals of creating web applications, from the basics of creating web pages with HTML, stylizing content with CSS, all the way to building interactivity into a page using JavaScript in the browser. Instead of watching tedious videos and wondering how to translate those videos into real code, you'll be practicing what you learn through interactive, test-based exercises within minutes. Along the way, you'll be able to produce functional modules, including an image carousel and a to-do list application. No prior knowledge is needed.
Before you dive in, tag each exercise with Difficulty, write down Constraints (input size, allowed methods, time/space goals), and define Acceptance criteria (examples + edge cases). A consistent spec builds the muscle you’ll need in interviews and on the job:
Template:
Difficulty: 2/5
Constraints: n ≤ 10^5, strings are ASCII, no regex allowed
Examples: input/output table with at least 3 cases
Edge cases: empty array, duplicate values, unicode, etc.
Goal: explain your approach and Big-O in one sentence
To keep momentum, set a streak (e.g., one challenge per day) or couple these with a 30-day project cadence like JavaScript30 to pair algorithms with UI skills.
Write a function that accepts an array of strings. Return the longest string.
String[] = an array of stringsString = the longest of the strings in the arrayDo we need to consider if there are no strings in the array?
Do we need to consider times when all of the strings are the same length?
For this problem, we need to:
// Try it out yourself here before checking the solution!
There are two different ways listed here as a possible solution to this problem: one uses the ES5 Syntax and uses the ES6 Syntax.
In both solutions, we initially need to declare a longest variable and initialize it to an empty string.
In the ES5 solution, we are utilizing a traditional for loop structure to look at each index in the array. We are using the array method forEach to iterate over the array in the ES6 solution.
As we iterate through the data structure, we want to take a look at the length of each string. In JavaScript, we can do that with the length property.
In the ES5 solution, we utilized a conditional statement to see if the current (arr[i]) string’s length is longer than (but not equal to) the current longest string(longest). The ES6 solution takes another approach with using the Math object property max as a shortcut for the conditional logic in the ES5 version.
If the string is longer, we assign it to the longest variable we already created.
Return the longest as our function’s terminal statement in both cases.
Write a function that takes a string, and returns the character that is most commonly used in the string.
String = one StringString = the single character that shows up the most in the given stringDo we need to consider if there is no string given?
Do we need to consider times when more than one character shows up the same number of times?
To find the character most often used in a string, we need to be able to count how many of each kind we have. We can do this using an object that has key:value pairs.
maxCharCount and new max key to maxChar.maxChar// Try it out yourself here before checking the solution!
Create a charCount object. This object can be named whatever you’d like as long as it’s consistent.
Use a standard for loop to iterate through the string, and check to see if it exists in the charCount object. If it does, add 1 to CharCount[key] and reassign it to the same variable. The pipes here will cover any instance where the key does not yet exist in the object and initialize the count to 1.
Loop through the object using a for...in loop. This type of loop allows us to iterate over each property in an object to look at its value. In this instance we want to find the largest number.
The wording for this problem indicates that they want us to find the last max number (in the case there is more than one character with the same amount of appearances), so we use >= to reassign the maxChar if needed.
Return our maxChar.
Create a function that takes in two strings as two parameters and returns a boolean that indicates whether or not the first string is an anagram of the second string.
String, String = two StringsBoolean are either true or false. True if they are anagrams of each other, and false if they are not.What is an anagram?
Do we need to consider whitespace?
If an anagram is just a bunch of letters jumbled around, we can see if the strings are anagrams of each other by:
Comparing the length: if they are not same length, they are not an anagram of the other
Sorting the string and using an equality operator to see if it’s equal.
There are a couple of different ways to solve this problem.
// Try it out yourself here before checking the solution!
false.true. If not, it will return false.This particular solution considers white space by sorting it to the end of the string, so it doesn’t necessarily matter if it’s counted or not.
Given a string, write a function that will return whether or not that string is a palindrome.
String = one StringBoolean means either true or false. true if string is a palindrome; false if it is not.Do we need to consider whitespace?
Does the palindrome need to be in the dictionary?
// Try it out yourself here before checking the solution!
This one is deceptively simple. You just need to have knowledge of array methods and how to work with strings in conjunction with those methods. Practice how this could possibly be done without the methods so you know how these methods work under the hood.
Learn JavaScript without scrubbing through videos or documentation. Educative’s text-based courses are easy to skim and feature live coding environments, making learning quick and efficient.
The Complete Guide to Modern JavaScript
Given a string possibly containing three types of braces ({}, [], ()), write a function that returns a Boolean indicating whether the given string contains a valid nesting of braces.
String = one StringBoolean means either true or false. true if string has balanced brackets; false if it does not.What do you mean by balanced brackets?
Will the string only have brackets in it?
Because we are looking for nested balanced brackets, we are looking to use a data structure to store the open brackets we have seen so far.
As we come to a closing bracket, we need to look to see if the last bracket added to the data structure matches the opposite of the current bracket.
If your data structure is empty by the time we get to the end of the string, we have balanced brackets.
// Try it out yourself here before checking the solution!
The process for this particular problem is more about checking for failures. We use a data structure called a stack that takes a first in, last out approach to do that.
When we come across an open bracket, we push it on to the stack. When we come across a closed one, we check to see if the last index of the stack is the opposite the current character.
If the stack is empty by the time we get to the end of our logic, we have balanced brackets.
Most JavaScript coding challenges lists now include real-world browser tasks. Add a few of these to round out your skill set.
Prompt: Implement debounce(fn, delay) so that rapid calls result in only the last call executing after delay ms.
Constraints: Must preserve this and arguments; return a cancellable wrapper.
function debounce(fn, delay = 300) {
let t;
return function debounced(...args) {
const ctx = this;
clearTimeout(t);
t = setTimeout(() => fn.apply(ctx, args), delay);
};
}
Why it matters: Prevents excessive DOM work (resize, scroll, input).
Prompt: Implement throttle(fn, interval) so calls execute at most once per interval.
function throttle(fn, interval = 200) {
let last = 0, timeout, savedArgs, savedThis;
const later = () => {
last = Date.now();
timeout = null;
fn.apply(savedThis, savedArgs);
};
return function throttled(...args) {
const now = Date.now();
const remaining = interval - (now - last);
savedArgs = args; savedThis = this;
if (remaining <= 0 || remaining > interval) {
if (timeout) { clearTimeout(timeout); timeout = null; }
last = now; fn.apply(savedThis, savedArgs);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
};
}
Why it matters: Smooth scroll handlers, drag events, infinite lists.
Prompt: Create promiseAll(iterable) that resolves to an array of values or rejects on the first failure.
function promiseAll(iterable) {
return new Promise((resolve, reject) => {
const results = [];
let remaining = 0, index = 0;
for (const p of iterable) {
const i = index++; remaining++;
Promise.resolve(p).then(
val => { results[i] = val; if (--remaining === 0) resolve(results); },
reject
);
}
if (index === 0) resolve([]);
});
}
Edge cases: Empty iterable; non-promise values in the list.
Prompt: Implement a tiny Emitter with on, off, and emit.
class Emitter {
constructor(){ this.map = new Map(); }
on(evt, cb){ (this.map.get(evt) ?? this.map.set(evt, []).get(evt)).push(cb); return () => this.off(evt, cb); }
off(evt, cb){ const arr = this.map.get(evt) || []; const i = arr.indexOf(cb); if (i>-1) arr.splice(i,1); }
emit(evt, ...args){ (this.map.get(evt) || []).forEach(cb => cb(...args)); }
}
Where it’s used: Pub/sub in widgets; wiring UI modules.
Tip: Balance these with UI practice from real designs (e.g., Frontend Mentor) so your JavaScript coding challenges reflect production realities.
An Armstrong number is an n-digit number that is equal to the sum of the powers of its digits. Determine if the input number is an Armstrong number. Return either true or false.
Number = n-digit numberBoolean means either true or false. true if number is Armstrong Number; false if it is not.May I have an example please?
Remember that in JavaScript type coercion exists. When working with Numbers is JS, remember to check the typeof the number. If it is a string, you’ll need to plan for that. This is really important when working with math in JS.
// Try it out yourself before clicking on the solution tab!
To solve this problem, we change the number to a string, figure out the power by getting the length of the number, and then use a for loop to add up the individual numbers ^ length of the number passed in.
We then check to see if the sum is equal to the original number. If it is, it is an Armstrong number.
Given an array of objects, sort the objects by population size. Return the entire object.
array of objectsarray of objects, sorted by populationJavaScript has a built-in array method called sort() that we can use for this particular exercise.
What’s interesting about this particular method is that in JavaScript, if you don’t use a callback compare function to sort the array of objects, the default sort method is used to sort by characters rather than number. For example:
const sortNumbers = (arr) => {
return arr.sort();
}
console.log(sortNumbers([1, 11, 27, 2, 34, 123])); // [ 1, 11, 123, 2, 27, 34 ]
We need to use a compare function as a callback passed into the sort method to make sure that the actual numbers are treated properly.
// Try it out yourself before clicking on the solution tab!
To sort an array of objects by a certain property, we have to use the compare function as a parameter in the built-in sort method in JavaScript.
The compare function takes in two parameters: here they are named a and b. They represent two objects next to each other in our array. If a and b are both objects, we can use dot notation to access the property population.
If we want to be sure that the array has objects sorted by population in increasing order, we evaluate a.population - b.population. If it’s -1, it will put the list in decreasing order by population. The opposite would put it in increasing order. If the expression happens to evaluate to 0, it means that the two patterns are equal to the other.
Modern codebases lean on functional patterns. Add these quick exercises to strengthen your grasp of JavaScript fundamentals.
Prompt:
flattenDeep([1, [2, [3]], 4]) // -> [1, 2, 3, 4]
const flattenDeep = arr => arr.reduce(
(acc, el) => acc.concat(Array.isArray(el) ? flattenDeep(el) : el), []
);
Alt: Iterative with a stack; compare space complexity.
Prompt:
curry(f)(a)(b)(c) === f(a, b, c) for any arity.
const curry = f => function curried(...args) {
return args.length >= f.length ? f(...args) : (...rest) => curried(...args, ...rest);
};
Prompt: Implement groupBy(arr, fn) returning an object keyed by fn(item).
const groupBy = (arr, fn) =>
arr.reduce((acc, x) => ((acc[fn(x)] ??= []).push(x), acc), {});
Use cases: Bucketing API results; building facets.
For each, note time/space complexity, common pitfalls (e.g., stack depth on deep flatten), and an alternative approach to imitate top-tier solutions.
Create a Node class and a LinkedList class with these methods:
insertFirst(data)insertLast(data)getFirst()getLast()Are we to assume this is a singly-linked list that actually exists?
The first thing we need to investigate is how a linked list works. Once we have that figured out, it might be a lot easier to write out the code for it.
Linked lists are different from arrays in that they don’t require a continuous place of residence in memory. Linked Lists can be broken up into single nodes if needed as long as the linked list’s next value is not null.
Let’s plan to initialize three variables: prev as NULL, curr as head, and next as NULL. Iterate trough the Linked List. In a while loop, do the following.
next = curr.nextcurr.next = prev// Try it out yourself here before checking the solution!
We start with the Node class. It will have a data property and a next property. The next property acts as a pointer that will direct us to the next node in our linked list if it exists, or null.
For the Linked List class, we check for failures in our methods before we handle the success. With Linked Lists, we will check to see if the actual list exists by checking to see if the head exists and perform the appropriate logic for each method.
When we reverse a linked list, juggling variables around is the rule of the day. We have one variable, node, that is declared and then initialized to head. We declare tmp and prev without initializing them.
While the node exists, we redirect the pointers by reassigning variables. The result is a reversed linked list.
Using the Node and LinkedList classes that you created in the previous question, add methods to it so that, when given an kth-integer, you can delete that node from the linked list
Is this a zero-based indexing?
When given K we have to be able to delete that node in a linked list. To handle edge cases, we need to have some knowledge of the size of the list, the node prior to the Kth node and the node after the Kth node. We essentially need to redirect the pointers for those nodes to not include the one we want to be deleted.
To do that, we’ll loop through the list, keep track of which node we’re on, and change the pointers to skip the Kth-node. It’ll still be in memory, ready to be written over, but it will be essentially deleted from our list.
// Try it out yourself here before checkin the solution!
Given a linked list, return true if the list is circular, false if it is not.
LinkedList()A Boolean that tells us if the linked list is circular or not.
What does it mean for a linked list to be circular?
We will have two pointers: one slow and one fast. The fast one will move twice as much as the slow one on each iteration. If it comes to be that the fast.next.next value doesn’t exist, we can assume it’s a linear linked list. If the slow pointer and the fast pointer ultimately end up equal, we know we have a circular component to our list.
// Try it out yourself here before checking the solution!
Here we declare and initialize two pointers, one slow and one fast, that are both pointed at the head. While the next two nodes exist, we’ll reassign the pointers: the slow will move to the next node and the fast will move two nodes.
If the slow ever is the same value as the fast, we have a circular linked list. If ever one of the two nodes past the current node doesn’t exist, we’ll break the loop and return false.
In this course, you’ll learn JavaScript from scratch by building things step by step. You’ll start with simple interactions like displaying messages and buttons that respond to clicks. Then, you’ll teach your code to think using logic, remember things with variables, and make decisions based on the user’s actions. You’ll explore variables, functions, objects, DOM manipulation, event handling, loops, and arrays to build simple yet interactive real-life projects. You’ll go from writing your first line of code to building mini apps like a quiz, a to-do list, and even your digital pet! Every lesson is project-based and beginner-friendly—designed to help you create, not just code. By the end, you’ll confidently understand how to control the page, respond to users, and build interactive web experiences.
Focus on strings, arrays, and maps along with your current algorithm sections.
Track your time and write Big-O for every solution.
Add the debounce, throttle, Promise, and Emitter challenges.
Build one mini UI per day (pair with a Frontend Mentor component).
Work on flatten, curry, compose, memoize, and groupBy.
Refactor earlier solutions using map, filter, and reduce.
Mix in kata-style problems and project streaks
(e.g., Codewars for graded kyu difficulty; JavaScript30 for daily shipping).
If you want feedback or review, try the Exercism JavaScript track to get mentor comments on your submissions.
Congrats! You’ve gone through 10 JavaScript coding challenges! One of the best things to do when encountering a problem is to use Georges Polya’s practices. Once we understand the problem, we make a plan and try to reiterate an unsolved problem. This way, you are more likely to break down the problem into smaller manageable components.
Solving these problems takes practice. You’ll develop an intuition for algorithms the more time you put into practicing. So don’t worry if it doesn’t come to you right away. Keep practicing!
Educative’s course The Complete Guide to Modern JavaScript
walks you through the basics of the language and all the new features introduced up to 2020. After following this course, functions and variables let, const, generators, promises and async won’t be a problem anymore.