This year Scrimba is holding an Advent calendar by releasing daily coding challenges from December 1st to the 24th. They are also giving away free yearly subscriptions each day and a USD 1,000 grand prize at the end.
I’ve been doing them since day one and I have to say that I’m having a blast! They are fun little challenges that don’t take more than five minutes and I find them as a perfect way to end my –coding– day. I specially like to check other’s people solutions afterwards, as some of them are pretty clever and I can always learn something new.
So, if you haven’t joined yet, what are your waiting for? It’s a lot of fun and you could end up winning the big prize! 🤑
What is Scrimba?
In case you didn’t know already, Scrimba is a learning platform for developers that teaches coding and other related skills through interactive screencasts, called “scrims” (they’re pretty neat, you can actually pause them at any time, edit and run code directly!). I’ve been using it since 2018 and it has become one of my favourite learning platforms. By the way, they have a lot of free courses if you want to check them out!.
Well, without further ado, here are my solutions:
Day 1: Candies
Challenge
n
children have got m
pieces of candy. They want to eat as much candy as they can, but each child must eat exactly the same amount of candy as any other child. Determine how many pieces of candy will be eaten by all the children together. Individual pieces of candy cannot be split.
My solution (🔗)
1function candies(children, candy) {2 return Math.floor(candy / children) * children;3}
Official solution (🔗)
1function candies(children, candy) {2 const candyPerChild = Math.floor(candy / children);34 return candyPerChild * children;5}
Day 2: Deposit Profit
Challenge
You have deposited a specific amount of dollars into your bank account. Each year your balance increases at the same growth rate. Find out how long it would take for your balance to pass a specific threshold with the assumption that you don’t make any additional deposits.
My solution (🔗)
1function depositProfit(deposit, rate, threshold) {2 const COMPOUND_RATE = 1 + rate / 100;3 let years = 0;4 let holdings = deposit;56 while (holdings < threshold) {7 years++;8 holdings *= COMPOUND_RATE;9 }1011 return years;12}
Official solution (🔗)
1function depositProfit(deposit, rate, threshold) {2 let year = 0;3 let currentAccountValue = deposit;45 while (threshold > currentAccountValue) {6 currentAccountValue += currentAccountValue * (rate / 100);7 year++;8 }910 return year;11}
Day 3: Chunky Monkey
Challenge
Write a function that splits an array (first argument) into groups the length of size (second argument) and returns them as a multidimensional array.
My solution (🔗)
1function chunkyMonkey(values, size) {2 let arr = [];3 let offset = 0;4 for (let i = 0; i < values.length / size; i++) {5 offset = size * i;6 arr.push(values.slice(offset, offset + size));7 }8 return arr;9}
Official solution (🔗)
1function chunkyMonkey(values, size) {2 const nested = [];3 let count = 0;45 while (count < values.length) {6 nested.push(values.slice(count, (count += size)));7 }89 return nested;10}
Day 4: Century From Year
Challenge
Given a year, return the century it is in. The first century spans from the year 1 up to and including the year 100, the second - from the year 101 up to and including the year 200, etc.
My solution (🔗)
1function centuryFromYear(num) {2 const rest = num % 100;34 let century = Math.floor(num / 100);5 century += rest > 0 ? 1 : 0;67 return century;8}
Official solution (🔗)
1function centuryFromYear(year) {2 const century = year / 100;34 if (year % 100 === 0) {5 return century;6 }78 return Math.floor(century) + 1;9}
Day 5: Reverse a string
Challenge
Reverse the provided string. You may need to turn the string into an array before you can reverse it. Your result must be a string.
My solution (🔗)
1function reverseAString(str) {2 return str3 .split('')4 .reverse()5 .join('');6}
Official solution (🔗)
1function reverseAString(str) {2 let reversedStr = '';34 for (let i = str.length - 1; i >= 0; i--) {5 reversedStr += str[i];6 }78 return reversedStr;9}
Day 6: Sort by Length
Challenge
Given an array of strings, sort them in the order of increasing lengths. If two strings have the same length, their relative order must be the same as in the initial array.
My solution (🔗)
1function sortByLength(strs) {2 return strs.sort((a, b) => a.length - b.length);3}
Official solution (🔗)
1function sortByLength(strs) {2 return strs.sort((str1, str2) => str1.length - str2.length);3}
Day 7: Count Vowel Consonant
Challenge
You are given a string s that consists of only lowercase English letters. If vowels (‘a’, ‘e’, ‘i’, ‘o’, and ‘u’) are given a value of 1 and consonants are given a value of 2, return the sum of all of the letters in the input string.
My solution (🔗)
1function countVowelConsonant(str) {2 const counter = (count, char) => {3 const isVowel = 'aeiou'.indexOf(char) !== -1;4 return count + (isVowel ? 1 : 2);5 };67 return str.split('').reduce(counter, 0);8}
Official solution (🔗)
1function countVowelConsonant(str) {2 const vowels = ['a', 'e', 'i', 'o', 'u'];3 const chars = str.split('');4 const total = chars.reduce((acc, char) => {5 if (vowels.includes(char)) {6 return acc + 1;7 }89 return acc + 2;10 }, 0);1112 return total;13}
Day 8: Rolling Dice
Note: This challenge involves additional HTML and CSS, so I recommend checking the solutions directly at Scrimba.
Challenge
In this challenge a casino has asked you to make an online dice that works just like it would in real life. Using the pre-made dice face that represents ‘one’, make the faces for ‘two’, ‘three’, ‘four’, ‘five’ and ‘six’. Now when the users clicks the dice on the screen the dice is expected to show one of the faces randomly.
My solution (🔗)
1const dice = document.querySelector('.dice');2const face = dice.querySelector('.face');3const message = document.querySelector('.message');45const roll = () => Math.ceil(Math.random() * 6);67dice.addEventListener('click', (e) => {8 const number = roll();9 face.className = `face dot-${number}`;10 message.innerHTML = `You got a ${number}!`;11});
Official solution (🔗)
1const dice = document.querySelector('.dice');2const allDots = Array.from(document.querySelectorAll('.dice div'));34function rollDice() {5 let randomNumber = 1 + Math.floor(Math.random() * 6);6 console.log(randomNumber);78 allDots.forEach((dot) => dot.classList.remove(...dot.classList));910 if (randomNumber === 1) {11 allDots[4].classList.add('dot');12 }1314 if (randomNumber === 2) {15 allDots[0].classList.add('dot');16 allDots[8].classList.add('dot');17 }18 if (randomNumber === 3) {19 allDots[0].classList.add('dot');20 allDots[4].classList.add('dot');21 allDots[8].classList.add('dot');22 }23 if (randomNumber === 4) {24 allDots[0].classList.add('dot');25 allDots[2].classList.add('dot');26 allDots[6].classList.add('dot');27 allDots[8].classList.add('dot');28 }29 if (randomNumber === 5) {30 allDots[0].classList.add('dot');31 allDots[2].classList.add('dot');32 allDots[4].classList.add('dot');33 allDots[6].classList.add('dot');34 allDots[8].classList.add('dot');35 }36 if (randomNumber === 6) {37 allDots[0].classList.add('dot');38 allDots[2].classList.add('dot');39 allDots[3].classList.add('dot');40 allDots[5].classList.add('dot');41 allDots[6].classList.add('dot');42 allDots[8].classList.add('dot');43 }44}45dice.addEventListener('click', rollDice);
Day 9: Sum Odd Fibonacci Numbers
Challenge
Given a positive integer num, return the sum of all odd Fibonacci numbers that are less than or equal to num. The first two numbers in the Fibonacci sequence are 1 and 1. Every additional number in the sequence is the sum of the two previous numbers. The first six numbers of the Fibonacci sequence are 1, 1, 2, 3, 5 and 8. For example, sumFibs(10) should return 10 because all odd Fibonacci numbers less than or equal to 10 are 1, 1, 3, and 5.
My solution (🔗)
1function sumOddFibonacciNumbers(num) {2 let a = 1,3 b = 1,4 sum = a,5 tmp;6 while (sum < num) {7 sum += b % 2 === 0 ? 0 : b;8 tmp = a;9 a = b;10 b += tmp;11 }12 return sum;13}
Official solution (🔗)
1function sumOddFibonacciNumbers(num) {2 let sum = 0;3 let previous = 0;4 let current = 1;56 while (current <= num) {7 if (current % 2 === 1) {8 sum += current;9 }1011 const nextValue = current + previous;12 previous = current;13 current = nextValue;14 }1516 return sum;17}
Day 10: Adjacent Elements Product
Challenge
Given an array of integers, find the pair of adjacent elements that has the largest product and return that product.
My solution (🔗)
1function adjacentElementsProduct(nums) {2 const products = nums.map((num, i, nums) => num * (nums[i + 1] ?? 1));3 return Math.max(...products);4}
Official solution (🔗)
1function adjacentElementsProduct(nums) {2 let largestProduct = nums[0] * nums[1];34 for (let i = 1; i < nums.length - 1; i++) {5 const adjacentProduct = nums[i] * nums[i + 1];67 if (largestProduct < adjacentProduct) {8 largestProduct = adjacentProduct;9 }10 }1112 return largestProduct;13}
Day 11: Avoid Obstacles
Challenge
You are given an array of integers representing coordinates of obstacles situated on a straight line.
Assume that you are jumping from the point with coordinate 0 to the right. You are allowed only to make jumps of the same length represented by some integer.
Find the minimal length of the jump enough to avoid all the obstacles.
My solution (🔗)
1function avoidObstacles(nums) {2 let min = 1;3 while (nums.some((num) => num % min === 0)) {4 min++;5 }6 return min;7}
Official solution (🔗)
1function avoidObstacles(nums) {2 const largestNum = nums.sort((a, b) => a - b)[nums.length - 1];34 for (let i = 1; i <= largestNum + 1; i++) {5 if (nums.every((value) => value % i !== 0)) {6 return i;7 }8 }9}
Day 12: Valid Time
Challenge
Check if the given string is a correct time representation of the 24-hour clock.
My solution (🔗)
1function validTime(str) {2 const [hour, minute] = str.split(':').map((x) => parseInt(x, 10));34 return hour >= 0 && hour < 24 && minute >= 0 && minute < 60;5}
Official solution (🔗)
1function validTime(str) {2 const [hours, minutes] = str.split(':');34 if (parseInt(hours) > 23 || parseInt(hours) < 0) {5 return false;6 }78 if (parseInt(minutes) > 59 || parseInt(minutes) < 0) {9 return false;10 }1112 return true;13}
Day 13: Extract Each Kth
Challenge
Given array of integers, remove each Kth element from it.
My solution (🔗)
1function extractEachKth(nums, index) {2 return nums.filter((num) => num % index !== 0);3}
Official solution (🔗)
1function extractEachKth(nums, index) {2 return nums.filter((value, i) => (i + 1) % index !== 0);3}
Day 14: Maximal Adjacent Difference
Challenge
Given an array of integers, find the maximal absolute difference between any two of its adjacent elements.
My solution (🔗)
1function arrayMaximalAdjacentDifference(nums) {2 const diffs = nums.map((num, i, nums) =>3 i ? Math.abs(num - nums[i - 1]) : 04 );56 return Math.max(...diffs);7}
Official solution (🔗)
1function arrayMaximalAdjacentDifference(nums) {2 let maxDifference = 0;34 for (let i = 0; i < nums.length - 1; i++) {5 const absoluteDifference = Math.abs(nums[i] - nums[i + 1]);67 if (maxDifference < absoluteDifference) {8 maxDifference = absoluteDifference;9 }10 }1112 return maxDifference;13}
Day 15: JavaScript Carousel
Note: This challenge involves additional HTML and CSS, so I recommend checking the solutions directly at Scrimba.
Challenge
Create a carousel with JavaScript.
My solution (🔗)
1const prevButton = document.querySelector('.previous');2const nextButton = document.querySelector('.next');3const gallery = document.querySelector('.gallery');4const cards = document.querySelectorAll('.card');56let currentCard = 0;7const lastCard = cards.length - 1;89const slide = (num) => {10 const prevCard = currentCard;11 const nextCard = currentCard + num;1213 currentCard = Math.max(nextCard, 0);14 currentCard = Math.min(currentCard, lastCard);1516 gallery.style = `transform: translateX(-${currentCard * 220}px);`;17 cards[prevCard].classList.remove('current');18 cards[currentCard].classList.add('current');1920 prevButton.style = 'opacity: 1';21 nextButton.style = 'opacity: 1';22 if (currentCard === 0) {23 prevButton.style = 'opacity: .3';24 } else if (currentCard === lastCard) {25 nextButton.style = 'opacity: .3';26 }27};2829nextButton.addEventListener('click', () => slide(1));30prevButton.addEventListener('click', () => slide(-1));
Official solution (🔗)
1const gallery = document.getElementsByClassName('gallery')[0];2const prevBtn = document.getElementsByClassName('previous')[0];3const nextBtn = document.getElementsByClassName('next')[0];4const galleryCardCount = document.getElementsByClassName('card').length;56let currentGalleryXOffset = 0;7const endGalleryXOffset = (galleryCardCount - 1) * -220;89prevBtn.addEventListener('click', galleryClickHandler);10nextBtn.addEventListener('click', galleryClickHandler);1112function galleryClickHandler(event) {13 let targetBtn = event.target.className;14 if (targetBtn == 'previous' && currentGalleryXOffset < 0) {15 currentGalleryXOffset += 220;16 } else if (targetBtn == 'next' && currentGalleryXOffset > endGalleryXOffset) {17 currentGalleryXOffset -= 220;18 }1920 if (currentGalleryXOffset == 0) {21 prevBtn.style.opacity = 0.3;22 prevBtn.style.cursor = 'default';23 } else {24 prevBtn.style.opacity = 1; //disabled25 prevBtn.style.cursor = 'pointer';26 }2728 if (currentGalleryXOffset == endGalleryXOffset) {29 nextBtn.style.opacity = 0.3;30 nextBtn.style.cursor = 'default';31 } else {32 nextBtn.style.opacity = 1;33 nextBtn.style.cursor = 'pointer';34 }3536 gallery.style.transform = `translateX(${currentGalleryXOffset}px)`;37}
Day 16: Insert Dashes
Challenge
Transform a given sentence into a new one with dashes between each two consecutive letters.
My solution (🔗)
1function insertDashes(arr) {2 return arr3 .split('')4 .join('-')5 .replace('- -', ' ');6}
Official solution (🔗)
1function insertDashes(str) {2 const words = str.split(' ');3 const dashedWords = words.map((word) => {4 const chars = word.split('');56 return chars.join('-');7 });89 return dashedWords.join(' ');10}
Day 17: Different symbols naive
Challenge
Given a string, find the number of different characters in it.
My solution (🔗)
1function differentSymbolsNaive(str) {2 return new Set(str.split('')).size;3}
Official solution (🔗)
1function differentSymbolsNaive(str) {2 const chars = str.split('');34 return new Set(chars).size;5}
Day 18: Array previous less
Challenge
Given array of integers, for each position i, search among the previous positions for the last (from the left) position that contains a smaller value. Store this value at position i in the answer. If no such value can be found, store -1 instead.
My solution (🔗)
1function arrayPreviousLess(nums) {2 return nums.map((num, i) => {3 const prevNum = nums[i - 1] ?? -1;4 return prevNum < num ? prevNum : -1;5 });6}
Official solution (🔗)
1function arrayPreviousLess(nums) {2 const previousLess = [];34 for (let i = nums.length - 1; i >= 0; i--) {5 for (let j = i; j >= 0; j--) {6 if (nums[i] > nums[j]) {7 previousLess.unshift(nums[j]);8 break;9 } else if (j === 0) {10 previousLess.unshift(-1);11 }12 }13 }1415 return previousLess;16}
Day 19: Alphabet Subsequence
Challenge
Check whether the given string is a subsequence of the plaintext alphabet.
My solution (🔗)
1function alphabetSubsequence(str) {2 const expectedStr = [...new Set(str.split(''))].sort().join('');3 return str === expectedStr;4}
Official solution (🔗)
1function alphabetSubsequence(str) {2 const chars = str.split('');3 const charCodes = chars.map((char) => char.charCodeAt(0));45 if (new Set(charCodes).size !== charCodes.length) {6 return false;7 }89 for (let i = 0; i < charCodes.length - 1; i++) {10 if (charCodes[i] > charCodes[i + 1]) {11 return false;12 }13 }1415 return true;16}
Day 20: Domain Type
Challenge
GoDaddy makes a lot of different top-level domains available to its customers. A top-level domain is one that goes directly after the last dot (’.’) in the domain name, for example .com in example.com. To help the users choose from available domains, GoDaddy is introducing a new feature that shows the type of the chosen top-level domain. You have to implement this feature. To begin with, you want to write a function that labels the domains as “commercial”, “organization”, “network” or “information” for .com, .org, .net or .info respectively. For the given list of domains return the list of their labels.
My solution (🔗)
1function domainType(domains) {2 const types = new Map([3 ['org', 'organization'],4 ['com', 'commercial'],5 ['net', 'network'],6 ['info', 'information'],7 ]);89 return domains.map((domain) => {10 const tld = domain11 .split('.')12 .pop()13 .toLowerCase();14 return types.has(tld) ? types.get(tld) : 'unknown';15 });16}
Official solution (🔗)
1function domainType(domains) {2 const domainTypes = [];34 for (let i = 0; i < domains.length; i++) {5 const urlPieces = domains[i].split('.');6 const domain = urlPieces[urlPieces.length - 1];78 if (domain === 'org') {9 domainTypes.push('organization');10 } else if (domain === 'com') {11 domainTypes.push('commercial');12 } else if (domain === 'net') {13 domainTypes.push('network');14 } else if (domain === 'info') {15 domainTypes.push('information');16 }17 }1819 return domainTypes;20}
Day 21: Sum of Two
Challenge
You have two integer arrays, a and b, and an integer target value v. Determine whether there is a pair of numbers, where one number is taken from a and the other from b, that can be added together to get a sum of v. Return true if such a pair exists, otherwise return false.
My solution (🔗)
1function sumOfTwo(nums1, nums2, value) {2 return nums1.some((num1) => nums2.includes(value - num1));3}
Official solution (🔗)
1function sumOfTwo(nums1, nums2, value) {2 const map = {};34 for (let num of nums1) {5 const difference = value - num;6 map[difference] = difference;7 }89 for (let num of nums2) {10 if (map.hasOwnProperty(num)) {11 return true;12 }13 }1415 return false;16}
Day 22: Extract Matrix Column
Challenge
Given a rectangular matrix and an integer column, return an array containing the elements of the columnth column of the given matrix (the leftmost column is the 0th one).
My solution (🔗)
1function extractMatrixColumn(matrix, column) {2 return matrix.map((rect) => rect[column]);3}
Official solution (🔗)
1function extractMatrixColumn(matrix, column) {2 return matrix.map((row) => row[column]);3}
Day 23: Social Media Input
Note: This challenge involves additional HTML and CSS, so I recommend checking the solutions directly at Scrimba.
Challenge
We are making a Social Media Character Counter! We want to display the available characters LEFT. Using the Keydown event should help you here. When the characters reach 20 and below, we want them to turn red. So we will use Javascript to add that styling to it. If the characters drop below 0, we want the button to be disabled BUT if there are only 0 characters left, we should still be able to tweet.
Keydown, addEventListeners, add and remove a class
My solution (🔗)
1const string = document.querySelector('#string');2const btn = document.querySelector('#btn');3const counter = document.querySelector('#counterFooter');4const MAX_LENGTH = 140;56string.addEventListener('keyup', (event) => {7 const length = event.target.value.length;8 const remaining = MAX_LENGTH - length;910 counter.innerText = `${remaining}/${MAX_LENGTH}`;11 counter.style.color = remaining <= 20 ? 'red' : 'white';1213 if (remaining < 0) {14 btn.setAttribute('disabled', 'disabled');15 btn.className = 'buttonDisabled';16 } else {17 btn.setAttribute('disabled', '');18 btn.className = '';19 }20});
Official solution
N/A
Day 24: Test your agility
Note: This challenge involves additional HTML and CSS, so I recommend checking the solutions directly at Scrimba.
Challenge
Make a counter that increments every 75 milliseconds in the spin() function and display whether the player wins or loses in the stop() function.
My solution (🔗)
1// Globals2let targetInt; // The target number to stop the wheel on3let pushed = false; // Has the stop button been pushed - false is default45// DOM Elements6const spinningElem = document.getElementById('spinning'); // The spinning number7const buttonElem = document.getElementById('buttonPressed');8const targetElem = document.getElementById('targetNum');9const resultElem = document.getElementById('result'); // Display your result message here1011// Functions12const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));1314const buttonPressed = () => (pushed = true);1516const setTargetInt = () => {17 targetInt = Math.floor(Math.random() * 101);18 targetElem.innerText = targetInt;19};2021const spin = async () => {22 let i = 0;23 while (!pushed) {24 i = i >= 100 ? 0 : i + 1;25 spinningElem.innerText = i;26 await sleep(75); // Paste this wherever you need to sleep the incrimentor27 }2829 stop(i); // Trigger this function when the STOP button has been pushed30};3132const stop = (i) => {33 const diff = Math.abs(i - targetInt);3435 if (diff === 0) {36 resultElem.innerText = `Yay! You did it!`;37 } else {38 resultElem.innerText = `Oh no, you lose! Off by ${diff}`;39 }40};4142// Event listeners43buttonElem.addEventListener('click', buttonPressed);4445// Init46setTargetInt();47spin();
Official solution (🔗)
1// javascript23//globals4var pushed = false; //Has the stop button been pushed - false is default5var targetInt; //The target number to stop the wheel on6var spinningElem = document.getElementById('spinning'); //The spinning number78//event listener9document10 .getElementById('buttonPressed')11 .addEventListener('click', buttonPressed);1213//When the stop button is pushed14function buttonPressed() {15 pushed = true;16}1718//set the target Int19function setTargetInt() {20 var targetElem = document.getElementById('targetNum');21 targetInt = Math.floor(Math.random() * 101);22 targetElem.innerHTML = targetInt;23}2425//sleep const26const sleep = (milliseconds) => {27 return new Promise((resolve) => setTimeout(resolve, milliseconds));28};2930//number spinner31const spin = async () => {32 for (var i = 0; i < 101; i++) {33 if (i == 100) {34 i = 0;35 }36 if (pushed == true) {37 stop(i); //Trigger this function when the STOP button has been pushed38 break;39 } else {40 spinningElem.innerHTML = i;41 await sleep(75); //Paste this42 }43 }44};4546function stop(i) {47 var offBy = Math.abs(targetInt - (i - 1));48 var message;4950 if (offBy == 0) {51 message = 'You Win!';52 } else {53 message = 'Oh no, you lose! Off by ' + offBy.toString();54 }55 var result = document.getElementById('result');56 result.innerHTML = message;57}5859setTargetInt();60spin();
And that’s it!
I would like to thank Scrimba for organizing this (it was a lot of fun!). And congratulations to @bhagwan_gb, who won the big prize!
See you all on #JavaScriptmas 2021!
Photo by Michael Hacker on Unsplash