├── .eslintrc.js ├── .gitignore ├── README.md ├── SpecRunner.html ├── lib ├── chai.js ├── css │ └── mocha.css ├── jquery.js ├── mocha.js ├── sinon.js └── testSupport.js ├── spec ├── part1.js └── part2.js └── src └── recursion.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb", 3 | "installedESLint": true 4 | }; 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Recursion Prompts 2 | 3 | ### **What is this?** 4 | This is a repository of toy problems to be solved using recursion and JavaScript. While the concept of recursion may not be difficult to grasp, the only way to improve at thinking recursively is by practice. If you need practice, then maybe this repo is for you. 5 | 6 | ### **A few guidelines:** 7 | - Please refrain from sharing solutions. As crazy as it sounds, giving someone the answer doesn't help them. Instead, give them a question that encourages them to think differently. 8 | 9 | > **Q:** Why does my function keep exceeding the call stack? 10 | 11 | > **A:** What's your base case? 12 | 13 | - Don't be afraid to pseudocode your algorithm before writing actual code. 14 | 15 | > Pseudocode helps you focus on the algorithm instead of getting distracted by syntax. 16 | 17 | - This repo requires each function call itself recursively and pays no attention to whether inner recursive functions are defined and called. 18 | 19 | > While both are valid uses of recursion, there are important lessons to learn by following the method this repo enforces. Defining inner functions and calling them recursively relies on side effects, while following the more pure approach requires an understanding of how values are passed through the call stack. 20 | 21 | - This repo restricts expanding the number of parameters a function accepts. 22 | 23 | > Expanding the number of parameters is a valid approach, but has been restricted here to emphasize certain lessons while learning recursion. 24 | 25 | - An attempt was made to order prompts by difficulty, but they don't have to be solved in any particular order. 26 | - Feel free to make pull requests or open issues regarding bugs or suggestions. 27 | - **`Watch`**, **`Star`**, and **`Fork`** this repo. You know you want to. 28 | 29 | ### **How to use this repo:** 30 | 1. Fork this repo and clone it to your local machine 31 | 2. Open `SpecRunner.html` in your web browser 32 | 3. Code your solutions in `recursion.js` 33 | 4. Review the tests in `spec/part1.js` and `spec/part2.js` as necessary 34 | 5. Save your work and refresh your browser to check for passing/failing tests 35 | 36 | --- 37 | ### What is recursion? 38 | > Recursion is when a function calls itself until it doesn't. --not helpful person 39 | 40 | Is it a true definition? Mostly. Recursion is when a function calls itself. A recursive function can call itself forever, but that's generally not preferred. It's often a good idea to include a condition in the function definition that allows it to stop calling itself. This condition is referred to as a **_base_** case. As a general rule, recursion shouldn't be utilized without an accompanying base case unless an infinite operation is desired. This leaves us with two fundamental conditions every recursive function should include: 41 | - A **`base`** case 42 | - A **`recursive`** case 43 | 44 | _What does this all mean?_ Let's consider a silly example: 45 | ```sh 46 | function stepsToZero(n) { 47 | if (!n === 0) { /* base case */ 48 | console.log('Reached zero'); 49 | return; 50 | } else { /* recursive case */ 51 | console.log(n + ' is not zero'); 52 | return stepsToZero(n-1); 53 | } 54 | } 55 | ``` 56 | This function doesn't do anything meaningful, but hopefully it demonstrates the fundamental idea behind recursion. Simply put, recursion provides us a looping or repeating mechanism. It repeats an operation until a `base` condition is met. Let's step through an invocation of the above function to see how it evaluates. 57 | 58 | 1. Invoke `stepsToZero(n)` where `n` is the number `2` 59 | 2. Is 2 zero? 60 | 3. No, print message to console that 2 is not zero 61 | 4. Invoke `stepsToZero(n-1)` where `n-1` evaluates to `1` 62 | 63 | > Every recursive call adds a new invocation to the stack on top of the previous invocation 64 | 65 | 5. Is 1 zero? 66 | 6. No, print message that 1 is not zero 67 | 7. Invoke `stepsToZero(n-1)` where `n-1` evaluates to `0` 68 | 8. Is 0 zero? 69 | 9. Yes, print message that reached zero 70 | 10. Return out of the current invocation 71 | 6. Resume the invocation from step 4 where it left off (in-between steps 6 and 7) 72 | 6. Return out of the invocation from step 4 73 | 12. Resume the initial invocation from step 1 where it left off (in-between steps 3 and 4) 74 | 12. Return out of the initial invocation 75 | 76 | Due to the way the execution stack operates, it's as if each function invocation pauses in time when a recursive call is made. The function that pauses before a recursive call will resume once the recursive call completes. If you've seen the movie [Inception], this model may sound reminiscent to when the characters enter a person's dreams and time slowed. The difference is time doesn't actually slow with recursive invocations; rather, it's a matter of order of operations. If a new invocation enters the execution stack, that invocation must complete before the previous can continue and complete. 77 | 78 | 79 | ### Why use recursion? 80 | Recursion can be elegant, but it can also be dangerous. It some cases, recursion feels like a more natural and readable solution; in others, it ends up being contrived. In most cases, recursion can be avoided entirely and sometimes should in order to minimize the possibility of exceeding the call stack and crashing your app. But keep in mind that code readability is important. If a recursive solution reads more naturally, then it may be the best solution for the given problem. 81 | 82 | Recursion isn't unique to any one programming language. As a software engineer, you _will_ encounter recursion and it's important to understand what's happening and how to work with it. It's also important to understand why someone might use it. Recursion is often used when the depth of a thing is unknown or every element of a thing needs to be touched. For example, you might use recursion if you want to find all DOM elements with a specific class name. You may not know how deep the DOM goes and need to touch every element so that none are missed. The same can be said for traversing any structure where all possible paths need to be considered and investigated. 83 | 84 | 85 | ### Divide and Conquer 86 | Recursion is often used in _divide and conquer_ algorithms where problems can be divided into similar subproblems and conquered individually. Consider traversing a tree structure. Each branch may have its own "children" branches. Every branch is essentually just another tree which means, as long as child trees are found, we can recurse on each child. 87 | 88 | [inception]: 89 | -------------------------------------------------------------------------------- /SpecRunner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Recursion Test Suite 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /lib/css/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight: 200; 31 | } 32 | 33 | #mocha h1 a { 34 | text-decoration: none; 35 | color: inherit; 36 | } 37 | 38 | #mocha h1 a:hover { 39 | text-decoration: underline; 40 | } 41 | 42 | #mocha .suite .suite h1 { 43 | margin-top: 0; 44 | font-size: .8em; 45 | } 46 | 47 | #mocha .hidden { 48 | display: none; 49 | } 50 | 51 | #mocha h2 { 52 | font-size: 12px; 53 | font-weight: normal; 54 | cursor: pointer; 55 | } 56 | 57 | #mocha .suite { 58 | margin-left: 15px; 59 | } 60 | 61 | #mocha .test { 62 | margin-left: 15px; 63 | overflow: hidden; 64 | } 65 | 66 | #mocha .test.pending:hover h2::after { 67 | content: '(pending)'; 68 | font-family: arial, sans-serif; 69 | } 70 | 71 | #mocha .test.pass.medium .duration { 72 | background: #c09853; 73 | } 74 | 75 | #mocha .test.pass.slow .duration { 76 | background: #b94a48; 77 | } 78 | 79 | #mocha .test.pass::before { 80 | content: '✓'; 81 | font-size: 12px; 82 | display: block; 83 | float: left; 84 | margin-right: 5px; 85 | color: #00d6b2; 86 | } 87 | 88 | #mocha .test.pass .duration { 89 | font-size: 9px; 90 | margin-left: 5px; 91 | padding: 2px 5px; 92 | color: #fff; 93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 96 | -webkit-border-radius: 5px; 97 | -moz-border-radius: 5px; 98 | -ms-border-radius: 5px; 99 | -o-border-radius: 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | #mocha .test.pass.fast .duration { 104 | display: none; 105 | } 106 | 107 | #mocha .test.pending { 108 | color: #0b97c4; 109 | } 110 | 111 | #mocha .test.pending::before { 112 | content: '◦'; 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.fail { 117 | color: #c00; 118 | } 119 | 120 | #mocha .test.fail pre { 121 | color: black; 122 | } 123 | 124 | #mocha .test.fail::before { 125 | content: '✖'; 126 | font-size: 12px; 127 | display: block; 128 | float: left; 129 | margin-right: 5px; 130 | color: #c00; 131 | } 132 | 133 | #mocha .test pre.error { 134 | color: #c00; 135 | max-height: 300px; 136 | overflow: auto; 137 | } 138 | 139 | #mocha .test .html-error { 140 | overflow: auto; 141 | color: black; 142 | line-height: 1.5; 143 | display: block; 144 | float: left; 145 | clear: left; 146 | font: 12px/1.5 monaco, monospace; 147 | margin: 5px; 148 | padding: 15px; 149 | border: 1px solid #eee; 150 | max-width: 85%; /*(1)*/ 151 | max-width: -webkit-calc(100% - 42px); 152 | max-width: -moz-calc(100% - 42px); 153 | max-width: calc(100% - 42px); /*(2)*/ 154 | max-height: 300px; 155 | word-wrap: break-word; 156 | border-bottom-color: #ddd; 157 | -webkit-box-shadow: 0 1px 3px #eee; 158 | -moz-box-shadow: 0 1px 3px #eee; 159 | box-shadow: 0 1px 3px #eee; 160 | -webkit-border-radius: 3px; 161 | -moz-border-radius: 3px; 162 | border-radius: 3px; 163 | } 164 | 165 | #mocha .test .html-error pre.error { 166 | border: none; 167 | -webkit-border-radius: 0; 168 | -moz-border-radius: 0; 169 | border-radius: 0; 170 | -webkit-box-shadow: 0; 171 | -moz-box-shadow: 0; 172 | box-shadow: 0; 173 | padding: 0; 174 | margin: 0; 175 | margin-top: 18px; 176 | max-height: none; 177 | } 178 | 179 | /** 180 | * (1): approximate for browsers not supporting calc 181 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 182 | * ^^ seriously 183 | */ 184 | #mocha .test pre { 185 | display: block; 186 | float: left; 187 | clear: left; 188 | font: 12px/1.5 monaco, monospace; 189 | margin: 5px; 190 | padding: 15px; 191 | border: 1px solid #eee; 192 | max-width: 85%; /*(1)*/ 193 | max-width: -webkit-calc(100% - 42px); 194 | max-width: -moz-calc(100% - 42px); 195 | max-width: calc(100% - 42px); /*(2)*/ 196 | word-wrap: break-word; 197 | border-bottom-color: #ddd; 198 | -webkit-box-shadow: 0 1px 3px #eee; 199 | -moz-box-shadow: 0 1px 3px #eee; 200 | box-shadow: 0 1px 3px #eee; 201 | -webkit-border-radius: 3px; 202 | -moz-border-radius: 3px; 203 | border-radius: 3px; 204 | } 205 | 206 | #mocha .test h2 { 207 | position: relative; 208 | } 209 | 210 | #mocha .test a.replay { 211 | position: absolute; 212 | top: 3px; 213 | right: 0; 214 | text-decoration: none; 215 | vertical-align: middle; 216 | display: block; 217 | width: 15px; 218 | height: 15px; 219 | line-height: 15px; 220 | text-align: center; 221 | background: #eee; 222 | font-size: 15px; 223 | -webkit-border-radius: 15px; 224 | -moz-border-radius: 15px; 225 | border-radius: 15px; 226 | -webkit-transition:opacity 200ms; 227 | -moz-transition:opacity 200ms; 228 | -o-transition:opacity 200ms; 229 | transition: opacity 200ms; 230 | opacity: 0.3; 231 | color: #888; 232 | } 233 | 234 | #mocha .test:hover a.replay { 235 | opacity: 1; 236 | } 237 | 238 | #mocha-report.pass .test.fail { 239 | display: none; 240 | } 241 | 242 | #mocha-report.fail .test.pass { 243 | display: none; 244 | } 245 | 246 | #mocha-report.pending .test.pass, 247 | #mocha-report.pending .test.fail { 248 | display: none; 249 | } 250 | #mocha-report.pending .test.pass.pending { 251 | display: block; 252 | } 253 | 254 | #mocha-error { 255 | color: #c00; 256 | font-size: 1.5em; 257 | font-weight: 100; 258 | letter-spacing: 1px; 259 | } 260 | 261 | #mocha-stats { 262 | position: fixed; 263 | top: 15px; 264 | right: 10px; 265 | font-size: 12px; 266 | margin: 0; 267 | color: #888; 268 | z-index: 1; 269 | } 270 | 271 | #mocha-stats .progress { 272 | float: right; 273 | padding-top: 0; 274 | 275 | /** 276 | * Set safe initial values, so mochas .progress does not inherit these 277 | * properties from Bootstrap .progress (which causes .progress height to 278 | * equal line height set in Bootstrap). 279 | */ 280 | height: auto; 281 | -webkit-box-shadow: none; 282 | -moz-box-shadow: none; 283 | box-shadow: none; 284 | background-color: initial; 285 | } 286 | 287 | #mocha-stats em { 288 | color: black; 289 | } 290 | 291 | #mocha-stats a { 292 | text-decoration: none; 293 | color: inherit; 294 | } 295 | 296 | #mocha-stats a:hover { 297 | border-bottom: 1px solid #eee; 298 | } 299 | 300 | #mocha-stats li { 301 | display: inline-block; 302 | margin: 0 5px; 303 | list-style: none; 304 | padding-top: 11px; 305 | } 306 | 307 | #mocha-stats canvas { 308 | width: 40px; 309 | height: 40px; 310 | } 311 | 312 | #mocha code .comment { color: #ddd; } 313 | #mocha code .init { color: #2f6fad; } 314 | #mocha code .string { color: #5890ad; } 315 | #mocha code .keyword { color: #8a6343; } 316 | #mocha code .number { color: #2f6fad; } 317 | 318 | @media screen and (max-device-width: 480px) { 319 | #mocha { 320 | margin: 60px 0px; 321 | } 322 | 323 | #mocha #stats { 324 | position: absolute; 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /lib/testSupport.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | mocha.setup({ui: 'bdd'}); 5 | window.expect = chai.expect; 6 | 7 | window.onload = function() { 8 | window.mochaPhantomJS ? mochaPhantomJS.run() : mocha.run(); 9 | }; 10 | 11 | 12 | // Disabling native methods is dangerous, we should spy on them instead 13 | before(function() { 14 | sinon.spy(Array.prototype,'map'); 15 | sinon.spy(Array.prototype,'sort'); 16 | sinon.spy(Array.prototype,'reverse'); 17 | sinon.spy(Object,'assign'); 18 | sinon.spy(JSON,'stringify'); 19 | sinon.spy(JSON,'parse'); 20 | window.analyze = o => { 21 | let c = 0; 22 | for (let k in o) { 23 | typeof o[k] === 'object' && (c += analyze(o[k])); 24 | c++; 25 | } 26 | return c; 27 | }; 28 | }); 29 | 30 | afterEach(function() { 31 | Array.prototype.map.reset(); 32 | Array.prototype.sort.reset(); 33 | Array.prototype.reverse.reset(); 34 | Object.assign.reset(); 35 | JSON.stringify.reset(); 36 | JSON.parse.reset(); 37 | }); 38 | 39 | after(function() { 40 | Array.prototype.map.restore(); 41 | Array.prototype.sort.restore(); 42 | Array.prototype.reverse.restore(); 43 | Object.assign.restore(); 44 | JSON.stringify.restore(); 45 | JSON.parse.restore(); 46 | delete window.analyze; 47 | }); 48 | 49 | }()); 50 | -------------------------------------------------------------------------------- /spec/part1.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | describe('Exercises in Recursion', function() { 7 | 8 | describe('1. Factorial', function() { 9 | var originalFactorial; 10 | 11 | before(function() { 12 | originalFactorial = factorial; 13 | factorial = sinon.spy(factorial); 14 | }); 15 | 16 | afterEach(function() { 17 | factorial.reset(); 18 | }); 19 | 20 | after(function() { 21 | factorial = originalFactorial; 22 | }); 23 | 24 | it('should return a number', function() { 25 | expect(factorial(5)).to.be.a('number'); 26 | }); 27 | 28 | it('should return factorial for non-negative integers', function() { 29 | expect(factorial(0)).to.equal(1); 30 | expect(factorial(1)).to.equal(1); 31 | expect(factorial(4)).to.equal(24); 32 | expect(factorial(5)).to.equal(120); 33 | }); 34 | 35 | it('should return null for negative integers', function() { 36 | expect(factorial(-5)).to.be.null; 37 | }); 38 | 39 | it('should use recursion by calling self', function() { 40 | factorial(4); 41 | expect(factorial.callCount).to.be.above(1); 42 | }); 43 | 44 | it('should be invoked with one argument', function() { 45 | factorial(4); 46 | factorial.args.forEach(arg => { 47 | expect(arg).to.have.length(1); 48 | }); 49 | }); 50 | 51 | }); 52 | 53 | 54 | 55 | describe('2. Sum of Integers', function() { 56 | var originalSum; 57 | 58 | before(function() { 59 | originalSum = sum; 60 | sum = sinon.spy(sum); 61 | }); 62 | 63 | afterEach(function() { 64 | sum.reset(); 65 | }); 66 | 67 | after(function() { 68 | sum = originalSum; 69 | }); 70 | 71 | it('should return a number', function() { 72 | expect(sum([1,2,3,4,5,6])).to.be.a('number'); 73 | }); 74 | 75 | it('should return the sum of an array of non-negative integers', function() { 76 | expect(sum([1,2,3,4,5,6])).to.equal(21); 77 | expect(sum([3,0,34,7,18])).to.equal(62); 78 | }); 79 | 80 | it('should return the sum of an array of negative integers', function() { 81 | expect(sum([-1,-2,-3,-4,-5,-6])).to.equal(-21); 82 | expect(sum([-3,-0,-34,-7,-18])).to.equal(-62); 83 | }); 84 | 85 | it('should return the sum of an array of mixed non-negative and negative integers', function() { 86 | expect(sum([1,-2,3,-4,5,-6])).to.equal(-3); 87 | expect(sum([-12,34,-56,78])).to.equal(44); 88 | expect(sum([3,0,-34,-7,18])).to.equal(-20); 89 | }); 90 | 91 | it('should return 0 for empty array', function() { 92 | expect(sum([])).to.equal(0); 93 | }); 94 | 95 | it('should accept an array with a single integer', function() { 96 | expect(sum([4])).to.equal(4); 97 | expect(sum([0])).to.equal(0); 98 | expect(sum([-37])).to.equal(-37); 99 | }); 100 | 101 | it('should not mutate the input array', function() { 102 | var input = [1,2,3,4,5]; 103 | sum(input); 104 | expect(input).to.eql([1,2,3,4,5]); 105 | }); 106 | 107 | it('should use recursion by calling self', function() { 108 | sum([1,2,3,4,5,6]); 109 | expect(sum.callCount).to.be.above(1); 110 | }); 111 | 112 | it('should be invoked with one argument', function() { 113 | sum([1,2,3,4,5,6]); 114 | sum.args.forEach(arg => { 115 | expect(arg).to.have.length(1); 116 | }); 117 | }); 118 | 119 | }); 120 | 121 | 122 | 123 | describe('3. Sum Integers in Array', function() { 124 | var originalArraySum; 125 | 126 | before(function() { 127 | originalArraySum = arraySum; 128 | arraySum = sinon.spy(arraySum); 129 | }); 130 | 131 | afterEach(function() { 132 | arraySum.reset(); 133 | }); 134 | 135 | after(function() { 136 | arraySum = originalArraySum; 137 | }); 138 | 139 | it('should return a number', function() { 140 | expect(arraySum([[1],[[2]],3,4])).to.be.a('number'); 141 | }); 142 | 143 | it('should return the sum of nested arrays containing non-negative integers', function() { 144 | expect(arraySum([[1],[2,3],[[4]],5])).to.equal(15); 145 | expect(arraySum([[12,[[34],[56]],78]])).to.equal(180); 146 | expect(arraySum([3,[0,[34,[7,[18]]]]])).to.equal(62); 147 | }); 148 | 149 | it('should return the sum of nested arrays containing negative integers', function() { 150 | expect(arraySum([[-1],[-2,-3],[[-4]],-5])).to.equal(-15); 151 | expect(arraySum([[-12,[[-34],[-56]],-78]])).to.equal(-180); 152 | expect(arraySum([-3,[0,[-34,[-7,[-18]]]]])).to.equal(-62); 153 | }); 154 | 155 | it('should return the sum of nested arrays containing both non-negative and negative integers', function() { 156 | expect(arraySum([[1],[-2,3],[[-4]],5,-6])).to.equal(-3); 157 | expect(arraySum([[-12,[[34],[-56]],78]])).to.equal(44); 158 | expect(arraySum([3,[0,[-34,[-7,[18]]]]])).to.equal(-20); 159 | }); 160 | 161 | it('should return 0 for empty array', function() { 162 | expect(arraySum([])).to.equal(0); 163 | }); 164 | 165 | it('should accept an array with a single integer', function() { 166 | expect(arraySum([4])).to.equal(4); 167 | expect(arraySum([0])).to.equal(0); 168 | expect(arraySum([-37])).to.equal(-37); 169 | }); 170 | 171 | it('should not mutate the input array', function() { 172 | var input = [[1],[[2]],3,4]; 173 | arraySum(input); 174 | expect(input).to.eql([[1],[[2]],3,4]); 175 | }); 176 | 177 | it('should use recursion by calling self', function() { 178 | arraySum([[1],[[2]],3,4]); 179 | expect(arraySum.callCount).to.be.above(1); 180 | }); 181 | 182 | it('should be invoked with one argument', function() { 183 | arraySum([[1],[[2]],3,4]); 184 | arraySum.args.forEach(arg => { 185 | expect(arg).to.have.length(1); 186 | }); 187 | }); 188 | 189 | }); 190 | 191 | 192 | 193 | describe('4. Check if Even', function() { 194 | var originalIsEven; 195 | 196 | before(function() { 197 | originalIsEven = isEven; 198 | isEven = sinon.spy(isEven); 199 | }); 200 | 201 | afterEach(function() { 202 | isEven.reset(); 203 | }); 204 | 205 | after(function() { 206 | isEven = originalIsEven; 207 | }); 208 | 209 | it('should return a boolean', function() { 210 | expect(isEven(5)).to.be.a('boolean'); 211 | expect(isEven(8)).to.be.a('boolean'); 212 | }); 213 | 214 | it("should not use modulo", function() { 215 | var stringified = originalIsEven.toString(); 216 | expect(stringified).to.not.contain('%'); 217 | expect(stringified).to.not.contain('modulo'); 218 | }); 219 | 220 | it('should return true for even numbers', function() { 221 | expect(isEven(48)).to.be.true; 222 | expect(isEven(0)).to.be.true; 223 | }); 224 | 225 | it('should return false for odd numbers', function() { 226 | expect(isEven(17)).to.be.false; 227 | expect(isEven(1)).to.be.false; 228 | }); 229 | 230 | it('should work with negative integers', function() { 231 | expect(isEven(-14)).to.be.true; 232 | expect(isEven(-31)).to.be.false; 233 | }); 234 | 235 | it('should use recursion by calling self', function() { 236 | isEven(8); 237 | expect(isEven.callCount).to.be.above(1); 238 | }); 239 | 240 | it('should be invoked with one argument', function() { 241 | isEven(8); 242 | isEven.args.forEach(arg => { 243 | expect(arg).to.have.length(1); 244 | }); 245 | }); 246 | 247 | }); 248 | 249 | 250 | 251 | describe('5. Sum Below', function() { 252 | var originalSumBelow; 253 | 254 | before(function() { 255 | originalSumBelow = sumBelow; 256 | sumBelow = sinon.spy(sumBelow); 257 | }); 258 | 259 | afterEach(function() { 260 | sumBelow.reset(); 261 | }); 262 | 263 | after(function() { 264 | sumBelow = originalSumBelow; 265 | }); 266 | 267 | it('should return a number', function() { 268 | expect(sumBelow(10)).to.be.a('number'); 269 | }); 270 | 271 | it('should return the sum of non-negative integers below given integer', function() { 272 | expect(sumBelow(0)).to.equal(0); 273 | expect(sumBelow(1)).to.equal(0); 274 | expect(sumBelow(2)).to.equal(1); 275 | expect(sumBelow(7)).to.equal(21); 276 | expect(sumBelow(12)).to.equal(66); 277 | }); 278 | 279 | it('should return the sum of an array of negative integers', function() { 280 | expect(sumBelow(-1)).to.equal(0); 281 | expect(sumBelow(-2)).to.equal(-1); 282 | expect(sumBelow(-6)).to.equal(-15); 283 | expect(sumBelow(-11)).to.equal(-55); 284 | }); 285 | 286 | it('should use recursion by calling self', function() { 287 | sumBelow(5); 288 | expect(sumBelow.callCount).to.be.above(1); 289 | }); 290 | 291 | it('should be invoked with one argument', function() { 292 | sumBelow(5); 293 | sumBelow.args.forEach(arg => { 294 | expect(arg).to.have.length(1); 295 | }); 296 | }); 297 | 298 | }); 299 | 300 | 301 | 302 | describe('6. Integer Range', function() { 303 | var originalRange; 304 | 305 | before(function() { 306 | originalRange = range; 307 | range = sinon.spy(range); 308 | }); 309 | 310 | afterEach(function() { 311 | range.reset(); 312 | }); 313 | 314 | after(function() { 315 | range = originalRange; 316 | }); 317 | 318 | it('should return an array', function() { 319 | expect(range(2,7)).to.be.an('array'); 320 | }); 321 | 322 | it('should return the integers between two numbers', function() { 323 | expect(range(3,8)).to.eql([4,5,6,7]); 324 | expect(range(127,131)).to.eql([128,129,130]); 325 | }); 326 | 327 | it('should return empty array if no integers in range', function() { 328 | expect(range(5,5)).to.eql([]); 329 | expect(range(2,3)).to.eql([]); 330 | }); 331 | 332 | it('should accept negative integers', function() { 333 | expect(range(-9,-4)).to.eql([-8,-7,-6,-5]); 334 | expect(range(-3,2)).to.eql([-2,-1,0,1]); 335 | expect(range(-3,-2)).to.eql([]); 336 | expect(range(-2,-2)).to.eql([]); 337 | }); 338 | 339 | it('should accept starting integer that\'s larger than ending', function() { 340 | expect(range(7,2)).to.eql([6,5,4,3]); 341 | expect(range(3,-3)).to.eql([2,1,0,-1,-2]); 342 | expect(range(-9,-4)).to.eql([-8,-7,-6,-5]); 343 | }); 344 | 345 | it('should use recursion by calling self', function() { 346 | range(3,8); 347 | expect(range.callCount).to.be.above(1); 348 | }); 349 | 350 | it('should be invoked with two arguments', function() { 351 | range(3,8); 352 | range.args.forEach(arg => { 353 | expect(arg).to.have.length(2); 354 | }); 355 | }); 356 | 357 | }); 358 | 359 | 360 | 361 | describe('7. Compute Exponent', function() { 362 | var originalExponent; 363 | 364 | before(function() { 365 | originalExponent = exponent; 366 | exponent = sinon.spy(exponent); 367 | }); 368 | 369 | afterEach(function() { 370 | exponent.reset(); 371 | }); 372 | 373 | after(function() { 374 | exponent = originalExponent; 375 | }); 376 | 377 | it('should return a number', function() { 378 | expect(exponent(4,3)).to.be.a('number'); 379 | }); 380 | 381 | it("should not use complex math", function() { 382 | expect(originalExponent.toString()).to.not.contain('Math'); 383 | }); 384 | 385 | it('should compute exponent of non-negative integers', function() { 386 | expect(exponent(3,4)).to.equal(81); 387 | expect(exponent(12,5)).to.equal(248832); 388 | expect(exponent(7,2)).to.equal(49); 389 | }); 390 | 391 | it('returns 1 when exponent is 0', function() { 392 | expect(exponent(8,0)).to.equal(1); 393 | expect(exponent(244,0)).to.equal(1); 394 | }); 395 | 396 | it('returns base when exponent is 1', function() { 397 | expect(exponent(9,1)).to.equal(9); 398 | expect(exponent(2300,1)).to.equal(2300); 399 | }); 400 | 401 | 402 | it('should accept negative integer for exponent', function() { 403 | expect(exponent(4,-2)).to.equal(0.0625); 404 | expect(exponent(5,-4)).to.equal(0.0016); 405 | expect(exponent(2,-5)).to.equal(0.03125); 406 | }); 407 | 408 | it('should use recursion by calling self', function() { 409 | exponent(3,4); 410 | expect(exponent.callCount).to.be.above(1); 411 | }); 412 | 413 | it('should be invoked with two arguments', function() { 414 | exponent(3,4); 415 | exponent.args.forEach(arg => { 416 | expect(arg).to.have.length(2); 417 | }); 418 | }); 419 | 420 | // remove the 'x' to enable test 421 | xit('optimize for even numbers', function() { 422 | exponent(3,4); 423 | expect(exponent.callCount).to.be.at.most(4); 424 | 425 | exponent.reset(); 426 | exponent(12,5); 427 | expect(exponent.callCount).to.be.at.most(5); 428 | 429 | exponent.reset(); 430 | exponent(19,7); 431 | expect(exponent.callCount).to.be.at.most(6); 432 | }); 433 | 434 | // remove the 'x' to enable test 435 | xit('should accept negative integer for base', function() { 436 | expect(exponent(-3,4)).to.equal(-81); 437 | expect(exponent(-12,5)).to.equal(-248832); 438 | expect(exponent(-7,2)).to.equal(-49); 439 | expect(exponent(-7,4)).to.equal(-2401); 440 | }); 441 | 442 | }); 443 | 444 | 445 | 446 | describe('8. Power of Two', function() { 447 | var originalPowerOfTwo; 448 | 449 | before(function() { 450 | originalPowerOfTwo = powerOfTwo; 451 | powerOfTwo = sinon.spy(powerOfTwo); 452 | }); 453 | 454 | afterEach(function() { 455 | powerOfTwo.reset(); 456 | }); 457 | 458 | after(function() { 459 | powerOfTwo = originalPowerOfTwo; 460 | }); 461 | 462 | it('should return a boolean', function() { 463 | expect(powerOfTwo(5)).to.be.a('boolean'); 464 | expect(powerOfTwo(8)).to.be.a('boolean'); 465 | }); 466 | 467 | it('should return true for powers of two', function() { 468 | expect(powerOfTwo(1)).to.be.true; 469 | expect(powerOfTwo(2)).to.be.true; 470 | expect(powerOfTwo(128)).to.be.true; 471 | }); 472 | 473 | it('should return false when input is not power of two', function() { 474 | expect(powerOfTwo(0)).to.be.false; 475 | expect(powerOfTwo(10)).to.be.false; 476 | expect(powerOfTwo(270)).to.be.false; 477 | }); 478 | 479 | it('should use recursion by calling self', function() { 480 | powerOfTwo(16); 481 | expect(powerOfTwo.callCount).to.be.above(1); 482 | }); 483 | 484 | it('should be invoked with one argument', function() { 485 | powerOfTwo(16); 486 | powerOfTwo.args.forEach(arg => { 487 | expect(arg).to.have.length(1); 488 | }); 489 | }); 490 | 491 | }); 492 | 493 | 494 | 495 | describe('9. Reverse String', function() { 496 | var originalReverse; 497 | 498 | before(function() { 499 | originalReverse = reverse; 500 | reverse = sinon.spy(reverse); 501 | }); 502 | 503 | afterEach(function() { 504 | reverse.reset(); 505 | }); 506 | 507 | after(function() { 508 | reverse = originalReverse; 509 | }); 510 | 511 | it('should return a string', function() { 512 | expect(reverse('traf')).to.be.a('string'); 513 | }); 514 | 515 | it('should return a string in reverse', function() { 516 | var input = 'All my base are belong to you.'; 517 | var tupni = '.uoy ot gnoleb era esab ym llA'; 518 | expect(reverse(input)).to.equal(tupni); 519 | }); 520 | 521 | it('should not use native reverse method', function() { 522 | // Spying on Array.prototype.reverse in testSupport.js 523 | reverse('traf'); 524 | expect(Array.prototype.reverse.called).to.be.false; 525 | }); 526 | 527 | it('should use recursion by calling self', function() { 528 | reverse('orangutan'); 529 | expect(reverse.callCount).to.be.above(1); 530 | }); 531 | 532 | it('should be invoked with one argument', function() { 533 | reverse('orangutan'); 534 | reverse.args.forEach(arg => { 535 | expect(arg).to.have.length(1); 536 | }); 537 | }); 538 | 539 | }); 540 | 541 | 542 | 543 | describe('10. Palindrome', function() { 544 | var originalPalindrome; 545 | 546 | before(function() { 547 | originalPalindrome = palindrome; 548 | palindrome = sinon.spy(palindrome); 549 | }); 550 | 551 | afterEach(function() { 552 | palindrome.reset(); 553 | }); 554 | 555 | after(function() { 556 | palindrome = originalPalindrome; 557 | }); 558 | 559 | it('should return a boolean', function() { 560 | expect(palindrome('rotor')).to.be.a('boolean'); 561 | expect(palindrome('motor')).to.be.a('boolean'); 562 | }); 563 | 564 | it('should not use native reverse method', function() { 565 | palindrome('hannah'); 566 | expect(Array.prototype.reverse.called).to.be.false; 567 | }); 568 | 569 | it('should return true for palindromes', function() { 570 | expect(palindrome('o')).to.be.true; 571 | expect(palindrome('racecar')).to.be.true; 572 | expect(palindrome('saippuakivikauppias')).to.be.true; 573 | }); 574 | 575 | it('should return false for non-palindromes', function() { 576 | expect(palindrome('hi')).to.be.false; 577 | expect(palindrome('orangutan')).to.be.false; 578 | }); 579 | 580 | it('should ignore spaces and capital letters', function() { 581 | expect(palindrome('Rotor')).to.be.true; 582 | expect(palindrome('sAip puaki v iKaup Pias')).to.be.true; 583 | }); 584 | 585 | it('should use recursion by calling self', function() { 586 | palindrome('racecar'); 587 | expect(palindrome.callCount).to.be.above(1); 588 | }); 589 | 590 | it('should be invoked with one argument', function() { 591 | palindrome('racecar'); 592 | palindrome.args.forEach(arg => { 593 | expect(arg).to.have.length(1); 594 | }); 595 | }); 596 | 597 | }); 598 | 599 | 600 | 601 | describe('11. Modulo', function() { 602 | var originalModulo; 603 | 604 | before(function() { 605 | originalModulo = modulo; 606 | modulo = sinon.spy(modulo); 607 | }); 608 | 609 | afterEach(function() { 610 | modulo.reset(); 611 | }); 612 | 613 | after(function() { 614 | modulo = originalModulo; 615 | }); 616 | 617 | it('should return a number', function() { 618 | expect(modulo(5,2)).to.be.a('number'); 619 | expect(modulo(8,4)).to.be.a('number'); 620 | }); 621 | 622 | it("should not use complex math", function() { 623 | var stringified = originalModulo.toString(); 624 | expect(stringified).to.not.contain('*'); 625 | expect(stringified).to.not.contain('/'); 626 | expect(stringified).to.not.contain('%'); 627 | expect(stringified).to.not.contain('Math'); 628 | }); 629 | 630 | it('should return the remainder of two integers', function() { 631 | expect(modulo(2, 1)).to.equal(2 % 1); 632 | expect(modulo(17, 5)).to.equal(17 % 5); 633 | expect(modulo(78, 453)).to.equal(78 % 453); 634 | expect(modulo(0, 32)).to.equal(0 % 32); 635 | expect(modulo(0, 0)).to.be.NaN; 636 | }); 637 | 638 | it('should accept negative integers', function() { 639 | expect(modulo(-79, 82)).to.equal(-79 % 82); 640 | expect(modulo(-275, -502)).to.equal(-275 % -502); 641 | expect(modulo(-275, -274)).to.equal(-275 % -274); 642 | expect(modulo(-4, 2)).to.equal(-4 % 2); 643 | }); 644 | 645 | it('should use recursion by calling self', function() { 646 | modulo(5,2); 647 | expect(modulo.callCount).to.be.above(1); 648 | }); 649 | 650 | it('should be invoked with two arguments', function() { 651 | modulo(5,2); 652 | modulo.args.forEach(arg => { 653 | expect(arg).to.have.length(2); 654 | }); 655 | }); 656 | 657 | }); 658 | 659 | 660 | 661 | describe('12. Multiply', function() { 662 | var originalMultiply; 663 | 664 | before(function() { 665 | originalMultiply = multiply; 666 | multiply = sinon.spy(multiply); 667 | }); 668 | 669 | afterEach(function() { 670 | multiply.reset(); 671 | }); 672 | 673 | after(function() { 674 | multiply = originalMultiply; 675 | }); 676 | 677 | it('should return a number', function() { 678 | expect(multiply(5,2)).to.be.a('number'); 679 | expect(multiply(8,4)).to.be.a('number'); 680 | }); 681 | 682 | it("should not use complex math", function() { 683 | var stringified = originalMultiply.toString(); 684 | expect(stringified).to.not.contain('*'); 685 | expect(stringified).to.not.contain('/'); 686 | expect(stringified).to.not.contain('%'); 687 | expect(stringified).to.not.contain('Math'); 688 | expect(stringified).to.not.contain('modulo'); 689 | }); 690 | 691 | it('should return the product of two positive integers', function() { 692 | expect(multiply(1, 2)).to.equal(1 * 2); 693 | expect(multiply(17, 5)).to.equal(17 * 5); 694 | expect(multiply(0, 32)).to.equal(0 * 32); 695 | expect(multiply(0, 0)).to.equal(0 * 0); 696 | // expect(multiply(78, 453)).to.equal(78 * 453); 697 | }); 698 | 699 | it('should return the product of two negative integers', function() { 700 | expect(multiply(-2, -2)).to.equal(-2 * -2); 701 | expect(multiply(-8, -3)).to.equal(-8 * -3); 702 | expect(multiply(-5, -27)).to.equal(-5 * -27); 703 | // expect(multiply(-79, -82)).to.equal(-79 * -82); 704 | // expect(multiply(-275, -502)).to.equal(-275 * -502); 705 | // expect(multiply(-12, -10)).to.equal(-12 * -10); 706 | // expect(multiply(-22, -3)).to.equal(-22 * -3); 707 | }); 708 | 709 | it('should return the product of mixed positive and negative integers', function() { 710 | expect(multiply(-79, 82)).to.equal(-79 * 82); 711 | expect(multiply(79, -82)).to.equal(79 * -82); 712 | expect(multiply(2, -2)).to.equal(2 * -2); 713 | expect(multiply(5, -27)).to.equal(5 * -27); 714 | // expect(multiply(-275, 502)).to.equal(-275 * 502); 715 | // expect(multiply(275, -502)).to.equal(275 * -502); 716 | // expect(multiply(-8, 3)).to.equal(-8 * 3); 717 | // expect(multiply(12, -10)).to.equal(12 * -10); 718 | // expect(multiply(-22, 3)).to.equal(-22 * 3); 719 | }); 720 | 721 | it('should accept parameters in any order', function() { 722 | expect(multiply(2, 1)).to.equal(1 * 2); 723 | expect(multiply(5, 17)).to.equal(5 * 17); 724 | expect(multiply(32, 0)).to.equal(0 * 32); 725 | // expect(multiply(453, 78)).to.equal(78 * 453); 726 | }); 727 | 728 | it('should use recursion by calling self', function() { 729 | multiply(8,4); 730 | expect(multiply.callCount).to.be.above(1); 731 | }); 732 | 733 | it('should be invoked with two arguments', function() { 734 | multiply(8,4); 735 | multiply.args.forEach(arg => { 736 | expect(arg).to.have.length(2); 737 | }); 738 | }); 739 | 740 | }); 741 | 742 | 743 | 744 | describe('13. Divide', function() { 745 | var originalDivide; 746 | 747 | before(function() { 748 | originalDivide = divide; 749 | divide = sinon.spy(divide); 750 | }); 751 | 752 | afterEach(function() { 753 | divide.reset(); 754 | }); 755 | 756 | after(function() { 757 | divide = originalDivide; 758 | }); 759 | 760 | it('should return a number', function() { 761 | expect(divide(5,2)).to.be.a('number'); 762 | expect(divide(8,4)).to.be.a('number'); 763 | }); 764 | 765 | it("should not use complex math", function() { 766 | var stringified = originalDivide.toString(); 767 | expect(stringified).to.not.contain('*'); 768 | expect(stringified).to.not.contain('/'); 769 | expect(stringified).to.not.contain('%'); 770 | expect(stringified).to.not.contain('Math'); 771 | expect(stringified).to.not.contain('modulo'); 772 | }); 773 | 774 | it('should return the quotient of two integers', function() { 775 | expect(divide(2, 1)).to.equal(~~(2 / 1)); 776 | expect(divide(17, 5)).to.equal(~~(17 / 5)); 777 | expect(divide(78, 453)).to.equal(~~(78 / 453)); 778 | expect(divide(-79, 82)).to.equal(~~(-79 / 82)); 779 | expect(divide(-275, -582)).to.equal(~~(-275 / -582)); 780 | expect(divide(0, 32)).to.equal(~~(0 / 32)); 781 | expect(divide(0, 0)).to.be.NaN; 782 | }); 783 | 784 | it('should use recursion by calling self', function() { 785 | divide(17, 5); 786 | expect(divide.callCount).to.be.above(1); 787 | }); 788 | 789 | it('should be invoked with two arguments', function() { 790 | divide(8,4); 791 | divide.args.forEach(arg => { 792 | expect(arg).to.have.length(2); 793 | }); 794 | }); 795 | 796 | }); 797 | 798 | 799 | 800 | describe('14. Greatest Common Divisor', function() { 801 | var originalGcd; 802 | 803 | before(function() { 804 | originalGcd = gcd; 805 | gcd = sinon.spy(gcd); 806 | }); 807 | 808 | afterEach(function() { 809 | gcd.reset(); 810 | }); 811 | 812 | after(function() { 813 | gcd = originalGcd; 814 | }); 815 | 816 | it('should return a number', function() { 817 | expect(gcd(4,36)).to.be.a('number'); 818 | }); 819 | 820 | it('should return greatest common divisor of two positive integers', function() { 821 | expect(gcd(4,36)).to.equal(4); 822 | expect(gcd(24,88)).to.equal(8); 823 | expect(gcd(339,17)).to.equal(1); 824 | expect(gcd(126,900)).to.equal(18); 825 | }); 826 | 827 | it('should return null for negative integers', function() { 828 | expect(gcd(-4, 2)).to.be.null; 829 | expect(gcd(-5, 5)).to.be.null; 830 | expect(gcd(5, -5)).to.be.null; 831 | expect(gcd(7, -36)).to.be.null; 832 | expect(gcd(-10, -58)).to.be.null; 833 | expect(gcd(-92, -5)).to.be.null; 834 | // expect(gcd(0, 0)).to.be.null; 835 | // expect(gcd(0, 5)).to.be.null; 836 | // expect(gcd(5, 0)).to.be.null; 837 | // expect(gcd(-5, 0)).to.be.null; 838 | // expect(gcd(0, -5)).to.be.null; 839 | }); 840 | 841 | it('should use recursion by calling self', function() { 842 | gcd(17, 5); 843 | expect(gcd.callCount).to.be.above(1); 844 | }); 845 | 846 | it('should be invoked with two arguments', function() { 847 | gcd(17,5); 848 | gcd.args.forEach(arg => { 849 | expect(arg).to.have.length(2); 850 | }); 851 | }); 852 | 853 | }); 854 | 855 | 856 | 857 | describe('15. Compare Strings', function() { 858 | var originalCompareStr; 859 | 860 | before(function() { 861 | originalCompareStr = compareStr; 862 | compareStr = sinon.spy(compareStr); 863 | }); 864 | 865 | afterEach(function() { 866 | compareStr.reset(); 867 | }); 868 | 869 | after(function() { 870 | compareStr = originalCompareStr; 871 | }); 872 | 873 | it('should return a boolean', function() { 874 | expect(compareStr('house', 'houses')).to.be.a('boolean'); 875 | expect(compareStr('', '')).to.be.a('boolean'); 876 | expect(compareStr('tomato', 'tomato')).to.be.a('boolean'); 877 | }); 878 | 879 | it('should return true for identical strings', function() { 880 | expect(compareStr('house', 'houses')).to.be.false; 881 | expect(compareStr('', '')).to.be.true; 882 | expect(compareStr('tomato', 'tomato')).to.be.true; 883 | expect(compareStr('', 'pop')).to.be.false; 884 | expect(compareStr('foot', '')).to.be.false; 885 | expect(compareStr('big dog', 'big dog')).to.be.true; 886 | }); 887 | 888 | it('should use recursion by calling self', function() { 889 | compareStr('house', 'houses'); 890 | expect(compareStr.callCount).to.be.above(1); 891 | }); 892 | 893 | it('should be invoked with two arguments', function() { 894 | compareStr('house', 'houses'); 895 | compareStr.args.forEach(arg => { 896 | expect(arg).to.have.length(2); 897 | }); 898 | }); 899 | 900 | }); 901 | 902 | 903 | 904 | describe('16. Create array from string', function() { 905 | var originalCreateArray; 906 | 907 | before(function() { 908 | originalCreateArray = createArray; 909 | createArray = sinon.spy(createArray); 910 | }); 911 | 912 | afterEach(function() { 913 | createArray.reset(); 914 | }); 915 | 916 | after(function() { 917 | createArray = originalCreateArray; 918 | }); 919 | 920 | it('should return an array', function() { 921 | expect(createArray('hello')).to.be.an('array'); 922 | }); 923 | 924 | it('should return an array where each index is a letter of the string', function() { 925 | expect(createArray('this is not a pipe')).to.eql(['t','h','i','s',' ','i','s',' ','n','o','t',' ','a',' ','p','i','p','e']); 926 | expect(createArray('hologram')).to.eql(['h','o','l','o','g','r','a','m']); 927 | expect(createArray('i')).to.eql(['i']); 928 | }); 929 | 930 | it('should use recursion by calling self', function() { 931 | createArray('hello'); 932 | expect(createArray.callCount).to.be.above(1); 933 | }); 934 | 935 | it('should be invoked with one argument', function() { 936 | createArray('hello'); 937 | createArray.args.forEach(arg => { 938 | expect(arg).to.have.length(1); 939 | }); 940 | }); 941 | 942 | }); 943 | 944 | 945 | 946 | describe('17. Reverse an array', function() { 947 | var originalReverseArr; 948 | 949 | before(function() { 950 | originalReverseArr = reverseArr; 951 | reverseArr = sinon.spy(reverseArr); 952 | }); 953 | 954 | afterEach(function() { 955 | reverseArr.reset(); 956 | }); 957 | 958 | after(function() { 959 | reverseArr = originalReverseArr; 960 | }); 961 | 962 | it('should return an array', function() { 963 | expect(reverseArr([3,2,1])).to.be.an('array'); 964 | }); 965 | 966 | it('should return array in reversed order', function() { 967 | expect(reverseArr([1,2,3,4,5])).to.eql([5,4,3,2,1]); 968 | expect(reverseArr([8,6,4,2])).to.eql([2,4,6,8]); 969 | }); 970 | 971 | it('should use recursion by calling self', function() { 972 | reverseArr([3,2,1]); 973 | expect(reverseArr.callCount).to.be.above(1); 974 | }); 975 | 976 | it('should be invoked with one argument', function() { 977 | reverseArr([5,4,3]); 978 | reverseArr.args.forEach(arg => { 979 | expect(arg).to.have.length(1); 980 | }); 981 | }); 982 | 983 | }); 984 | 985 | 986 | 987 | describe('18. Build an array with a given value and length', function() { 988 | var originalBuildList; 989 | 990 | before(function() { 991 | originalBuildList = buildList; 992 | buildList = sinon.spy(buildList); 993 | }); 994 | 995 | afterEach(function() { 996 | buildList.reset(); 997 | }); 998 | 999 | after(function() { 1000 | buildList = originalBuildList; 1001 | }); 1002 | 1003 | it('should return an array', function() { 1004 | expect(buildList(0,5)).to.be.an('array'); 1005 | }); 1006 | 1007 | it('should return array of given length with given value at each index', function() { 1008 | expect(buildList(0, 5)).to.eql([0,0,0,0,0]); 1009 | expect(buildList('banana', 3)).to.eql(['banana','banana','banana']); 1010 | expect(buildList(NaN, 4)).to.eql([NaN, NaN, NaN, NaN]); 1011 | expect(buildList(undefined, 1)).to.eql([undefined]); 1012 | expect(buildList([], 2)).to.eql([[],[]]); 1013 | expect(buildList({}, 4)).to.eql([{},{},{},{}]); 1014 | expect(buildList(true, 3)).to.eql([true,true,true]); 1015 | }); 1016 | 1017 | it('should use recursion by calling self', function() { 1018 | buildList(2,4); 1019 | expect(buildList.callCount).to.be.above(1); 1020 | }); 1021 | 1022 | it('should be invoked with two arguments', function() { 1023 | buildList('five',3); 1024 | buildList.args.forEach(arg => { 1025 | expect(arg).to.have.length(2); 1026 | }); 1027 | }); 1028 | 1029 | }); 1030 | 1031 | 1032 | 1033 | describe('19. FizzBuzz', function() { 1034 | var originalFizzBuzz, actualResult, expectedResult; 1035 | 1036 | before(function() { 1037 | originalFizzBuzz = fizzBuzz; 1038 | fizzBuzz = sinon.spy(fizzBuzz); 1039 | }); 1040 | 1041 | afterEach(function() { 1042 | fizzBuzz.reset(); 1043 | }); 1044 | 1045 | after(function() { 1046 | fizzBuzz = originalFizzBuzz; 1047 | }); 1048 | 1049 | it('should return an array', function() { 1050 | expect(fizzBuzz(3)).to.be.an('array'); 1051 | }); 1052 | 1053 | it('should return string representations of numbers 1 to n', function() { 1054 | actualResult = fizzBuzz(10); 1055 | actualResult.forEach(function(value) { 1056 | expect(value).to.be.a('string'); 1057 | }); 1058 | }); 1059 | 1060 | it('should output "Fizz" for multiples of three', function() { 1061 | actualResult = fizzBuzz(3); 1062 | expectedResult = ['1','2','Fizz']; 1063 | expect(actualResult).to.eql(expectedResult); 1064 | }); 1065 | 1066 | it('should output "Buzz" for multiples of five', function() { 1067 | actualResult = fizzBuzz(12); 1068 | expectedResult = ['1','2','Fizz','4','Buzz','Fizz','7','8','Fizz','Buzz','11','Fizz']; 1069 | expect(actualResult).to.eql(expectedResult); 1070 | }); 1071 | 1072 | it('should output "FizzBuzz" for multiples of both three and five', function() { 1073 | actualResult = fizzBuzz(15); 1074 | expectedResult = ['1','2','Fizz','4','Buzz','Fizz','7','8','Fizz','Buzz','11','Fizz','13','14','FizzBuzz']; 1075 | expect(actualResult).to.eql(expectedResult); 1076 | }); 1077 | 1078 | it('should use recursion by calling self', function() { 1079 | fizzBuzz(5); 1080 | expect(fizzBuzz.callCount).to.be.above(1); 1081 | }); 1082 | 1083 | it('should be invoked with one argument', function() { 1084 | fizzBuzz(5); 1085 | fizzBuzz.args.forEach(arg => { 1086 | expect(arg).to.have.length(1); 1087 | }); 1088 | }); 1089 | 1090 | }); 1091 | 1092 | 1093 | 1094 | describe('20. Count value in array', function() { 1095 | var originalCountOccurrence; 1096 | 1097 | before(function() { 1098 | originalCountOccurrence = countOccurrence; 1099 | countOccurrence = sinon.spy(countOccurrence); 1100 | }); 1101 | 1102 | afterEach(function() { 1103 | countOccurrence.reset(); 1104 | }); 1105 | 1106 | after(function() { 1107 | countOccurrence = originalCountOccurrence; 1108 | }); 1109 | 1110 | it('should return a number', function() { 1111 | expect(countOccurrence([2,7,4,4,1,4], 4)).to.be.a('number'); 1112 | expect(countOccurrence([2,'banana',4,4,1,'banana'], 'banana')).to.be.a('number'); 1113 | }); 1114 | 1115 | it('should return the number of occurrences of the value', function() { 1116 | expect(countOccurrence([2,7,4,4,1,4], 4)).to.equal(3); 1117 | expect(countOccurrence([2,'banana',4,4,1,'banana'], 'banana')).to.equal(2); 1118 | expect(countOccurrence([undefined,7,undefined,4,1,4], undefined)).to.equal(2); 1119 | expect(countOccurrence(['',null,0,'0',false], 0)).to.equal(1); 1120 | expect(countOccurrence(['',null,0,'false',false], false)).to.equal(1); 1121 | expect(countOccurrence(['',7,null,0,'0',false], null)).to.equal(1); 1122 | expect(countOccurrence(['',7,null,0,'0',false], '')).to.equal(1); 1123 | // expect(countOccurrence(['',7,null,0,NaN,'0',false], NaN)).to.equal(1); 1124 | }); 1125 | 1126 | it('should use recursion by calling self', function() { 1127 | countOccurrence([2,7,4,4,1,4], 4); 1128 | expect(countOccurrence.callCount).to.be.above(1); 1129 | }); 1130 | 1131 | it('should be invoked with two arguments', function() { 1132 | countOccurrence([2,7,4,4,1,4], 4); 1133 | countOccurrence.args.forEach(arg => { 1134 | expect(arg).to.have.length(2); 1135 | }); 1136 | }); 1137 | 1138 | }); 1139 | 1140 | 1141 | 1142 | describe('21. Recursive Map', function() { 1143 | var originalRMap, timesTwo, input, result; 1144 | 1145 | before(function() { 1146 | originalRMap = rMap; 1147 | rMap = sinon.spy(rMap); 1148 | timesTwo = function(n) { return n * 2; }; 1149 | }); 1150 | 1151 | beforeEach(function() { 1152 | input = [1,2,3,4,5]; 1153 | }); 1154 | 1155 | afterEach(function() { 1156 | rMap.reset(); 1157 | }); 1158 | 1159 | after(function() { 1160 | rMap = originalRMap; 1161 | }); 1162 | 1163 | it('should return an array', function() { 1164 | expect(rMap(input, timesTwo)).to.be.an('array'); 1165 | }); 1166 | 1167 | it('should not use the native version of map', function() { 1168 | // Spying on Array.prototype.map in testSupport.js 1169 | rMap(input, timesTwo); 1170 | expect(Array.prototype.map.called).to.be.false; 1171 | }); 1172 | 1173 | it('should return new array without mutating the input array', function() { 1174 | result = rMap(input, num => num); 1175 | expect(input).to.eql([1,2,3,4,5]); 1176 | // should deeply equal input 1177 | expect(result).to.eql(input); 1178 | // should not be same array in memory 1179 | expect(result).to.not.equal(input); 1180 | }); 1181 | 1182 | it('should apply a function to every value in an array', function() { 1183 | result = rMap([1,2,3], timesTwo); 1184 | expect(result).to.eql([2,4,6]); 1185 | }); 1186 | 1187 | it('should use recursion by calling self', function() { 1188 | rMap([1,2,3,4], timesTwo); 1189 | expect(rMap.callCount).to.be.above(1); 1190 | }); 1191 | 1192 | it('should be invoked with two arguments', function() { 1193 | rMap([1,2,3,4], timesTwo); 1194 | rMap.args.forEach(arg => { 1195 | expect(arg).to.have.length(2); 1196 | }); 1197 | }); 1198 | 1199 | }); 1200 | 1201 | 1202 | 1203 | describe('22. Count key in object', function() { 1204 | var originalCountKeysInObj, input; 1205 | 1206 | before(function() { 1207 | originalCountKeysInObj = countKeysInObj; 1208 | countKeysInObj = sinon.spy(countKeysInObj); 1209 | input = {e:{x:'y'},t:{r:{e:'r'},p:{y:'r'}},y:'e'}; 1210 | }); 1211 | 1212 | afterEach(function() { 1213 | countKeysInObj.reset(); 1214 | }); 1215 | 1216 | after(function() { 1217 | countKeysInObj = originalCountKeysInObj; 1218 | }); 1219 | 1220 | it('should return a number', function() { 1221 | expect(countKeysInObj(input, 'r')).to.be.a('number'); 1222 | }); 1223 | 1224 | it('should return the number of occurrences of the property', function() { 1225 | expect(countKeysInObj(input, 'e')).to.equal(2); 1226 | expect(countKeysInObj(input, 'x')).to.equal(1); 1227 | expect(countKeysInObj(input, 'y')).to.equal(2); 1228 | expect(countKeysInObj(input, 't')).to.equal(1); 1229 | expect(countKeysInObj(input, 'r')).to.equal(1); 1230 | expect(countKeysInObj(input, 'p')).to.equal(1); 1231 | }); 1232 | 1233 | it('should use recursion by calling self', function() { 1234 | countKeysInObj(input, 'e'); 1235 | expect(countKeysInObj.callCount).to.be.above(1); 1236 | }); 1237 | 1238 | it('should be invoked with two arguments', function() { 1239 | countKeysInObj(input, 'e'); 1240 | countKeysInObj.args.forEach(arg => { 1241 | expect(arg).to.have.length(2); 1242 | }); 1243 | }); 1244 | 1245 | }); 1246 | 1247 | 1248 | 1249 | describe('23. Count value in object', function() { 1250 | var originalCountValuesInObj, input; 1251 | 1252 | before(function() { 1253 | originalCountValuesInObj = countValuesInObj; 1254 | countValuesInObj = sinon.spy(countValuesInObj); 1255 | input = {e:{x:'y'},t:{r:{e:'r'},p:{y:'r'}},y:'e'}; 1256 | }); 1257 | 1258 | afterEach(function() { 1259 | countValuesInObj.reset(); 1260 | }); 1261 | 1262 | after(function() { 1263 | countValuesInObj = originalCountValuesInObj; 1264 | }); 1265 | 1266 | it('should return a number', function() { 1267 | expect(countValuesInObj(input, 'r')).to.be.a('number'); 1268 | }); 1269 | 1270 | it('should return the count of the occurrences of the property', function() { 1271 | expect(countValuesInObj(input, 'e')).to.equal(1); 1272 | expect(countValuesInObj(input, 'x')).to.equal(0); 1273 | expect(countValuesInObj(input, 'y')).to.equal(1); 1274 | expect(countValuesInObj(input, 't')).to.equal(0); 1275 | expect(countValuesInObj(input, 'r')).to.equal(2); 1276 | expect(countValuesInObj(input, 'p')).to.equal(0); 1277 | }); 1278 | 1279 | it('should use recursion by calling self', function() { 1280 | countValuesInObj(input, 'r'); 1281 | expect(countValuesInObj.callCount).to.be.above(1); 1282 | }); 1283 | 1284 | it('should be invoked with two arguments', function() { 1285 | countValuesInObj(input, 'r'); 1286 | countValuesInObj.args.forEach(arg => { 1287 | expect(arg).to.have.length(2); 1288 | }); 1289 | }); 1290 | 1291 | }); 1292 | 1293 | 1294 | 1295 | describe('24. Replace keys in object', function() { 1296 | var originalReplaceKeysInObj, input, output; 1297 | 1298 | before(function() { 1299 | originalReplaceKeysInObj = replaceKeysInObj; 1300 | replaceKeysInObj = sinon.spy(replaceKeysInObj); 1301 | }); 1302 | 1303 | beforeEach(function() { 1304 | input = {e:{x:'y'},t:{r:{e:'r'},p:{y:'r'}},y:'e'}; 1305 | }); 1306 | 1307 | afterEach(function() { 1308 | replaceKeysInObj.reset(); 1309 | }); 1310 | 1311 | after(function() { 1312 | replaceKeysInObj = originalReplaceKeysInObj; 1313 | }); 1314 | 1315 | it('should return an object', function() { 1316 | output = replaceKeysInObj(input, 'r', 'a'); 1317 | expect(output).to.be.an('object'); 1318 | }); 1319 | 1320 | it('should mutate the input object', function() { 1321 | output = replaceKeysInObj(input, 'y', 'u'); 1322 | expect(input).to.equal(output); 1323 | }); 1324 | 1325 | it('should return object containing renamed keys', function() { 1326 | replaceKeysInObj(input, 'e', 'f'); 1327 | 1328 | expect(input).to.have.all.keys('f','t','y'); 1329 | expect(input.f).to.be.an('object'); 1330 | expect(input.f).to.have.all.keys('x'); 1331 | 1332 | expect(input.f.x).to.be.a('string'); 1333 | expect(input.f.x).to.equal('y'); 1334 | 1335 | expect(input.t).to.be.an('object'); 1336 | expect(input.t).to.have.all.keys('r','p'); 1337 | 1338 | expect(input.t.r).to.be.an('object'); 1339 | expect(input.t.r).to.have.all.keys('f'); 1340 | expect(input.t.r.f).to.be.a('string'); 1341 | expect(input.t.r.f).to.equal('r'); 1342 | 1343 | expect(input.t.p).to.be.an('object'); 1344 | expect(input.t.p).to.have.all.keys('y'); 1345 | expect(input.t.p.y).to.be.a('string'); 1346 | expect(input.t.p.y).to.equal('r'); 1347 | 1348 | expect(input.y).to.be.a('string'); 1349 | expect(input.y).to.equal('e'); 1350 | 1351 | expect(input).to.not.have.ownProperty('e'); 1352 | expect(input.t.r).to.not.have.ownProperty('e'); 1353 | }); 1354 | 1355 | it('should return object with same number of keys', function() { 1356 | expect(analyze(input)).to.equal(8); 1357 | output = replaceKeysInObj(input, 'e', 'f'); 1358 | expect(analyze(output)).to.equal(8); 1359 | }); 1360 | 1361 | it('should use recursion by calling self', function() { 1362 | replaceKeysInObj(input, 'r', 'a'); 1363 | expect(replaceKeysInObj.callCount).to.be.above(1); 1364 | }); 1365 | 1366 | it('should be invoked with three arguments', function() { 1367 | replaceKeysInObj(input, 'r', 'a'); 1368 | replaceKeysInObj.args.forEach(arg => { 1369 | expect(arg).to.have.length(3); 1370 | }); 1371 | }); 1372 | 1373 | }); 1374 | 1375 | 1376 | 1377 | describe('25. First n Fibonacci', function() { 1378 | var originalFibonacci; 1379 | 1380 | before(function() { 1381 | originalFibonacci = fibonacci; 1382 | fibonacci = sinon.spy(fibonacci); 1383 | }); 1384 | 1385 | afterEach(function() { 1386 | fibonacci.reset(); 1387 | }); 1388 | 1389 | after(function() { 1390 | fibonacci = originalFibonacci; 1391 | }); 1392 | 1393 | it('should return an array', function() { 1394 | expect(fibonacci(5)).to.be.an('array'); 1395 | }); 1396 | 1397 | it('should return first n Fibonacci numbers where n starts at index 1', function() { 1398 | expect(fibonacci(1)).to.eql([0,1]); 1399 | expect(fibonacci(2)).to.eql([0,1,1]); 1400 | expect(fibonacci(3)).to.eql([0,1,1,2]); 1401 | expect(fibonacci(4)).to.eql([0,1,1,2,3]); 1402 | expect(fibonacci(5)).to.eql([0,1,1,2,3,5]); 1403 | expect(fibonacci(8)).to.eql([0,1,1,2,3,5,8,13,21]); 1404 | }); 1405 | 1406 | it('should return null for zero and negative integers', function() { 1407 | expect(fibonacci(0)).to.be.null; 1408 | expect(fibonacci(-7)).to.be.null; 1409 | }); 1410 | 1411 | it('should use recursion by calling self', function() { 1412 | fibonacci(5); 1413 | expect(fibonacci.callCount).to.be.above(1); 1414 | }); 1415 | 1416 | it('should be invoked with one argument', function() { 1417 | fibonacci(5); 1418 | fibonacci.args.forEach(arg => { 1419 | expect(arg).to.have.length(1); 1420 | }); 1421 | }); 1422 | 1423 | }); 1424 | 1425 | 1426 | 1427 | describe('26. Return nth Fibonacci', function() { 1428 | var originalNthFibo; 1429 | 1430 | before(function() { 1431 | originalNthFibo = nthFibo; 1432 | nthFibo = sinon.spy(nthFibo); 1433 | }); 1434 | 1435 | afterEach(function() { 1436 | nthFibo.reset(); 1437 | }); 1438 | 1439 | after(function() { 1440 | nthFibo = originalNthFibo; 1441 | }); 1442 | 1443 | it('should return a number', function() { 1444 | expect(nthFibo(5)).to.be.a('number'); 1445 | }); 1446 | 1447 | it('should return the nth nthFibo number', function() { 1448 | expect(nthFibo(0)).to.equal(0); 1449 | expect(nthFibo(1)).to.equal(1); 1450 | expect(nthFibo(2)).to.equal(1); 1451 | expect(nthFibo(3)).to.equal(2); 1452 | expect(nthFibo(4)).to.equal(3); 1453 | expect(nthFibo(5)).to.equal(5); 1454 | expect(nthFibo(8)).to.equal(21); 1455 | }); 1456 | 1457 | it('should return null for negative integers', function() { 1458 | expect(nthFibo(-5)).to.be.null; 1459 | expect(nthFibo(-7)).to.be.null; 1460 | }); 1461 | 1462 | it('should use recursion by calling self', function() { 1463 | nthFibo(5); 1464 | expect(nthFibo.callCount).to.be.above(1); 1465 | }); 1466 | 1467 | it('should be invoked with one argument', function() { 1468 | nthFibo(5); 1469 | nthFibo.args.forEach(arg => { 1470 | expect(arg).to.have.length(1); 1471 | }); 1472 | }); 1473 | 1474 | }); 1475 | 1476 | 1477 | 1478 | describe('27. Capitalize words in array', function() { 1479 | var originalCapitalizeWords; 1480 | 1481 | before(function() { 1482 | originalCapitalizeWords = capitalizeWords; 1483 | capitalizeWords = sinon.spy(capitalizeWords); 1484 | }); 1485 | 1486 | afterEach(function() { 1487 | capitalizeWords.reset(); 1488 | }); 1489 | 1490 | after(function() { 1491 | capitalizeWords = originalCapitalizeWords; 1492 | }); 1493 | 1494 | it('should return an array', function() { 1495 | expect(capitalizeWords(['recursion'])).to.be.an('array'); 1496 | }); 1497 | 1498 | it('should capitalize all words in array', function() { 1499 | expect(capitalizeWords(["ceci","n'est","pas","une","pipe"])).to.eql(["CECI", "N'EST", "PAS", "UNE", "PIPE"]); 1500 | }); 1501 | 1502 | it('should use recursion by calling self', function() { 1503 | capitalizeWords(['i','am','learning','recursion']); 1504 | expect(capitalizeWords.callCount).to.be.above(1); 1505 | }); 1506 | 1507 | it('should be invoked with one argument', function() { 1508 | capitalizeWords(['you','got','this']); 1509 | capitalizeWords.args.forEach(arg => { 1510 | expect(arg).to.have.length(1); 1511 | }); 1512 | }); 1513 | 1514 | }); 1515 | 1516 | 1517 | 1518 | describe('28. Capitalize first letter of words in array', function() { 1519 | var originalCapitalizeFirst; 1520 | 1521 | before(function() { 1522 | originalCapitalizeFirst = capitalizeFirst; 1523 | capitalizeFirst = sinon.spy(capitalizeFirst); 1524 | }); 1525 | 1526 | afterEach(function() { 1527 | capitalizeFirst.reset(); 1528 | }); 1529 | 1530 | after(function() { 1531 | capitalizeFirst = originalCapitalizeFirst; 1532 | }); 1533 | 1534 | it('should return an array', function() { 1535 | expect(capitalizeFirst(['recursion'])).to.be.an('array'); 1536 | }); 1537 | 1538 | it('should capitalize first letter of each word in array', function() { 1539 | expect(capitalizeFirst(["ceci","n'est","pas","une","pipe"])).to.eql(["Ceci", "N'est", "Pas", "Une", "Pipe"]); 1540 | }); 1541 | 1542 | it('should use recursion by calling self', function() { 1543 | capitalizeFirst(["ceci","n'est","pas","une","pipe"]); 1544 | expect(capitalizeFirst.callCount).to.be.above(1); 1545 | }); 1546 | 1547 | it('should be invoked with one argument', function() { 1548 | capitalizeFirst(['you','got','this']); 1549 | capitalizeFirst.args.forEach(arg => { 1550 | expect(arg).to.have.length(1); 1551 | }); 1552 | }); 1553 | 1554 | }); 1555 | 1556 | 1557 | 1558 | describe('29. Sum even numbers in nested objects', function() { 1559 | var originalNestedEvenSum, input; 1560 | 1561 | before(function() { 1562 | originalNestedEvenSum = nestedEvenSum; 1563 | nestedEvenSum = sinon.spy(nestedEvenSum); 1564 | input = { 1565 | a: 2, 1566 | b: {b: 2, bb: {b: 3, bb: {b: 2}}}, 1567 | c: {c: {c: 2}, cc: 'ball', ccc: 5}, 1568 | d: 1, 1569 | e: {e: {e: 2}, ee: 'car'} 1570 | }; 1571 | }); 1572 | 1573 | afterEach(function() { 1574 | nestedEvenSum.reset(); 1575 | }); 1576 | 1577 | after(function() { 1578 | nestedEvenSum = originalNestedEvenSum; 1579 | }); 1580 | 1581 | it('should return a number', function() { 1582 | expect(nestedEvenSum(input)).to.be.a('number'); 1583 | }); 1584 | 1585 | it('should sum even numbers', function() { 1586 | expect(nestedEvenSum(input)).to.equal(10); 1587 | }); 1588 | 1589 | it('should use recursion by calling self', function() { 1590 | nestedEvenSum(input); 1591 | expect(nestedEvenSum.callCount).to.be.above(1); 1592 | }); 1593 | 1594 | it('should be invoked with one argument', function() { 1595 | nestedEvenSum(input); 1596 | nestedEvenSum.args.forEach(arg => { 1597 | expect(arg).to.have.length(1); 1598 | }); 1599 | }); 1600 | 1601 | }); 1602 | 1603 | 1604 | 1605 | describe('30. Flatten nested arrays', function() { 1606 | var originalFlatten; 1607 | 1608 | before(function() { 1609 | originalFlatten = flatten; 1610 | flatten = sinon.spy(flatten); 1611 | }); 1612 | 1613 | afterEach(function() { 1614 | flatten.reset(); 1615 | }); 1616 | 1617 | after(function() { 1618 | flatten = originalFlatten; 1619 | }); 1620 | 1621 | it('should return an array', function() { 1622 | expect(flatten([1,[2],[[3]]])).to.be.an('array'); 1623 | }); 1624 | 1625 | it('should return flattened array', function() { 1626 | expect(flatten([[1],[2,3],[[4]],5])).to.eql([1,2,3,4,5]); 1627 | expect(flatten([3,[0,[34,[7,[18]]]]])).to.eql([3,0,34,7,18]); 1628 | expect(flatten([[[[[3],0],34],7],18])).to.eql([3,0,34,7,18]); 1629 | expect(flatten([[1],[2,[],3],[],[[4]],5])).to.eql([1,2,3,4,5]); 1630 | }); 1631 | 1632 | it('should use recursion by calling self', function() { 1633 | flatten([3,[0,[34]]]); 1634 | expect(flatten.callCount).to.be.above(1); 1635 | }); 1636 | 1637 | it('should be invoked with one argument', function() { 1638 | flatten([3,[0,[34]]]); 1639 | flatten.args.forEach(arg => { 1640 | expect(arg).to.have.length(1); 1641 | }); 1642 | }); 1643 | 1644 | }); 1645 | 1646 | 1647 | 1648 | describe('31. Tally letters in string', function() { 1649 | var originalLetterTally; 1650 | 1651 | before(function() { 1652 | originalLetterTally = letterTally; 1653 | letterTally = sinon.spy(letterTally); 1654 | }); 1655 | 1656 | afterEach(function() { 1657 | letterTally.reset(); 1658 | }); 1659 | 1660 | after(function() { 1661 | letterTally = originalLetterTally; 1662 | }); 1663 | 1664 | it('should return an object', function() { 1665 | expect(letterTally('orangutan')).to.be.an('object'); 1666 | }); 1667 | 1668 | it('should return object containing tallies of unique letters', function() { 1669 | var output = letterTally('potato'); 1670 | 1671 | expect(output.p).to.equal(1); 1672 | expect(output.o).to.equal(2); 1673 | expect(output.t).to.equal(2); 1674 | expect(output.a).to.equal(1); 1675 | }); 1676 | 1677 | it('should return object containing the number of keys corresponding to unique letters', function() { 1678 | var output = letterTally('mississippi'); 1679 | var keyCount = Object.keys(output).length; 1680 | expect(keyCount).to.equal(4); 1681 | }); 1682 | 1683 | it('should use recursion by calling self', function() { 1684 | letterTally('invasion'); 1685 | expect(letterTally.callCount).to.be.above(1); 1686 | }); 1687 | 1688 | it('should be invoked with at most two arguments', function() { 1689 | letterTally('invasion'); 1690 | letterTally.args.forEach(arg => { 1691 | expect(arg).to.have.length.of.at.most(2); 1692 | }); 1693 | }); 1694 | 1695 | }); 1696 | 1697 | 1698 | 1699 | describe('32. Eliminate consecutive duplicates', function() { 1700 | var originalCompress, input1, input2; 1701 | 1702 | before(function() { 1703 | originalCompress = compress; 1704 | compress = sinon.spy(compress); 1705 | input1 = [1,2,2,3,4,4,5,5,5]; 1706 | input2 = [1,2,2,3,4,4,2,5,5,5,4,4]; 1707 | }); 1708 | 1709 | afterEach(function() { 1710 | compress.reset(); 1711 | }); 1712 | 1713 | after(function() { 1714 | compress = originalCompress; 1715 | }); 1716 | 1717 | it('should return an array', function() { 1718 | expect(compress(input1)).to.be.an('array'); 1719 | }); 1720 | 1721 | it('should not mutate the input array', function() { 1722 | var result = compress(input1); 1723 | expect(input1).to.eql([1,2,2,3,4,4,5,5,5]); 1724 | expect(input1).to.not.equal(result); 1725 | }); 1726 | 1727 | it('should remove consecutive duplicates', function() { 1728 | expect(compress(input1)).to.eql([1,2,3,4,5]); 1729 | expect(compress(input2)).to.eql([1,2,3,4,2,5,4]); 1730 | }); 1731 | 1732 | it('should use recursion by calling self', function() { 1733 | compress(input1); 1734 | expect(compress.callCount).to.be.above(1); 1735 | }); 1736 | 1737 | it('should be invoked with one argument', function() { 1738 | compress(input1); 1739 | compress.args.forEach(arg => { 1740 | expect(arg).to.have.length(1); 1741 | }); 1742 | }); 1743 | 1744 | }); 1745 | 1746 | 1747 | 1748 | describe('33. Augment each element in nested arrays', function() { 1749 | var originalAugmentElements; 1750 | 1751 | before(function() { 1752 | originalAugmentElements = augmentElements; 1753 | augmentElements = sinon.spy(augmentElements); 1754 | }); 1755 | 1756 | afterEach(function() { 1757 | augmentElements.reset(); 1758 | }); 1759 | 1760 | after(function() { 1761 | augmentElements = originalAugmentElements; 1762 | }); 1763 | 1764 | it('should return an array', function() { 1765 | expect(augmentElements([[],[3]], 5)).to.be.an('array'); 1766 | }); 1767 | 1768 | it('should augment each element with given value', function() { 1769 | expect(augmentElements([[],[3],[7]], 5)).to.eql([[5],[3,5],[7,5]]); 1770 | expect(augmentElements([[],[3],[7]], null)).to.eql([[null],[3,null],[7,null]]); 1771 | expect(augmentElements([[],[3],[7]], '')).to.eql([[''],[3,''],[7,'']]); 1772 | expect(augmentElements([[],[3],[7]], false)).to.eql([[false],[3,false],[7,false]]); 1773 | }); 1774 | 1775 | it('should use recursion by calling self', function() { 1776 | augmentElements([[],[3]], 5); 1777 | expect(augmentElements.callCount).to.be.above(1); 1778 | }); 1779 | 1780 | it('should be invoked with two arguments', function() { 1781 | augmentElements([[],[3]], 5); 1782 | augmentElements.args.forEach(arg => { 1783 | expect(arg).to.have.length(2); 1784 | }); 1785 | }); 1786 | 1787 | }); 1788 | 1789 | 1790 | 1791 | describe('34. Minimize zeroes', function() { 1792 | var originalMinimizeZeroes, input1, input2; 1793 | 1794 | before(function() { 1795 | originalMinimizeZeroes = minimizeZeroes; 1796 | minimizeZeroes = sinon.spy(minimizeZeroes); 1797 | }); 1798 | 1799 | beforeEach(function() { 1800 | input1 = [2,0,0,0,1,4]; 1801 | input2 = [2,0,0,0,1,0,0,4]; 1802 | }); 1803 | 1804 | afterEach(function() { 1805 | minimizeZeroes.reset(); 1806 | }); 1807 | 1808 | after(function() { 1809 | minimizeZeroes = originalMinimizeZeroes; 1810 | }); 1811 | 1812 | it('should return an array', function() { 1813 | expect(minimizeZeroes(input1)).to.be.an('array'); 1814 | }); 1815 | 1816 | it('should not mutate the input array', function() { 1817 | expect(minimizeZeroes(input2)).to.not.equal(input2); 1818 | }); 1819 | 1820 | it('should remove excess zeroes', function() { 1821 | expect(minimizeZeroes(input1)).to.eql([2,0,1,4]); 1822 | expect(minimizeZeroes(input2)).to.eql([2,0,1,0,4]); 1823 | }); 1824 | 1825 | it('should use recursion by calling self', function() { 1826 | minimizeZeroes(input1); 1827 | expect(minimizeZeroes.callCount).to.be.above(1); 1828 | }); 1829 | 1830 | it('should be invoked with one argument', function() { 1831 | minimizeZeroes(input2); 1832 | minimizeZeroes.args.forEach(arg => { 1833 | expect(arg).to.have.length(1); 1834 | }); 1835 | }); 1836 | 1837 | }); 1838 | 1839 | 1840 | 1841 | describe('35. Alternate sign', function() { 1842 | var originalAlternateSign, input1, input2; 1843 | 1844 | before(function() { 1845 | originalAlternateSign = alternateSign; 1846 | alternateSign = sinon.spy(alternateSign); 1847 | }); 1848 | 1849 | beforeEach(function() { 1850 | input1 = [2,7,8,3,1,4]; 1851 | input2 = [-2,-7,8,3,-1,4]; 1852 | }); 1853 | 1854 | afterEach(function() { 1855 | alternateSign.reset(); 1856 | }); 1857 | 1858 | after(function() { 1859 | alternateSign = originalAlternateSign; 1860 | }); 1861 | 1862 | it('should return an array', function() { 1863 | expect(alternateSign(input1)).to.be.an('array'); 1864 | }); 1865 | 1866 | it('should not mutate the input array', function() { 1867 | expect(alternateSign(input2)).to.not.equal(input2); 1868 | }); 1869 | 1870 | it('should remove excess zeroes', function() { 1871 | expect(alternateSign(input1)).to.eql([2,-7,8,-3,1,-4]); 1872 | expect(alternateSign(input2)).to.eql([2,-7,8,-3,1,-4]); 1873 | }); 1874 | 1875 | it('should use recursion by calling self', function() { 1876 | alternateSign(input1); 1877 | expect(alternateSign.callCount).to.be.above(1); 1878 | }); 1879 | 1880 | it('should be invoked with one argument', function() { 1881 | alternateSign(input2); 1882 | alternateSign.args.forEach(arg => { 1883 | expect(arg).to.have.length(1); 1884 | }); 1885 | }); 1886 | 1887 | }); 1888 | 1889 | 1890 | 1891 | describe('36. Convert numbers to text', function() { 1892 | var originalNumToText, input1, input2; 1893 | 1894 | before(function() { 1895 | originalNumToText = numToText; 1896 | numToText = sinon.spy(numToText); 1897 | }); 1898 | 1899 | beforeEach(function() { 1900 | input1 = '5 dogs and 6 ponies'; 1901 | input2 = 'Give me 8 dollars'; 1902 | }); 1903 | 1904 | afterEach(function() { 1905 | numToText.reset(); 1906 | }); 1907 | 1908 | after(function() { 1909 | numToText = originalNumToText; 1910 | }); 1911 | 1912 | it('should return a string', function() { 1913 | expect(numToText(input1)).to.be.a('string'); 1914 | }); 1915 | 1916 | it('should convert single digits to their word equivalent', function() { 1917 | expect(numToText(input1)).to.equal('five dogs and six ponies'); 1918 | expect(numToText(input2)).to.equal('Give me eight dollars'); 1919 | }); 1920 | 1921 | it('should use recursion by calling self', function() { 1922 | numToText(input2); 1923 | expect(numToText.callCount).to.be.above(1); 1924 | }); 1925 | 1926 | it('should be invoked with one argument', function() { 1927 | numToText(input2); 1928 | numToText.args.forEach(arg => { 1929 | expect(arg).to.have.length(1); 1930 | }); 1931 | }); 1932 | 1933 | }); 1934 | 1935 | }); 1936 | }()); 1937 | -------------------------------------------------------------------------------- /spec/part2.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | 3 | (function() { 4 | 'use strict'; 5 | 6 | describe('More Exercises in Recursion', function() { 7 | 8 | describe('37. Count Tags', function() { 9 | var originalTagCount, actualResults, expectedResults, tags, $child, $rootElement; 10 | 11 | before(function() { 12 | originalTagCount = tagCount; 13 | tagCount = sinon.spy(tagCount); 14 | actualResults = []; 15 | expectedResults = []; 16 | $rootElement = $('

beep

blip

blorp

'); 17 | $child = $('#mocha'); 18 | $child.remove(); 19 | $('body').append($rootElement); 20 | tags = document.getElementById('tagCountTest'); 21 | }); 22 | 23 | afterEach(function() { 24 | tagCount.reset(); 25 | }); 26 | 27 | after(function() { 28 | $rootElement.remove(); 29 | $('body').append($child); 30 | tagCount = originalTagCount; 31 | }); 32 | 33 | it('should return a number', function() { 34 | actualResults.push(tagCount('p')); 35 | expectedResults.push(document.getElementsByTagName('p').length); 36 | expect(actualResults[0]).to.be.a('number'); 37 | }); 38 | 39 | it('should return number of times tag occurs', function() { 40 | actualResults.push(tagCount('div')); 41 | expectedResults.push(document.getElementsByTagName('div').length); 42 | expect(actualResults[1]).to.equal(expectedResults[1]); 43 | }); 44 | 45 | it('should support various tag types', function() { 46 | actualResults.push(tagCount('span')); 47 | expectedResults.push(document.getElementsByTagName('span').length); 48 | actualResults.forEach(function(result, i) { 49 | expect(result).to.equal(expectedResults[i]); 50 | }); 51 | }); 52 | 53 | it('should not require starting node argument', function() { 54 | tagCount('html'); 55 | expect(tagCount.args[0]).to.have.length(1); 56 | }); 57 | 58 | it('should use recursion by calling self', function() { 59 | tagCount('p') 60 | expect(tagCount.callCount).to.be.above(3); 61 | }); 62 | 63 | it('should be invoked with at most two arguments', function() { 64 | tagCount('p', tags); 65 | tagCount.args.forEach(arg => { 66 | expect(arg).to.have.length.of.at.most(2); 67 | }); 68 | }); 69 | 70 | }); 71 | 72 | 73 | 74 | describe('38. Binary Search', function() { 75 | var originalBinarySearch, input1, input2, input3, input4, input5, primes; 76 | 77 | before(function() { 78 | originalBinarySearch = binarySearch; 79 | binarySearch = sinon.spy(binarySearch); 80 | input1 = [1,2,3,4,5,6]; 81 | input2 = [1,2,3,4,5,6,7]; 82 | input3 = [-5,-4,-3,-2,-1]; 83 | input4 = [-6,-5,-4,-3,-2,-1]; 84 | input5 = [-4,-3,-2,-1,0,1,2,3]; 85 | primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43]; 86 | }); 87 | 88 | afterEach(function() { 89 | binarySearch.reset(); 90 | }); 91 | 92 | after(function() { 93 | binarySearch = originalBinarySearch; 94 | }); 95 | 96 | it('should return a number', function() { 97 | expect(binarySearch(input1, 3)).to.be.a('number'); 98 | }); 99 | 100 | it('should not mutate the input array', function() { 101 | binarySearch(input1, 4); 102 | expect(input1).to.eql([1,2,3,4,5,6]); 103 | }); 104 | 105 | it('should return index of target', function() { 106 | expect(binarySearch(input1, 1)).to.equal(0); 107 | expect(binarySearch(input1, 2)).to.equal(1); 108 | expect(binarySearch(input1, 3)).to.equal(2); 109 | expect(binarySearch(input1, 4)).to.equal(3); 110 | expect(binarySearch(input1, 5)).to.equal(4); 111 | expect(binarySearch(input1, 6)).to.equal(5); 112 | expect(binarySearch(input2, 1)).to.equal(0); 113 | expect(binarySearch(input2, 2)).to.equal(1); 114 | expect(binarySearch(input2, 3)).to.equal(2); 115 | expect(binarySearch(input2, 4)).to.equal(3); 116 | expect(binarySearch(input2, 5)).to.equal(4); 117 | expect(binarySearch(input2, 6)).to.equal(5); 118 | expect(binarySearch(input2, 7)).to.equal(6); 119 | expect(binarySearch(primes, 2)).to.equal(0); 120 | expect(binarySearch(primes,19)).to.equal(7); 121 | expect(binarySearch(primes,41)).to.equal(12); 122 | }); 123 | 124 | it('should support negative numbers', function() { 125 | expect(binarySearch(input3,-5)).to.equal(0); 126 | expect(binarySearch(input3,-4)).to.equal(1); 127 | expect(binarySearch(input3,-3)).to.equal(2); 128 | expect(binarySearch(input3,-2)).to.equal(3); 129 | expect(binarySearch(input3,-1)).to.equal(4); 130 | expect(binarySearch(input4,-6)).to.equal(0); 131 | expect(binarySearch(input4,-5)).to.equal(1); 132 | expect(binarySearch(input4,-4)).to.equal(2); 133 | expect(binarySearch(input4,-3)).to.equal(3); 134 | expect(binarySearch(input4,-2)).to.equal(4); 135 | expect(binarySearch(input4,-1)).to.equal(5); 136 | expect(binarySearch(input5,-2)).to.equal(2); 137 | expect(binarySearch(input5, 2)).to.equal(6); 138 | }); 139 | 140 | it('should return null if target not found', function() { 141 | expect(binarySearch(input1,-1)).to.be.null; 142 | expect(binarySearch(input1, 7)).to.be.null; 143 | expect(binarySearch(input2,-1)).to.be.null; 144 | expect(binarySearch(input2, 8)).to.be.null; 145 | expect(binarySearch(input3,-6)).to.be.null; 146 | expect(binarySearch(input3, 0)).to.be.null; 147 | expect(binarySearch(input4,-8)).to.be.null; 148 | expect(binarySearch(input4, 1)).to.be.null; 149 | expect(binarySearch(input5,-8)).to.be.null; 150 | expect(binarySearch(input5, 4)).to.be.null; 151 | expect(binarySearch(primes,32)).to.be.null; 152 | }); 153 | 154 | it('should use recursion by calling self', function() { 155 | binarySearch(primes, 3); 156 | expect(binarySearch.callCount).to.be.above(1); 157 | }); 158 | 159 | it('should be invoked with at most four arguments', function() { 160 | binarySearch(primes, 4); 161 | binarySearch.args.forEach(arg => { 162 | expect(arg).to.have.length.of.at.most(4); 163 | }); 164 | }); 165 | 166 | xit('should be invoked with two arguments', function() { 167 | binarySearch(primes, 4); 168 | binarySearch.args.forEach(arg => { 169 | expect(arg).to.have.length(2); 170 | }); 171 | }); 172 | 173 | }); 174 | 175 | 176 | 177 | describe('39. Merge Sort', function() { 178 | var originalMergeSort, numbers, sorted; 179 | 180 | before(function() { 181 | originalMergeSort = mergeSort; 182 | mergeSort = sinon.spy(mergeSort); 183 | }); 184 | 185 | beforeEach(function() { 186 | numbers = [8,2,20,1,15]; 187 | }); 188 | 189 | afterEach(function() { 190 | mergeSort.reset(); 191 | }); 192 | 193 | after(function() { 194 | mergeSort = originalMergeSort; 195 | }); 196 | 197 | it('should return an array', function() { 198 | sorted = mergeSort(numbers); 199 | expect(sorted).to.be.an('array'); 200 | }); 201 | 202 | it('should not mutate the input array', function() { 203 | sorted = mergeSort(numbers); 204 | expect(numbers).to.eql([8,2,20,1,15]); 205 | expect(numbers).to.not.equal(sorted); 206 | }); 207 | 208 | it('should sort an array of numbers in order of least to greatest', function() { 209 | expect(mergeSort([])).to.eql([]); 210 | expect(mergeSort([0])).to.eql([0]); 211 | expect(mergeSort([1,0])).to.eql([0,1]); 212 | expect(mergeSort([0,1,2,3])).to.eql([0,1,2,3]); 213 | expect(mergeSort([5,4,3,2,1])).to.eql([1,2,3,4,5]); 214 | expect(mergeSort([10,1,8,5,0])).to.eql([0,1,5,8,10]); 215 | expect(mergeSort([8,2,20,1,15])).to.eql([1,2,8,15,20]); 216 | }); 217 | 218 | it('should be able to handle negative numbers', function() { 219 | expect(mergeSort([-1])).to.eql([-1]); 220 | expect(mergeSort([0,-1])).to.eql([-1,0]); 221 | expect(mergeSort([0,1,-2,-3])).to.eql([-3,-2,0,1]); 222 | expect(mergeSort([8,-2,20,1,-15])).to.eql([-15,-2,1,8,20]); 223 | expect(mergeSort([0,-1,-2,-3,-4,-5,-10])).to.eql([-10,-5,-4,-3,-2,-1,0]); 224 | }); 225 | 226 | it("should not use the native Array sort method", function() { 227 | // Spying on Array.prototype.sort in testSupport.js 228 | mergeSort(numbers); 229 | expect(Array.prototype.sort.called).to.equal(false); 230 | }); 231 | 232 | it('should use recursion by calling self', function () { 233 | mergeSort(numbers); 234 | expect(mergeSort.callCount).to.be.above(1); 235 | }); 236 | 237 | it('should be invoked with one argument', function() { 238 | mergeSort(numbers); 239 | mergeSort.args.forEach(arg => { 240 | expect(arg).to.have.length(1); 241 | }); 242 | }); 243 | 244 | }); 245 | 246 | 247 | 248 | describe('40. Clone', function() { 249 | var originalClone, object1, object2, array1, array2, result; 250 | 251 | before(function() { 252 | originalClone = clone; 253 | clone = sinon.spy(clone); 254 | object1 = {a:1,b:{bb:{bbb:2}},c:3}; 255 | object2 = {a:1,b:['bb',{bbb:[2]}],c:{cc:[3,{ccc:4},5]}}; 256 | array1 = [1,[2,[]],3,[[[4]],5]]; 257 | array2 = [1,[2,{a:[{},2,3]}],{3:[4]},5]; 258 | }); 259 | 260 | afterEach(function() { 261 | clone.reset(); 262 | }); 263 | 264 | after(function() { 265 | clone = originalClone; 266 | }); 267 | 268 | it('should return an object when input is an object', function() { 269 | result = clone(object1); 270 | expect(result).to.be.an('object'); 271 | expect(result).to.not.be.an('array'); 272 | }); 273 | 274 | it('should return an array when input is an array', function() { 275 | result = clone(array1); 276 | expect(result).to.be.an('array'); 277 | expect(result).to.not.be.an('object'); 278 | }); 279 | 280 | it('should not mutate the input object or array', function() { 281 | result = clone(object1); 282 | expect(object1).to.eql({a:1,b:{bb:{bbb:2}},c:3}); 283 | expect(result).to.not.equal(object1); 284 | result = clone(array1); 285 | expect(array1).to.eql([1,[2,[]],3,[[[4]],5]]); 286 | expect(result).to.not.equal(array1); 287 | }); 288 | 289 | it('should shallow clone objects', function() { 290 | result = clone(object1); 291 | expect(result).to.eql(object1); 292 | }); 293 | 294 | it('should shallow clone arrays', function() { 295 | result = clone(array1); 296 | expect(result).to.eql(array1); 297 | }); 298 | 299 | it('should deeply clone objects', function() { 300 | result = clone(object2); 301 | expect(result).to.eql(object2); 302 | expect(result.b).to.eql(object2.b); 303 | expect(result.b).to.not.equal(object2.b); 304 | expect(result.b[1]).to.eql(object2.b[1]); 305 | expect(result.b[1]).to.not.equal(object2.b[1]); 306 | expect(result.b[1].bbb).to.eql(object2.b[1].bbb); 307 | expect(result.b[1].bbb).to.not.equal(object2.b[1].bbb); 308 | expect(result.c).to.eql(object2.c); 309 | expect(result.c).to.not.equal(object2.c); 310 | expect(result.c.cc).to.eql(object2.c.cc); 311 | expect(result.c.cc).to.not.equal(object2.c.cc); 312 | expect(result.c.cc[1]).to.eql(object2.c.cc[1]); 313 | expect(result.c.cc[1]).to.not.equal(object2.c.cc[1]); 314 | }); 315 | 316 | it('should deeply clone arrays', function() { 317 | result = clone(array2); 318 | expect(result).to.eql(array2); 319 | expect(result[1]).to.eql(array2[1]); 320 | expect(result[1]).to.not.equal(array2[1]); 321 | expect(result[1][1]).to.eql(array2[1][1]); 322 | expect(result[1][1]).to.not.equal(array2[1][1]); 323 | expect(result[1][1].a).to.eql(array2[1][1].a); 324 | expect(result[1][1].a).to.not.equal(array2[1][1].a); 325 | expect(result[1][1].a[0]).to.eql(array2[1][1].a[0]); 326 | expect(result[1][1].a[0]).to.not.equal(array2[1][1].a); 327 | expect(result[2]).to.eql(array2[2]); 328 | expect(result[2]).to.not.equal(array2[2]); 329 | expect(result[2]['3']).to.eql(array2[2]['3']); 330 | expect(result[2]['3']).to.not.equal(array2[2]['3']); 331 | }); 332 | 333 | it("should not use native JSON methods or Object.assign", function() { 334 | // Spying on methods in testSupport.js 335 | clone(object2); 336 | clone(array2); 337 | expect(Object.assign.called).to.be.false; 338 | expect(JSON.stringify.called).to.be.false; 339 | expect(JSON.parse.called).to.be.false; 340 | }); 341 | 342 | it('should use recursion by calling self', function () { 343 | clone(object1); 344 | expect(clone.callCount).to.be.above(1); 345 | clone.reset(); 346 | clone(array1); 347 | expect(clone.callCount).to.be.above(1); 348 | }); 349 | 350 | it('should be invoked with one argument', function() { 351 | clone(object1); 352 | clone.args.forEach(arg => { 353 | expect(arg).to.have.length(1); 354 | }); 355 | clone.reset(); 356 | clone(array1); 357 | clone.args.forEach(arg => { 358 | expect(arg).to.have.length(1); 359 | }); 360 | }); 361 | 362 | }); 363 | 364 | }); 365 | }()); 366 | -------------------------------------------------------------------------------- /src/recursion.js: -------------------------------------------------------------------------------- 1 | /* jshint esversion: 6 */ 2 | 3 | // Solve the following prompts using recursion. 4 | 5 | // 1. Calculate the factorial of a number. The factorial of a non-negative integer n, 6 | // denoted by n!, is the product of all positive integers less than or equal to n. 7 | // Example: 5! = 5 x 4 x 3 x 2 x 1 = 120 8 | // factorial(5); // 120 9 | var factorial = function(n) { 10 | }; 11 | 12 | // 2. Compute the sum of an array of integers. 13 | // sum([1,2,3,4,5,6]); // 21 14 | var sum = function(array) { 15 | }; 16 | 17 | // 3. Sum all numbers in an array containing nested arrays. 18 | // arraySum([1,[2,3],[[4]],5]); // 15 19 | var arraySum = function(array) { 20 | }; 21 | 22 | // 4. Check if a number is even. 23 | var isEven = function(n) { 24 | }; 25 | 26 | // 5. Sum all integers below a given integer. 27 | // sumBelow(10); // 45 28 | // sumBelow(7); // 21 29 | var sumBelow = function(n) { 30 | }; 31 | 32 | // 6. Get the integers within a range (x, y). 33 | // range(2,9); // [3,4,5,6,7,8] 34 | var range = function(x, y) { 35 | }; 36 | 37 | // 7. Compute the exponent of a number. 38 | // The exponent of a number says how many times the base number is used as a factor. 39 | // 8^2 = 8 x 8 = 64. Here, 8 is the base and 2 is the exponent. 40 | // exponent(4,3); // 64 41 | // https://www.khanacademy.org/computing/computer-science/algorithms/recursive-algorithms/a/computing-powers-of-a-number 42 | var exponent = function(base, exp) { 43 | }; 44 | 45 | // 8. Determine if a number is a power of two. 46 | // powerOfTwo(1); // true 47 | // powerOfTwo(16); // true 48 | // powerOfTwo(10); // false 49 | var powerOfTwo = function(n) { 50 | }; 51 | 52 | // 9. Write a function that reverses a string. 53 | var reverse = function(string) { 54 | }; 55 | 56 | // 10. Write a function that determines if a string is a palindrome. 57 | var palindrome = function(string) { 58 | }; 59 | 60 | // 11. Write a function that returns the remainder of x divided by y without using the 61 | // modulo (%) operator. 62 | // modulo(5,2) // 1 63 | // modulo(17,5) // 2 64 | // modulo(22,6) // 4 65 | var modulo = function(x, y) { 66 | }; 67 | 68 | // 12. Write a function that multiplies two numbers without using the * operator or 69 | // Math methods. 70 | var multiply = function(x, y) { 71 | }; 72 | 73 | // 13. Write a function that divides two numbers without using the / operator or 74 | // Math methods. 75 | var divide = function(x, y) { 76 | }; 77 | 78 | // 14. Find the greatest common divisor (gcd) of two positive numbers. The GCD of two 79 | // integers is the greatest integer that divides both x and y with no remainder. 80 | // gcd(4,36); // 4 81 | // http://www.cse.wustl.edu/~kjg/cse131/Notes/Recursion/recursion.html 82 | // https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/the-euclidean-algorithm 83 | var gcd = function(x, y) { 84 | }; 85 | 86 | // 15. Write a function that compares each character of two strings and returns true if 87 | // both are identical. 88 | // compareStr('house', 'houses') // false 89 | // compareStr('tomato', 'tomato') // true 90 | var compareStr = function(str1, str2) { 91 | }; 92 | 93 | // 16. Write a function that accepts a string and creates an array where each letter 94 | // occupies an index of the array. 95 | var createArray = function(str) { 96 | }; 97 | 98 | // 17. Reverse the order of an array 99 | var reverseArr = function(array) { 100 | }; 101 | 102 | // 18. Create a new array with a given value and length. 103 | // buildList(0,5) // [0,0,0,0,0] 104 | // buildList(7,3) // [7,7,7] 105 | var buildList = function(value, length) { 106 | }; 107 | 108 | // 19. Implement FizzBuzz. Given integer n, return an array of the string representations of 1 to n. 109 | // For multiples of three, output 'Fizz' instead of the number. 110 | // For multiples of five, output 'Buzz' instead of the number. 111 | // For numbers which are multiples of both three and five, output “FizzBuzz” instead of the number. 112 | // fizzBuzz(5) // ['1','2','Fizz','4','Buzz'] 113 | var fizzBuzz = function(n) { 114 | }; 115 | 116 | // 20. Count the occurence of a value in a list. 117 | // countOccurrence([2,7,4,4,1,4], 4) // 3 118 | // countOccurrence([2,'banana',4,4,1,'banana'], 'banana') // 2 119 | var countOccurrence = function(array, value) { 120 | }; 121 | 122 | // 21. Write a recursive version of map. 123 | // rMap([1,2,3], timesTwo); // [2,4,6] 124 | var rMap = function(array, callback) { 125 | }; 126 | 127 | // 22. Write a function that counts the number of times a key occurs in an object. 128 | // var obj = {'e':{'x':'y'},'t':{'r':{'e':'r'},'p':{'y':'r'}},'y':'e'}; 129 | // countKeysInObj(obj, 'r') // 1 130 | // countKeysInObj(obj, 'e') // 2 131 | var countKeysInObj = function(obj, key) { 132 | }; 133 | 134 | // 23. Write a function that counts the number of times a value occurs in an object. 135 | // var obj = {'e':{'x':'y'},'t':{'r':{'e':'r'},'p':{'y':'r'}},'y':'e'}; 136 | // countValuesInObj(obj, 'r') // 2 137 | // countValuesInObj(obj, 'e') // 1 138 | var countValuesInObj = function(obj, value) { 139 | }; 140 | 141 | // 24. Find all keys in an object (and nested objects) by a provided name and rename 142 | // them to a provided new name while preserving the value stored at that key. 143 | var replaceKeysInObj = function(obj, oldKey, newKey) { 144 | }; 145 | 146 | // 25. Get the first n Fibonacci numbers. In the Fibonacci sequence, each subsequent 147 | // number is the sum of the previous two. 148 | // Example: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34..... 149 | // fibonacci(5); // [0,1,1,2,3,5] 150 | // Note: The 0 is not counted. 151 | var fibonacci = function(n) { 152 | }; 153 | 154 | // 26. Return the Fibonacci number located at index n of the Fibonacci sequence. 155 | // [0,1,1,2,3,5,8,13,21] 156 | // nthFibo(5); // 5 157 | // nthFibo(7); // 13 158 | // nthFibo(3); // 2 159 | var nthFibo = function(n) { 160 | }; 161 | 162 | // 27. Given an array of words, return a new array containing each word capitalized. 163 | // var words = ['i', 'am', 'learning', 'recursion']; 164 | // capitalizedWords(words); // ['I', 'AM', 'LEARNING', 'RECURSION'] 165 | var capitalizeWords = function(array) { 166 | }; 167 | 168 | // 28. Given an array of strings, capitalize the first letter of each index. 169 | // capitalizeFirst(['car','poop','banana']); // ['Car','Poop','Banana'] 170 | var capitalizeFirst = function(array) { 171 | }; 172 | 173 | // 29. Return the sum of all even numbers in an object containing nested objects. 174 | // var obj1 = { 175 | // a: 2, 176 | // b: {b: 2, bb: {b: 3, bb: {b: 2}}}, 177 | // c: {c: {c: 2}, cc: 'ball', ccc: 5}, 178 | // d: 1, 179 | // e: {e: {e: 2}, ee: 'car'} 180 | // }; 181 | // nestedEvenSum(obj1); // 10 182 | var nestedEvenSum = function(obj) { 183 | }; 184 | 185 | // 30. Flatten an array containing nested arrays. 186 | // flatten([1,[2],[3,[[4]]],5]); // [1,2,3,4,5] 187 | var flatten = function(array) { 188 | }; 189 | 190 | // 31. Given a string, return an object containing tallies of each letter. 191 | // letterTally('potato'); // {p:1, o:2, t:2, a:1} 192 | var letterTally = function(str, obj) { 193 | }; 194 | 195 | // 32. Eliminate consecutive duplicates in a list. If the list contains repeated 196 | // elements they should be replaced with a single copy of the element. The order of the 197 | // elements should not be changed. 198 | // compress([1,2,2,3,4,4,5,5,5]) // [1,2,3,4,5] 199 | // compress([1,2,2,3,4,4,2,5,5,5,4,4]) // [1,2,3,4,2,5,4] 200 | var compress = function(list) { 201 | }; 202 | 203 | // 33. Augument every element in a list with a new value where each element is an array 204 | // itself. 205 | // augmentElements([[],[3],[7]], 5); // [[5],[3,5],[7,5]] 206 | var augmentElements = function(array, aug) { 207 | }; 208 | 209 | // 34. Reduce a series of zeroes to a single 0. 210 | // minimizeZeroes([2,0,0,0,1,4]) // [2,0,1,4] 211 | // minimizeZeroes([2,0,0,0,1,0,0,4]) // [2,0,1,0,4] 212 | var minimizeZeroes = function(array) { 213 | }; 214 | 215 | // 35. Alternate the numbers in an array between positive and negative regardless of 216 | // their original sign. The first number in the index always needs to be positive. 217 | // alternateSign([2,7,8,3,1,4]) // [2,-7,8,-3,1,-4] 218 | // alternateSign([-2,-7,8,3,-1,4]) // [2,-7,8,-3,1,-4] 219 | var alternateSign = function(array) { 220 | }; 221 | 222 | // 36. Given a string, return a string with digits converted to their word equivalent. 223 | // Assume all numbers are single digits (less than 10). 224 | // numToText("I have 5 dogs and 6 ponies"); // "I have five dogs and six ponies" 225 | var numToText = function(str) { 226 | }; 227 | 228 | 229 | // *** EXTRA CREDIT *** 230 | 231 | // 37. Return the number of times a tag occurs in the DOM. 232 | var tagCount = function(tag, node) { 233 | }; 234 | 235 | // 38. Write a function for binary search. 236 | // var array = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]; 237 | // binarySearch(array, 5) // 5 238 | // https://www.khanacademy.org/computing/computer-science/algorithms/binary-search/a/binary-search 239 | var binarySearch = function(array, target, min, max) { 240 | }; 241 | 242 | // 39. Write a merge sort function. 243 | // mergeSort([34,7,23,32,5,62]) // [5,7,23,32,34,62] 244 | // https://www.khanacademy.org/computing/computer-science/algorithms/merge-sort/a/divide-and-conquer-algorithms 245 | var mergeSort = function(array) { 246 | }; 247 | 248 | // 40. Deeply clone objects and arrays. 249 | // var obj1 = {a:1,b:{bb:{bbb:2}},c:3}; 250 | // var obj2 = clone(obj1); 251 | // console.log(obj2); // {a:1,b:{bb:{bbb:2}},c:3} 252 | // obj1 === obj2 // false 253 | var clone = function(input) { 254 | }; 255 | --------------------------------------------------------------------------------