Suggested Solutions
Hopefully you’ve tried out the exercises before you’re reading this far. No cheating!
Remember, each suggested solution is just one of a bunch of different ways to approach the problems. They’re not “the right answer,” but they do illustrate a reasonable way to approach each exercise.
The most important benefit you can get from reading these suggested solutions is to compare them to your code and analyze why we each made similar or different choices. Don’t get into too much bikeshedding; try to stay focused on the main topic rather than the small details.
Suggested: Buckets of Marbles
The Buckets of Marbles Exercise can be solved like this:
// RED(1)
const howMany = 100;
// Sieve of Eratosthenes
function findPrimes(howMany) {
// BLUE(2)
var sieve = Array(howMany).fill(true);
var max = Math.sqrt(howMany);
for (let i = 2; i < max; i++) {
// GREEN(3)
if (sieve[i]) {
// ORANGE(4)
let j = Math.pow(i,2);
for (let k = j; k < howMany; k += i) {
// PURPLE(5)
sieve[k] = false;
}
}
}
return sieve
.map(function getPrime(flag,prime){
// PINK(6)
if (flag) return prime;
return flag;
})
.filter(function onlyPrimes(v){
// YELLOW(7)
return !!v;
})
.slice(1);
}
findPrimes(howMany);
// [
// 2, 3, 5, 7, 11, 13, 17,
// 19, 23, 29, 31, 37, 41,
// 43, 47, 53, 59, 61, 67,
// 71, 73, 79, 83, 89, 97
// ]
Suggested: Closure (PART 1)
The Closure Exercise (PART 1) for isPrime(..)
and factorize(..)
, can be solved like this:
var isPrime = (function isPrime(v){
var primes = {};
return function isPrime(v) {
if (v in primes) {
return primes[v];
}
if (v <= 3) {
return (primes[v] = v > 1);
}
if (v % 2 == 0 || v % 3 == 0) {
return (primes[v] = false);
}
let vSqrt = Math.sqrt(v);
for (let i = 5; i <= vSqrt; i += 6) {
if (v % i == 0 || v % (i + 2) == 0) {
return (primes[v] = false);
}
}
return (primes[v] = true);
};
})();
var factorize = (function factorize(v){
var factors = {};
return function findFactors(v) {
if (v in factors) {
return factors[v];
}
if (!isPrime(v)) {
let i = Math.floor(Math.sqrt(v));
while (v % i != 0) {
i--;
}
return (factors[v] = [
...findFactors(i),
...findFactors(v / i)
]);
}
return (factors[v] = [v]);
};
})();
The general steps I used for each utility:
Wrap an IIFE to define the scope for the cache variable to reside.
In the underlying call, first check the cache, and if a result is already known, return.
At each place where a
return
was happening originally, assign to the cache and just return the results of that assignment operation—this is a space savings trick mostly just for brevity in the book.
I also renamed the inner function from factorize(..)
to findFactors(..)
. That’s not technically necessary, but it helps it make clearer which function the recursive calls invoke.
Suggested: Closure (PART 2)
The Closure Exercise (PART 2) toggle(..)
can be solved like this:
function toggle(...vals) {
var unset = {};
var cur = unset;
return function next(){
// save previous value back at
// the end of the list
if (cur != unset) {
vals.push(cur);
}
cur = vals.shift();
return cur;
};
}
var hello = toggle("hello");
var onOff = toggle("on","off");
var speed = toggle("slow","medium","fast");
hello(); // "hello"
hello(); // "hello"
onOff(); // "on"
onOff(); // "off"
onOff(); // "on"
speed(); // "slow"
speed(); // "medium"
speed(); // "fast"
speed(); // "slow"
Suggested: Closure (PART 3)
The Closure Exercise (PART 3) calculator()
can be solved like this:
// from earlier:
//
// function useCalc(..) { .. }
// function formatTotal(..) { .. }
function calculator() {
var currentTotal = 0;
var currentVal = "";
var currentOper = "=";
return pressKey;
// ********************
function pressKey(key){
// number key?
if (/\d/.test(key)) {
currentVal += key;
return key;
}
// operator key?
else if (/[+*/-]/.test(key)) {
// multiple operations in a series?
if (
currentOper != "=" &&
currentVal != ""
) {
// implied '=' keypress
pressKey("=");
}
else if (currentVal != "") {
currentTotal = Number(currentVal);
}
currentOper = key;
currentVal = "";
return key;
}
// = key?
else if (
key == "=" &&
currentOper != "="
) {
currentTotal = op(
currentTotal,
currentOper,
Number(currentVal)
);
currentOper = "=";
currentVal = "";
return formatTotal(currentTotal);
}
return "";
};
function op(val1,oper,val2) {
var ops = {
// NOTE: using arrow functions
// only for brevity in the book
"+": (v1,v2) => v1 + v2,
"-": (v1,v2) => v1 - v2,
"*": (v1,v2) => v1 * v2,
"/": (v1,v2) => v1 / v2
};
return ops[oper](val1,val2);
}
}
var calc = calculator();
useCalc(calc,"4+3="); // 4+3=7
useCalc(calc,"+9="); // +9=16
useCalc(calc,"*8="); // *5=128
useCalc(calc,"7*2*3="); // 7*2*3=42
useCalc(calc,"1/0="); // 1/0=ERR
useCalc(calc,"+3="); // +3=ERR
useCalc(calc,"51="); // 51
NOTE: |
---|
Remember: this exercise is about closure. Don’t focus too much on the actual mechanics of a calculator, but rather on whether you are properly remembering the calculator state across function calls. |
Suggested: Modules
The Modules Exercise calculator()
can be solved like this:
// from earlier:
//
// function useCalc(..) { .. }
// function formatTotal(..) { .. }
function calculator() {
var currentTotal = 0;
var currentVal = "";
var currentOper = "=";
var publicAPI = {
number,
eq,
plus() { return operator("+"); },
minus() { return operator("-"); },
mult() { return operator("*"); },
div() { return operator("/"); }
};
return publicAPI;
// ********************
function number(key) {
// number key?
if (/\d/.test(key)) {
currentVal += key;
return key;
}
}
function eq() {
// = key?
if (currentOper != "=") {
currentTotal = op(
currentTotal,
currentOper,
Number(currentVal)
);
currentOper = "=";
currentVal = "";
return formatTotal(currentTotal);
}
return "";
}
function operator(key) {
// multiple operations in a series?
if (
currentOper != "=" &&
currentVal != ""
) {
// implied '=' keypress
eq();
}
else if (currentVal != "") {
currentTotal = Number(currentVal);
}
currentOper = key;
currentVal = "";
return key;
}
function op(val1,oper,val2) {
var ops = {
// NOTE: using arrow functions
// only for brevity in the book
"+": (v1,v2) => v1 + v2,
"-": (v1,v2) => v1 - v2,
"*": (v1,v2) => v1 * v2,
"/": (v1,v2) => v1 / v2
};
return ops[oper](val1,val2);
}
}
var calc = calculator();
useCalc(calc,"4+3="); // 4+3=7
useCalc(calc,"+9="); // +9=16
useCalc(calc,"*8="); // *5=128
useCalc(calc,"7*2*3="); // 7*2*3=42
useCalc(calc,"1/0="); // 1/0=ERR
useCalc(calc,"+3="); // +3=ERR
useCalc(calc,"51="); // 51
That’s it for this book, congratulations on your achievement! When you’re ready, move on to Book 3, Objects & Classes.
[^MathJSisPrime]: Math.js: isPrime(..), https://github.com/josdejong/mathjs/blob/develop/src/function/utils/isPrime.js, 3 March 2020.