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:

  1. // RED(1)
  2. const howMany = 100;
  3. // Sieve of Eratosthenes
  4. function findPrimes(howMany) {
  5. // BLUE(2)
  6. var sieve = Array(howMany).fill(true);
  7. var max = Math.sqrt(howMany);
  8. for (let i = 2; i < max; i++) {
  9. // GREEN(3)
  10. if (sieve[i]) {
  11. // ORANGE(4)
  12. let j = Math.pow(i,2);
  13. for (let k = j; k < howMany; k += i) {
  14. // PURPLE(5)
  15. sieve[k] = false;
  16. }
  17. }
  18. }
  19. return sieve
  20. .map(function getPrime(flag,prime){
  21. // PINK(6)
  22. if (flag) return prime;
  23. return flag;
  24. })
  25. .filter(function onlyPrimes(v){
  26. // YELLOW(7)
  27. return !!v;
  28. })
  29. .slice(1);
  30. }
  31. findPrimes(howMany);
  32. // [
  33. // 2, 3, 5, 7, 11, 13, 17,
  34. // 19, 23, 29, 31, 37, 41,
  35. // 43, 47, 53, 59, 61, 67,
  36. // 71, 73, 79, 83, 89, 97
  37. // ]

Suggested: Closure (PART 1)

The Closure Exercise (PART 1) for isPrime(..) and factorize(..), can be solved like this:

  1. var isPrime = (function isPrime(v){
  2. var primes = {};
  3. return function isPrime(v) {
  4. if (v in primes) {
  5. return primes[v];
  6. }
  7. if (v <= 3) {
  8. return (primes[v] = v > 1);
  9. }
  10. if (v % 2 == 0 || v % 3 == 0) {
  11. return (primes[v] = false);
  12. }
  13. let vSqrt = Math.sqrt(v);
  14. for (let i = 5; i <= vSqrt; i += 6) {
  15. if (v % i == 0 || v % (i + 2) == 0) {
  16. return (primes[v] = false);
  17. }
  18. }
  19. return (primes[v] = true);
  20. };
  21. })();
  22. var factorize = (function factorize(v){
  23. var factors = {};
  24. return function findFactors(v) {
  25. if (v in factors) {
  26. return factors[v];
  27. }
  28. if (!isPrime(v)) {
  29. let i = Math.floor(Math.sqrt(v));
  30. while (v % i != 0) {
  31. i--;
  32. }
  33. return (factors[v] = [
  34. ...findFactors(i),
  35. ...findFactors(v / i)
  36. ]);
  37. }
  38. return (factors[v] = [v]);
  39. };
  40. })();

The general steps I used for each utility:

  1. Wrap an IIFE to define the scope for the cache variable to reside.

  2. In the underlying call, first check the cache, and if a result is already known, return.

  3. 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:

  1. function toggle(...vals) {
  2. var unset = {};
  3. var cur = unset;
  4. return function next(){
  5. // save previous value back at
  6. // the end of the list
  7. if (cur != unset) {
  8. vals.push(cur);
  9. }
  10. cur = vals.shift();
  11. return cur;
  12. };
  13. }
  14. var hello = toggle("hello");
  15. var onOff = toggle("on","off");
  16. var speed = toggle("slow","medium","fast");
  17. hello(); // "hello"
  18. hello(); // "hello"
  19. onOff(); // "on"
  20. onOff(); // "off"
  21. onOff(); // "on"
  22. speed(); // "slow"
  23. speed(); // "medium"
  24. speed(); // "fast"
  25. speed(); // "slow"

Suggested: Closure (PART 3)

The Closure Exercise (PART 3) calculator() can be solved like this:

  1. // from earlier:
  2. //
  3. // function useCalc(..) { .. }
  4. // function formatTotal(..) { .. }
  5. function calculator() {
  6. var currentTotal = 0;
  7. var currentVal = "";
  8. var currentOper = "=";
  9. return pressKey;
  10. // ********************
  11. function pressKey(key){
  12. // number key?
  13. if (/\d/.test(key)) {
  14. currentVal += key;
  15. return key;
  16. }
  17. // operator key?
  18. else if (/[+*/-]/.test(key)) {
  19. // multiple operations in a series?
  20. if (
  21. currentOper != "=" &&
  22. currentVal != ""
  23. ) {
  24. // implied '=' keypress
  25. pressKey("=");
  26. }
  27. else if (currentVal != "") {
  28. currentTotal = Number(currentVal);
  29. }
  30. currentOper = key;
  31. currentVal = "";
  32. return key;
  33. }
  34. // = key?
  35. else if (
  36. key == "=" &&
  37. currentOper != "="
  38. ) {
  39. currentTotal = op(
  40. currentTotal,
  41. currentOper,
  42. Number(currentVal)
  43. );
  44. currentOper = "=";
  45. currentVal = "";
  46. return formatTotal(currentTotal);
  47. }
  48. return "";
  49. };
  50. function op(val1,oper,val2) {
  51. var ops = {
  52. // NOTE: using arrow functions
  53. // only for brevity in the book
  54. "+": (v1,v2) => v1 + v2,
  55. "-": (v1,v2) => v1 - v2,
  56. "*": (v1,v2) => v1 * v2,
  57. "/": (v1,v2) => v1 / v2
  58. };
  59. return ops[oper](val1,val2);
  60. }
  61. }
  62. var calc = calculator();
  63. useCalc(calc,"4+3="); // 4+3=7
  64. useCalc(calc,"+9="); // +9=16
  65. useCalc(calc,"*8="); // *5=128
  66. useCalc(calc,"7*2*3="); // 7*2*3=42
  67. useCalc(calc,"1/0="); // 1/0=ERR
  68. useCalc(calc,"+3="); // +3=ERR
  69. 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:

  1. // from earlier:
  2. //
  3. // function useCalc(..) { .. }
  4. // function formatTotal(..) { .. }
  5. function calculator() {
  6. var currentTotal = 0;
  7. var currentVal = "";
  8. var currentOper = "=";
  9. var publicAPI = {
  10. number,
  11. eq,
  12. plus() { return operator("+"); },
  13. minus() { return operator("-"); },
  14. mult() { return operator("*"); },
  15. div() { return operator("/"); }
  16. };
  17. return publicAPI;
  18. // ********************
  19. function number(key) {
  20. // number key?
  21. if (/\d/.test(key)) {
  22. currentVal += key;
  23. return key;
  24. }
  25. }
  26. function eq() {
  27. // = key?
  28. if (currentOper != "=") {
  29. currentTotal = op(
  30. currentTotal,
  31. currentOper,
  32. Number(currentVal)
  33. );
  34. currentOper = "=";
  35. currentVal = "";
  36. return formatTotal(currentTotal);
  37. }
  38. return "";
  39. }
  40. function operator(key) {
  41. // multiple operations in a series?
  42. if (
  43. currentOper != "=" &&
  44. currentVal != ""
  45. ) {
  46. // implied '=' keypress
  47. eq();
  48. }
  49. else if (currentVal != "") {
  50. currentTotal = Number(currentVal);
  51. }
  52. currentOper = key;
  53. currentVal = "";
  54. return key;
  55. }
  56. function op(val1,oper,val2) {
  57. var ops = {
  58. // NOTE: using arrow functions
  59. // only for brevity in the book
  60. "+": (v1,v2) => v1 + v2,
  61. "-": (v1,v2) => v1 - v2,
  62. "*": (v1,v2) => v1 * v2,
  63. "/": (v1,v2) => v1 / v2
  64. };
  65. return ops[oper](val1,val2);
  66. }
  67. }
  68. var calc = calculator();
  69. useCalc(calc,"4+3="); // 4+3=7
  70. useCalc(calc,"+9="); // +9=16
  71. useCalc(calc,"*8="); // *5=128
  72. useCalc(calc,"7*2*3="); // 7*2*3=42
  73. useCalc(calc,"1/0="); // 1/0=ERR
  74. useCalc(calc,"+3="); // +3=ERR
  75. 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.