├── .editorconfig ├── .gitignore ├── README.md ├── async ├── README.md ├── package.json └── test.js ├── binary-search ├── README.md ├── package.json └── test.js ├── curry ├── README.md ├── package.json └── test.js ├── debounce ├── README.md ├── package.json └── test.js ├── flatten-thunk ├── README.md ├── package.json └── test.js ├── flatten ├── README.md ├── package.json └── test.js ├── invert-tree ├── README.md ├── package.json └── test.js ├── jasmine-async ├── README.md ├── package.json └── test.js ├── map ├── README.md ├── package.json └── test.js ├── memoize ├── README.md ├── package.json └── test.js ├── merkle ├── README.md ├── package.json └── test.js ├── middleware ├── README.md ├── package.json └── test.js ├── morse-code ├── README.md ├── codes.json ├── package.json └── test.js ├── once ├── README.md ├── package.json └── test.js ├── package.json ├── sort ├── README.md ├── package.json └── test.js ├── throttle-promises ├── README.md ├── package.json └── test.js ├── throttle ├── README.md ├── package.json └── test.js ├── update ├── README.md ├── package.json └── test.js └── value ├── README.md ├── package.json └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gitter][gitter-image]][gitter-url] 2 | 3 | These are some basic (and advanced) coding challenges 4 | 5 | Here's the basic workflow: 6 | 7 | ```bash 8 | [~] $ git clone https://github.com/kolodny/exercises 9 | [~] $ cd exercises 10 | [exercises] $ npm install 11 | [exercises] $ cd debounce 12 | [debounce] $ vi index.js 13 | [debounce] $ npm test 14 | ``` 15 | This uses a basic TDD approach so take a look at the test.js file in each directory to see what needs to be implemented, write an index.js as the solution file 16 | 17 | ### Contributing 18 | 19 | Pull requests welcome, please follow the basic workflow: 20 | 21 | 1. Make a folder 22 | 2. Copy a package.json from a sibling folder 23 | 3. Make a test.js file 24 | 4. Optionally provide a README.md 25 | 26 | [gitter-image]: https://badges.gitter.im/Join%20Chat.svg 27 | [gitter-url]: https://gitter.im/kolodny/exercises 28 | -------------------------------------------------------------------------------- /async/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var async = require('./') // <- this is the file you make; 5 | 6 | var getUser = function(userId) { 7 | return function(cb) { 8 | setTimeout(function() { 9 | cb(null, {userId: userId, name: 'Joe'}); 10 | }, Math.random() * 100); 11 | }; 12 | }; 13 | 14 | var upperCaseName = function(cb, user) { 15 | cb(null, user.name.toUpperCase()); 16 | }; 17 | 18 | var userThunk = getUser(22); 19 | 20 | async.sequence([userThunk, upperCaseName])(function(err, data) { 21 | console.log(data); // JOE 22 | }); 23 | 24 | var userThunk1 = getUser(1); 25 | var userThunk2 = getUser(2); 26 | 27 | async.parallel([userThunk1, userThunk2])(function(err, users) { 28 | console.log(users); // [ { userId: 1, name: 'Joe' }, { userId: 2, name: 'Joe' } ] 29 | }); 30 | 31 | var faster = function(cb) { 32 | setTimeout(cb.bind(null, null, "I'm faster"), 10); 33 | } 34 | async.race([userThunk1, faster])(function(err, winner) { 35 | console.log(winner); // I'm faster 36 | }); 37 | ``` 38 | -------------------------------------------------------------------------------- /async/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /async/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var async = require('./'); 3 | 4 | describe('async', function() { 5 | 6 | describe('has a sequence method that', function() { 7 | it('runs functions in sequence', function(done) { 8 | var fun1 = function(cb) { 9 | setTimeout(cb.bind(null, null, 'test'), 10); 10 | }; 11 | var fun2 = function(cb, data) { 12 | setTimeout(cb.bind(null, null, data + 'ing'), 10); 13 | }; 14 | 15 | // returns a thunk 16 | async.sequence([fun1, fun2])(function(err, data) { 17 | assert.equal(data, 'testing'); 18 | done(); 19 | }); 20 | }); 21 | 22 | it('correctly handles sync functions in sequence', function(done) { 23 | var fun1 = function(cb) { 24 | cb(null, 'test1'); 25 | }; 26 | var fun2 = function(cb, data) { 27 | cb(null, data); 28 | }; 29 | 30 | // returns a thunk 31 | async.sequence([fun1, fun2])(function(err, data) { 32 | assert.equal(data, 'test1'); 33 | done(); 34 | }); 35 | }); 36 | 37 | it('handles delayed thunk invocation', function(done) { 38 | var fun1 = function(cb) { 39 | cb(null, 'test2'); 40 | }; 41 | var fun2 = function(cb, data) { 42 | cb(null, data.toUpperCase()); 43 | }; 44 | 45 | // returns a thunk 46 | var setter = async.sequence([fun1, fun2]) 47 | 48 | setTimeout(function() { 49 | setter(function(err, data) { 50 | assert.equal(data, 'TEST2'); 51 | done(); 52 | }); 53 | }, 100) 54 | }); 55 | }); 56 | 57 | describe('has a parallel method that', function() { 58 | it('runs functions in parallel', function(done) { 59 | var fun1 = function(cb) { 60 | setTimeout(cb.bind(null, null, 'test'), 10); 61 | }; 62 | var fun2 = function(cb) { 63 | setTimeout(cb.bind(null, null, 'ing'), 10); 64 | }; 65 | 66 | // returns a thunk 67 | async.parallel([fun1, fun2])(function(err, data) { 68 | assert.deepEqual(data, ['test', 'ing']); 69 | done(); 70 | }); 71 | }); 72 | }); 73 | 74 | describe('has a race method that', function() { 75 | it('uses the first completing function', function(done) { 76 | var fun1 = function(ms) { 77 | return function(cb) { 78 | setTimeout(cb.bind(null, null, 'test'), ms); 79 | }; 80 | }; 81 | var fun2 = function(ms) { 82 | return function(cb) { 83 | setTimeout(cb.bind(null, null, 'ing'), ms); 84 | }; 85 | }; 86 | 87 | // returns a thunk 88 | async.race([fun1(10), fun2(20)])(function(err, data) { 89 | assert.equal(data, 'test'); 90 | async.race([fun1(20), fun2(10)])(function(err, data) { 91 | assert.equal(data, 'ing'); 92 | done(); 93 | }); 94 | 95 | }); 96 | }); 97 | }); 98 | 99 | }); 100 | -------------------------------------------------------------------------------- /binary-search/README.md: -------------------------------------------------------------------------------- 1 | A binary search is an algorithm where you look for an item in a sorted array using a divide and conquer technique 2 | 3 | The classic example given is looking for a name in a phonebook, you first look in the middle and if the name is higher you take the upper half and start the process again, ditto if it was lower 4 | 5 | Imagine a bunch of words in a list 6 | 7 | ```js 8 | ['apple', 'banana', 'cherry', 'dates', 'eggs', 'figs', 'grapes'] 9 | ``` 10 | 11 | If we looks for `cherry` then we first look at the middle of the array we have: 12 | 13 | ```js 14 | VVV 15 | ['apple', 'banana', 'cherry', 'dates', 'eggs', 'figs', 'grapes'] 16 | ``` 17 | 18 | Since `dates` are larger than `cherry` we need to take the lower half of the array and try again: 19 | 20 | ```js 21 | VVVV 22 | ['apple', 'banana', 'cherry', 23 | ``` 24 | 25 | Now `banana` is the middle so we take the larger part of the array and try again: 26 | 27 | ```js 28 | VVVV 29 | ['cherry'] 30 | ``` 31 | 32 | We found it! 33 | 34 | Think about how to write this algorithm and keep in mind edge cases like how to not get caught in an infinite loop if it's not found, or what to do when there are an even number of elements 35 | 36 | Here's the basic usage of the file that you'll be creating: 37 | 38 | ```js 39 | var search = require('./') // <- this is the file you make; 40 | 41 | var arr = ['apple', 'banana', 'cherry', 'dates', 'eggs', 'figs', 'grapes']; 42 | 43 | var foundAt = search(arr, 'cherry'); // 2 since arr[2] === 'cherry' 44 | 45 | foundAt = search(arr, 'zebra') // -1 for not found 46 | ``` 47 | 48 | More info: https://en.wikipedia.org/wiki/Binary_search_algorithm 49 | -------------------------------------------------------------------------------- /binary-search/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /binary-search/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var binarySearch = require('./'); 3 | 4 | var generateRandomNumberArray = function(arrayLength) { 5 | var arr = new Array(arrayLength); 6 | for (var i = 0; i < arrayLength; i++) { 7 | arr[i] = Math.random(); 8 | } 9 | return arr.sort(); 10 | } 11 | 12 | describe('binarySearch', function() { 13 | 14 | it('works on an empty array', function() { 15 | var arr = []; 16 | var index = binarySearch(arr, 'elem'); 17 | assert.equal(index, -1); 18 | }); 19 | 20 | it('works on a not found call in an array of one item', function() { 21 | var arr = generateRandomNumberArray(1); 22 | var index = binarySearch(arr, arr[0] + 1); 23 | assert.equal(index, -1); 24 | }); 25 | 26 | it('works on a found call in an array of one item', function() { 27 | var arr = generateRandomNumberArray(1); 28 | var index = binarySearch(arr, arr[0]); 29 | assert.equal(index, 0); 30 | }); 31 | 32 | it('works on a not found call in an array of many items', function() { 33 | var arr = generateRandomNumberArray(15); 34 | var index = binarySearch(arr, arr[14] + 1); 35 | assert.equal(index, -1); 36 | }); 37 | 38 | it('works on a found call in an array of many items', function() { 39 | var arr = generateRandomNumberArray(15); 40 | var index = binarySearch(arr, arr[0]); 41 | assert.equal(index, 0); 42 | }); 43 | 44 | it('works on a found call in an array of many items higher in the array', function() { 45 | var arr = generateRandomNumberArray(15); 46 | var index = binarySearch(arr, arr[14]); 47 | assert.equal(index, 14); 48 | }); 49 | 50 | it('uses a divide and conquer algorithm', function() { 51 | var lookups = {}; 52 | var arrayLength = 10000; 53 | var proxyArray = generateRandomNumberArray(arrayLength); 54 | var smartArray = []; 55 | for (var i = 0; i < 10000; i++) { 56 | (function(i) { 57 | Object.defineProperty(smartArray, i, { 58 | get: function() { 59 | lookups[i] = true; 60 | return proxyArray[i]; 61 | }, 62 | }); 63 | })(i); 64 | } 65 | var indexOfElement = arrayLength - 4; 66 | var lookingFor = proxyArray[indexOfElement]; 67 | var index = binarySearch(smartArray, lookingFor); 68 | assert.equal(index, indexOfElement); 69 | assert.equal(Object.keys(lookups).length, 13); 70 | }); 71 | 72 | 73 | 74 | }); 75 | -------------------------------------------------------------------------------- /curry/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var curry = require('./') // <- this is the file you make; 5 | 6 | function add(a, b) { 7 | return a + b; 8 | } 9 | 10 | var curried = curry(add); 11 | console.log( curried(1)(2) ); // 3 12 | ``` 13 | 14 | More info: https://en.wikipedia.org/wiki/Currying 15 | -------------------------------------------------------------------------------- /curry/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /curry/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var curry = require('./'); 3 | 4 | describe('curry', function() { 5 | 6 | it('curries the function at least once', function() { 7 | var add = curry(function(a, b) { 8 | return a + b; 9 | }); 10 | assert.equal(add(1)(2), 3); 11 | }); 12 | 13 | it('curries the function even with a single argument', function() { 14 | var output = curry(function(n) { 15 | return n; 16 | }); 17 | assert.equal(output(1), 1); 18 | }); 19 | 20 | it('curries the function until the arguments needed are given at least once', function() { 21 | var add = curry(function(a, b, c) { 22 | return a + b + c; 23 | }); 24 | assert.equal(add(1, 2)(3), 6); 25 | }); 26 | 27 | it('curries the function until the arguments needed are given multiple times', function() { 28 | var add = curry(function(a, b, c) { 29 | return a + b + c; 30 | }); 31 | assert.equal(add(1)(2)(3), 6); 32 | }); 33 | 34 | it("doesn't share state between calls", function() { 35 | var add = curry(function(a, b, c) { 36 | return a + b + c; 37 | }); 38 | assert.equal(add(1)(2)(3), 6); 39 | assert.equal(add(2)(3)(4), 9); 40 | }); 41 | 42 | it("doesn't only work with addition", function() { 43 | var merge = curry(function(a, b, c) { 44 | return [a, b, c].join(', '); 45 | }); 46 | assert.equal(merge('1')(2)(3), '1, 2, 3'); 47 | }); 48 | 49 | it("doesn't share state between inner calls", function() { 50 | var add = curry(function(a, b, c, d) { 51 | return a + b + c + d; 52 | }); 53 | var firstTwo = add(1)(2); 54 | assert.equal(firstTwo(3)(4), 10); 55 | var firstThree = firstTwo(5); 56 | assert.equal(firstThree(6), 14); 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /debounce/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var debounce = require('./') // <- this is the file you make; 5 | 6 | var sayHi = function() { 7 | console.log('hi'); 8 | }; 9 | 10 | var debounced = debounce(sayHi, 100); 11 | 12 | debounced(); 13 | debounced(); 14 | debounced(); 15 | debounced(); 16 | 17 | // there should only be one 'hi' message on the console 18 | ``` 19 | 20 | More info: http://underscorejs.org/#debounce 21 | 22 | 23 | ### Difference between debounce and throttle 24 | There's a lot in common between debounce and throttle, [people sometimes confuse the two](https://github.com/kolodny/exercises/issues/3#issuecomment-111623806). The general difference between them is that throttle is used to make sure the function doesn't execute more than once per x milliseconds as opposed to debounce which makes sure that the function doesn't execute until x milliseconds passed without it being called. 25 | 26 | For example: Let's say that you have a page that darkens the background based on how far down the page the user scrolled and also saves the scroll location on the server. In the case of smooth scrolling this function can end up being called hundreds of times per second. Assuming that checking the page scroll involves some fancy math and DOM touching this can end up causing the function to still be running as the next scroll event happens :( . 27 | 28 | Let's assume the user is continuously scrolling the page... 29 | 30 | In the case of darkening the background you would use a throttle function because you want to still darken the background even as new scroll events happen, just not as often as they come in. 31 | 32 | In the case of saving the scroll position on the server, you wouldn't want it to be saved until after the user is done scrolling, so you would use the debounce function. 33 | -------------------------------------------------------------------------------- /debounce/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /debounce/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var debounce = require('./'); 3 | 4 | describe('debounce', function() { 5 | it('waits for the threshold to pass before executing', function(done) { 6 | var now = new Date(); 7 | var debounced = debounce(function() { 8 | assert(new Date() - now >= 10); 9 | done(); 10 | }, 10); 11 | debounced(); 12 | }); 13 | 14 | it("won't execute more than once within the threshold", function(done) { 15 | var called = 0; 16 | var debounced = debounce(function() { 17 | called++; 18 | }, 10); 19 | debounced(); 20 | debounced(); 21 | debounced(); 22 | setTimeout(function() { 23 | assert.equal(called, 1); 24 | done(); 25 | }, 15); 26 | }); 27 | 28 | it("will execute more than once outside the threshold", function(done) { 29 | var called = 0; 30 | var debounced = debounce(function() { 31 | called++; 32 | }, 10); 33 | debounced(); 34 | setTimeout(debounced, 5); 35 | setTimeout(debounced, 20); 36 | setTimeout(function() { 37 | assert.equal(called, 2); 38 | done(); 39 | }, 45); 40 | }); 41 | 42 | it('gets called with context', function(done) { 43 | var ctx; 44 | var debounced = debounce(function() { 45 | ctx = this; 46 | }, 10); 47 | debounced.call(11); 48 | debounced.call(22); 49 | setTimeout(function() { 50 | assert.equal(ctx, 22); 51 | done(); 52 | }, 15) 53 | }); 54 | 55 | it('gets called with arguments', function(done) { 56 | var args; 57 | var debounced = debounce(function() { 58 | args = [].slice.call(arguments); 59 | }, 10); 60 | debounced(11, 22, 33); 61 | debounced(22, 33, 44); 62 | setTimeout(function() { 63 | assert.deepEqual(args, [22, 33, 44]); 64 | done(); 65 | }, 15); 66 | }); 67 | 68 | 69 | }); 70 | -------------------------------------------------------------------------------- /flatten-thunk/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var flattenThunk = require('./') // <- this is the file you make; 5 | 6 | var thunk1 = function(cb) { 7 | setTimeout(function() { 8 | cb(null, 'done'); 9 | }, 1); 10 | } 11 | var thunk2 = function(cb) { 12 | setTimeout(function() { 13 | cb(null, thunk1); 14 | }, 1); 15 | } 16 | var thunk3 = function(cb) { 17 | setTimeout(function() { 18 | cb(null, thunk2); 19 | }, 1); 20 | } 21 | 22 | flattenThunk(thunk3)(function(err, result) { 23 | console.log(result); // 'done' 24 | }); 25 | ``` 26 | 27 | A thunk is basically a function that you call with just the callback as a parameter: 28 | 29 | ```js 30 | 31 | // this is a regular node CPS function 32 | fs.readFile('package.json', function(err, result) { 33 | console.log(result); 34 | }); 35 | 36 | // this is a thunk 37 | var readFileThunk = fs.readFileThunkily('package.json'); 38 | readFileThunk(function(err, result) { 39 | console.log(result); 40 | }); 41 | ``` 42 | 43 | More info: https://github.com/tj/node-thunkify 44 | -------------------------------------------------------------------------------- /flatten-thunk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /flatten-thunk/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var flattenThunk = require('./'); 3 | 4 | describe('flattenThunk', function() { 5 | 6 | it('flattens the promises', function(done) { 7 | 8 | var thunk1 = function(cb) { 9 | setTimeout(function() { 10 | cb(null, 'done'); 11 | }, 1); 12 | } 13 | var thunk2 = function(cb) { 14 | setTimeout(function() { 15 | cb(null, thunk1); 16 | }, 1); 17 | } 18 | var thunk3 = function(cb) { 19 | setTimeout(function() { 20 | cb(null, thunk2); 21 | }, 1); 22 | } 23 | 24 | flattenThunk(thunk3)(function(err, result) { 25 | assert.equal(result, 'done'); 26 | done(); 27 | }); 28 | }); 29 | 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /flatten/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var flatten = require('./') // <- this is the file you make; 5 | 6 | var arr = [1, [2], [3, 4, [5]]]; 7 | 8 | flatten(arr); 9 | // => [1, 2, 3, 4, 5]; 10 | 11 | ``` 12 | -------------------------------------------------------------------------------- /flatten/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /flatten/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var flatten = require('./'); 3 | 4 | describe('flatten', function() { 5 | var arr = [1, [2], [3, 4, [5]]]; 6 | 7 | it('will return another array', function() { 8 | assert.notEqual(flatten(arr), arr); 9 | }); 10 | 11 | it('will flatten an simple array', function() { 12 | assert.deepEqual(flatten([1, 2, 3, 4, [5]]), [1, 2, 3, 4, 5]); 13 | }); 14 | 15 | it('will flatten an array', function() { 16 | assert.deepEqual(flatten(arr), [1, 2, 3, 4, 5]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /invert-tree/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var invertTree = require('./'); // <- this is the file you make 5 | 6 | var root = {value: 6}; 7 | var left = {value: 4}; 8 | var right = {value: 8}; 9 | var leftOfLeft = {value: 3}; 10 | var rightOfLeft = {value: 5}; 11 | var leftOfRight = {value: 7}; 12 | var rightOfRight = {value: 9}; 13 | root.left = left; 14 | root.right = right; 15 | left.left = leftOfLeft; 16 | left.right = rightOfLeft; 17 | right.left = leftOfRight; 18 | right.right = rightOfRight; 19 | 20 | invertTree(root); 21 | 22 | console.log(root.left.left.value); // should be 9 23 | ``` 24 | 25 | More info: https://en.wikipedia.org/?title=Binary_tree 26 | Also https://twitter.com/mxcl/status/608682016205344768 :wink: 27 | -------------------------------------------------------------------------------- /invert-tree/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /invert-tree/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var invertTree = require('./'); 3 | 4 | describe('invert-tree', function() { 5 | it('inverts a binary tree', function() { 6 | var root = {value: 6}; 7 | var left = {value: 4}; 8 | var right = {value: 8}; 9 | var leftOfLeft = {value: 3}; 10 | var rightOfLeft = {value: 5}; 11 | var leftOfRight = {value: 7}; 12 | var rightOfRight = {value: 9}; 13 | root.left = left; 14 | root.right = right; 15 | left.left = leftOfLeft; 16 | left.right = rightOfLeft; 17 | right.left = leftOfRight; 18 | right.right = rightOfRight; 19 | 20 | invertTree(root); 21 | 22 | assert.deepEqual(root, { 23 | "value": 6, 24 | "left": { 25 | "value": 8, 26 | "left": { 27 | "value": 9 28 | }, 29 | "right": { 30 | "value": 7 31 | } 32 | }, 33 | "right": { 34 | "value": 4, 35 | "left": { 36 | "value": 5 37 | }, 38 | "right": { 39 | "value": 3 40 | } 41 | } 42 | }); 43 | }); 44 | 45 | it('inverts a right-leaning asymmetric tree', function() { 46 | var root = {value: 4}; 47 | var right = {value: 6}; 48 | var rightOfRight = {value: 8}; 49 | root.right = right; 50 | right.right = rightOfRight; 51 | 52 | invertTree(root); 53 | 54 | root = JSON.parse(JSON.stringify(root)); 55 | 56 | assert.deepEqual(root, { 57 | "value": 4, 58 | "left": { 59 | "value": 6, 60 | "left": { 61 | value: 8 62 | } 63 | } 64 | }); 65 | }); 66 | 67 | it('inverts a left-leaning asymmetric tree', function() { 68 | var root = {value: 5}; 69 | var left = {value: 7}; 70 | var leftOfLeft = {value: 9}; 71 | root.left = left; 72 | left.left = leftOfLeft; 73 | 74 | invertTree(root); 75 | 76 | root = JSON.parse(JSON.stringify(root)); 77 | 78 | assert.deepEqual(root, { 79 | "value": 5, 80 | "right": { 81 | "value": 7, 82 | "right": { 83 | value: 9 84 | } 85 | } 86 | }); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /jasmine-async/README.md: -------------------------------------------------------------------------------- 1 | Jasmine 1.3 doesn't really support async testing, instead it exposes two functions `runs` and `waitsFor`. You need to write it along these lines: 2 | 3 | ```js 4 | it("does something async", function() { 5 | var flag = false; 6 | 7 | runs(function() { 8 | setTimeout(function() { 9 | flag = true; 10 | }, 500); 11 | }); 12 | 13 | waitsFor(function() { 14 | return flag; 15 | }, 750); 16 | 17 | runs(function() { 18 | console.log('asserting...'); 19 | assert(flag === true); 20 | }); 21 | }); 22 | ``` 23 | 24 | Assuming `it`, `runs`, and `waitFor` are available in scope, your job is to write a function thats usage is like this: 25 | 26 | ```js 27 | var itWill = require('./') // <- this is the file you make; 28 | 29 | itWill(function() { 30 | var flag = false; 31 | return { 32 | desc: "does something async", 33 | setup: function(done) { 34 | setTimeout(function() { 35 | flag = true; 36 | done(); 37 | }, 500); 38 | }, 39 | test: function() { 40 | console.log('asserting...'); 41 | assert(flag === true); 42 | } 43 | }; 44 | }); 45 | ``` 46 | 47 | More info: http://jasmine.github.io/1.3/introduction.html#section-Asynchronous_Support 48 | -------------------------------------------------------------------------------- /jasmine-async/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /jasmine-async/test.js: -------------------------------------------------------------------------------- 1 | var inject = (function() { 2 | var assert = require('assert'); 3 | var thingsToDo = []; 4 | var runs = function(runFn) { 5 | thingsToDo.push({type: 'run', todo: runFn}); 6 | }; 7 | var waitsFor = function(waitsForFn) { 8 | thingsToDo.push({type: 'waitsFor', todo: waitsForFn}); 9 | }; 10 | var it = function(desc, fn) { 11 | fn(); 12 | assert.equal(thingsToDo[0].type, 'run'); 13 | assert.equal(thingsToDo[1].type, 'waitsFor'); 14 | assert.equal(thingsToDo[2].type, 'run'); 15 | thingsToDo[0].todo(); 16 | next(); 17 | function next() { 18 | if (thingsToDo[1].todo()) { 19 | thingsToDo[2].todo(); 20 | } else { 21 | setTimeout(next, 1); 22 | } 23 | } 24 | }; 25 | 26 | }).toString().split('\n').slice(1, -1).join('\n'); 27 | 28 | var assert = require('assert'); 29 | var fs = require('fs'); 30 | var indexContents = fs.readFileSync(__dirname + '/index.js').toString(); 31 | fs.writeFileSync(__dirname + '/index__TMP__.js', indexContents + inject); 32 | var jasmineAsync = require(__dirname + '/index__TMP__.js'); 33 | fs.unlinkSync(__dirname + '/index__TMP__.js'); 34 | 35 | var mochaIt = it; 36 | 37 | describe('jasmineAsync', function() { 38 | 39 | mochaIt('wraps the async weirdness', function(done) { 40 | 41 | jasmineAsync(function() { 42 | var test = 0; 43 | return { 44 | desc: 'a test', 45 | setup: function(doneJasmine) { 46 | setTimeout(function() {test++;}, 5); 47 | setTimeout(doneJasmine, 10); 48 | setTimeout(function() {test++;}, 20); 49 | }, 50 | test: function() { 51 | assert.equal(test, 1); 52 | done(); 53 | } 54 | }; 55 | }) 56 | 57 | }); 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /map/README.md: -------------------------------------------------------------------------------- 1 | You might know about [map](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map) method, let's implement your own `map` one. 2 | 3 | `map` should function like `map` does: 4 | 5 | ```js 6 | var map = require('./') // <- this is the file you make; 7 | 8 | var numbers = [1, 2, 3]; 9 | 10 | var doubles = map(numbers, function(number) { 11 | return number * 2; 12 | }); 13 | 14 | console.log(doubles); // [2, 4, 6] 15 | 16 | ``` 17 | 18 | More info: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map 19 | -------------------------------------------------------------------------------- /map/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /map/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var NativeMap = Array.prototype.map; 4 | var calledNativeMap; 5 | Array.prototype.map = function() { 6 | calledNativeMap = true; 7 | return NativeMap.apply(this, arguments); 8 | } 9 | 10 | var map = require('./'); 11 | 12 | 13 | 14 | describe('map', function() { 15 | 16 | it('should not use the native map', function() { 17 | calledNativeMap = false; 18 | map(['x'], function(x) { return x; }); 19 | assert(!calledNativeMap); 20 | }); 21 | 22 | it('should return another array', function() { 23 | var arr = [1, 2, 3]; 24 | var mapped = map(arr, function() {}); 25 | 26 | assert.notEqual(arr, mapped); 27 | }); 28 | 29 | it('should return the right results', function() { 30 | var arr = [1, 2, 3]; 31 | var mapped = map(arr, function(value) { 32 | return value * 2; 33 | }); 34 | 35 | assert.deepEqual(mapped, [2, 4, 6]); 36 | }); 37 | 38 | it('callback will be called once for each element', function() { 39 | var called = 0; 40 | var arr = [1, 2, 3]; 41 | 42 | map(arr, function(value) { 43 | called++; 44 | return value; 45 | }); 46 | 47 | assert.equal(called, 3); 48 | }); 49 | 50 | it('callback will be invoked with three arguments', function() { 51 | var args; 52 | var arr = [1]; 53 | 54 | map(arr, function() { 55 | args = arguments.length; 56 | }); 57 | 58 | assert.equal(args, 3); 59 | }); 60 | 61 | it("callback's arguments (value, index, array) should have right values", function() { 62 | var i = 0; 63 | var arr = [1, 2, 3]; 64 | 65 | map(arr, function(value, index, array) { 66 | assert.equal(value, arr[i]); 67 | assert.equal(index, i); 68 | assert.deepEqual(arr, array); 69 | i++; 70 | }); 71 | }); 72 | 73 | it('callback should gets called with context', function() { 74 | var ctx; 75 | var arr = [5]; 76 | 77 | map(arr, function() { 78 | ctx = this; 79 | }, 3); 80 | 81 | assert.equal(ctx, 3); 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /memoize/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var memoize = require('./') // <- this is the file you make; 5 | 6 | function expensiveOperation() { 7 | console.log('this should be shown once'); 8 | return 22; 9 | } 10 | 11 | var memoized = memoize(expensiveOperation); 12 | console.log(memoized()); 13 | console.log(memoized()); 14 | 15 | // the console should show: 16 | // this should be shown once 17 | // 22 18 | // 22 19 | 20 | ``` 21 | 22 | More info: http://en.wikipedia.org/wiki/Memoization 23 | -------------------------------------------------------------------------------- /memoize/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /memoize/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var memoize = require('./'); 3 | 4 | describe('memoize', function() { 5 | 6 | it('can handle a single argument', function() { 7 | var called = 0; 8 | var fib = memoize(function(n) { 9 | called++; 10 | if (n < 2) return n; 11 | return fib(n - 1) + fib(n - 2); 12 | }); 13 | fib(10); 14 | assert.equal(called, 11); 15 | }); 16 | 17 | it('can handle multiple arguments', function() { 18 | var called = 0; 19 | var fib = memoize(function(n, unused) { 20 | called++; 21 | if (n < 2) return n; 22 | return fib(n - 1, unused) + fib(n - 2, unused); 23 | }); 24 | fib(10, 'x'); 25 | fib(10, 'y'); 26 | assert.equal(called, 22); 27 | 28 | }); 29 | 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /merkle/README.md: -------------------------------------------------------------------------------- 1 | A merkle tree is an algorithm that takes a list of values and recursively reduces them by hashing them in pairs of two until a single value remains 2 | 3 | ### HUH? 4 | 5 | Let's say I have a bunch of contacts in my contacts list, assume I have 8 contacts, if I wanted to create a merkle tree from this I would first line them up as follows: 6 | 7 | ``` 8 | C1 C2 C3 C4 C5 C6 C7 C8 9 | ``` 10 | 11 | then I would hash `C1 + C2`, literately concat the string values then run it through some hash function (md5, sha1, crc), and do the same for `C3 + C4`, `C5 + C6`, and `C7 + C8` 12 | 13 | At the end of the first pass I'm left with the following: 14 | 15 | ``` 16 | H12 H34 H56 H78 17 | C1 C2 C3 C4 C5 C6 C7 C8 18 | ``` 19 | 20 | Now I repeat the process with H12, H34, H56, and H78. So now I'll concat `H12 + H34` and do the same for `H56 + H78` and I'll have this: 21 | 22 | ``` 23 | H1234 H5678 24 | H12 H34 H56 H78 25 | C1 C2 C3 C4 C5 C6 C7 C8 26 | ``` 27 | 28 | And one more pass 29 | 30 | 31 | ``` 32 | H12345678 33 | H1234 H5678 34 | H12 H34 H56 H78 35 | C1 C2 C3 C4 C5 C6 C7 C8 36 | ``` 37 | 38 | Now I've reduced the list of values to a single value. 39 | 40 | **If you start with an odd number of elements, you need to first duplicate the last element to the end of the list to get an even number before starting the process** 41 | 42 | ## How is this useful? 43 | 44 | Assume I wanted to know if a number is in the contacts list, but all I knew was the root number `H12345678`, I can ask someone who knows all the contact to send me some specific hashes and verify them myself. For example if I asked someone for `C6` they would send my `C5`, `H78`, and `H1234` and I would hash `C5 + C6` to get `H56` then do `H5678 = hash(H56 + H78)` and then do `root = hash(H1234 + H5678)` and check that the root I derive is the same as the root that I knew beforehand. If it matches then the data must have been in the set the entire time. 45 | 46 | Since this is [Divide and conquer algorithm](https://en.wikipedia.org/wiki/Divide_and_conquer_algorithms), for a list of 1 quadrillion contacts (1,000,000,000,000,000) we only need 50 hashes. 47 | 48 | For this exercise you need to return a function that takes a list of values and a hashing function an returns an object that contains a root property and a getVerification method as follows: 49 | 50 | 51 | ```js 52 | var merkle = require('./') // <- this is the file you make; 53 | 54 | // helper code from http://stackoverflow.com/a/7616484 55 | var hasher = function(str) { 56 | var hash = 0, i, chr, len; 57 | if (str.length == 0) return hash; 58 | for (i = 0, len = str.length; i < len; i++) { 59 | chr = str.charCodeAt(i); 60 | hash = ((hash << 5) - hash) + chr; 61 | hash |= 0; // Convert to 32bit integer 62 | } 63 | return hash.toString(); 64 | }; 65 | 66 | 67 | var myMerkle = merkle(['This', 'is', 'a', 'test'], hasher); 68 | console.log(myMerkle.root); // -1427219841 69 | myMerkle.getVerification('not in tree'); // false 70 | 71 | // this is an object that the server would return 72 | var verificationObject = myMerkle.getVerification('test'); // { index: 3, breadcrumbs: [ 'a', '-1790830488' ] } 73 | 74 | // this is how the client would verify that the contact exists (the client somehow already knows the root) 75 | var root = myMerkle.root; 76 | merkle.verify('test', root, obj, simpleHasher); // true 77 | merkle.verify('testing', root, obj, simpleHasher); // false 78 | ``` 79 | 80 | More info: https://en.wikipedia.org/wiki/Merkle_tree 81 | -------------------------------------------------------------------------------- /merkle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /merkle/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var merkle = require('./'); 3 | 4 | var crypto = require('crypto'); 5 | 6 | describe('merkle', function() { 7 | 8 | describe('has a root property correctly set when', function() { 9 | 10 | it('has a power of two elements', function() { 11 | var myMerkle = merkle(['This', 'is', 'a', 'test'], simpleHasher); 12 | var verificationObject = myMerkle.getVerification('test') 13 | assert.equal(myMerkle.root, -1427219841, 'hashes match') 14 | }); 15 | 16 | it('has a power of two elements with dupes', function() { 17 | var myMerkle = merkle(['This', 'is', 'cool', 'cool'], simpleHasher); 18 | assert.equal(myMerkle.root, 678075951, 'hashes match') 19 | }); 20 | 21 | it('has a an odd number of elements', function() { 22 | var myMerkle = merkle(['This', 'is', 'cool'], simpleHasher); 23 | assert.equal(myMerkle.root, 678075951, 'hashes match') 24 | }); 25 | 26 | it('has a large number of elements', function() { 27 | var arr = 'here is a test to see if we can find all the cool words in this list'.split(' '); 28 | var myMerkle = merkle(arr, simpleHasher); 29 | assert.equal(myMerkle.root, -721821363) 30 | }); 31 | 32 | }); 33 | 34 | describe('can verify an element is in a tree when', function() { 35 | 36 | it('has a power of two elements', function() { 37 | var myMerkle = merkle(['This', 'is', 'a', 'test'], simpleHasher); 38 | var obj = myMerkle.getVerification('is') 39 | assert(merkle.verify('is', myMerkle.root, obj, simpleHasher)) 40 | }); 41 | 42 | it('has a power of two elements with dupes', function() { 43 | var myMerkle = merkle(['This', 'is', 'cool', 'cool'], simpleHasher); 44 | var obj = myMerkle.getVerification('cool') 45 | assert(merkle.verify('cool', myMerkle.root, obj, simpleHasher)) 46 | }); 47 | 48 | it('has a an odd number of elements', function() { 49 | var myMerkle = merkle(['This', 'is', 'cool'], simpleHasher); 50 | var obj = myMerkle.getVerification('cool') 51 | assert(merkle.verify('cool', myMerkle.root, obj, simpleHasher)) 52 | }); 53 | 54 | it('has a large number of elements', function() { 55 | var arr = 'here is a test to see if we can find all the cool words in this list'.split(' '); 56 | var myMerkle = merkle(arr, simpleHasher); 57 | var obj = myMerkle.getVerification('cool') 58 | assert(merkle.verify('cool', myMerkle.root, obj, simpleHasher)) 59 | }); 60 | 61 | }); 62 | 63 | 64 | it('works just like bitcoin!', function() { 65 | var seeder = doublesha256('some seed'); 66 | var values = []; 67 | for (var i = 0; i < 10000; i++) { 68 | values.push('value' + i + ' ' + doublesha256(seeder + i)); 69 | } 70 | var myMerkle = merkle(values, doublesha256); 71 | var lookFor = values[4567]; 72 | var obj = myMerkle.getVerification(lookFor); 73 | assert.equal(myMerkle.root, 'e213bb72c8975346d44abc5bfc917ef2e28ef8277007e9c3346f238c6a0d68d1') 74 | assert(merkle.verify(lookFor, myMerkle.root, obj, doublesha256)) 75 | }); 76 | 77 | }); 78 | 79 | 80 | 81 | 82 | 83 | 84 | // HELPERS 85 | 86 | // from http://stackoverflow.com/a/7616484 87 | function simpleHasher(str) { 88 | var hash = 0, i, chr, len; 89 | if (str.length == 0) return hash; 90 | for (i = 0, len = str.length; i < len; i++) { 91 | chr = str.charCodeAt(i); 92 | hash = ((hash << 5) - hash) + chr; 93 | hash |= 0; // Convert to 32bit integer 94 | } 95 | return hash.toString(); 96 | }; 97 | 98 | function doublesha256(str) { 99 | return sha256(sha256(str)); 100 | } 101 | 102 | function sha256(str) { 103 | return crypto.createHash('sha256').update(str).digest('hex'); 104 | } 105 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | Middleware is the programming pattern of providing hooks with a resume callback. 2 | Here's the basic usage of the file that you'll be creating: 3 | 4 | ```js 5 | var Middleware = require('./'); // <- this is the file you make; 6 | 7 | var middleware = new Middleware(); 8 | 9 | middleware.use(function(next) { 10 | var self = this; 11 | setTimeout(function() { 12 | self.hook1 = true; 13 | next(); 14 | }, 10); 15 | }); 16 | 17 | middleware.use(function(next) { 18 | var self = this; 19 | setTimeout(function() { 20 | self.hook2 = true; 21 | next(); 22 | }, 10); 23 | }); 24 | 25 | var start = new Date(); 26 | middleware.go(function() { 27 | console.log(this.hook1); // true 28 | console.log(this.hook2); // true 29 | console.log(new Date() - start); // around 20 30 | }); 31 | ``` 32 | 33 | More info: http://expressjs.com/guide/using-middleware.html 34 | -------------------------------------------------------------------------------- /middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /middleware/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var Middleware = require('./'); 3 | 4 | describe('middleware', function() { 5 | it('works with a single instance', function(done) { 6 | 7 | var middleware = new Middleware(); 8 | 9 | middleware.use(function(next) { 10 | var ctx = this; 11 | setTimeout(function() { 12 | ctx.first = true; 13 | next(); 14 | }, 10); 15 | }); 16 | 17 | middleware.use(function(next) { 18 | var ctx = this; 19 | setTimeout(function() { 20 | ctx.second = true; 21 | next(); 22 | }, 10); 23 | }); 24 | 25 | middleware.go(function() { 26 | assert.equal(this.first, true); 27 | assert.equal(this.second, true); 28 | done(); 29 | }); 30 | }); 31 | 32 | 33 | 34 | 35 | 36 | it('works with multiple instances', function(done) { 37 | 38 | var middleware1 = new Middleware(); 39 | var middleware2 = new Middleware(); 40 | 41 | middleware1.use(function(next) { 42 | var ctx = this; 43 | setTimeout(function() { 44 | ctx.first = true; 45 | next(); 46 | }, 10); 47 | }); 48 | 49 | middleware2.use(function(next) { 50 | var ctx = this; 51 | setTimeout(function() { 52 | ctx.second = true; 53 | next(); 54 | }, 10); 55 | }); 56 | 57 | middleware1.go(function() { 58 | assert.equal(this.first, true); 59 | middleware2.go(function() { 60 | assert.equal(this.second, true); 61 | done(); 62 | }) 63 | }); 64 | 65 | }); 66 | 67 | 68 | }); 69 | -------------------------------------------------------------------------------- /morse-code/README.md: -------------------------------------------------------------------------------- 1 | Let's create a morse code transmitter 2 | 3 | Create a function that gets injected: a lightbulb toggler function, a timeout mechanism function, a string message, and a map of chars to dot-dashes and make magic happen, additionally it should run a callback when done 4 | 5 | The timeout mechanism is like setTimeout, the difference is that it takes a measurement of dots instead of ms so instead of `setTimeout(toggle, 150)` for dot you would do `options.timeouter(toggle, 1)` and for a dash you would do `options.timeouter(toggle, 3)` 6 | 7 | A couple of things to know: 8 | 9 | 1. dot duration is the baseline timing measurement 10 | 2. dashes are timed as 3 dots 11 | 3. time between each dot or dash in the same letter is 1 dot 12 | 3. time between letters are 3 dots 13 | 4. time between words are 7 dots 14 | 15 | Here's a pretty timeline graph of the string "so so": 16 | 17 | ``` 18 | Char: 's' 'o' 's' 'o' 19 | Padding: vvvvv vvvvvvvvvvv vvvvv vvvvvvvvvvv 20 | Lightbulb state: 101010001110111011100000001010100011101110111 21 | ``` 22 | 23 | Here's the the basic idea of how the file you make will get used 24 | 25 | ```js 26 | var transmitter = require('./') // <- this is the file you make; 27 | 28 | var codes = {s: '...', o: '---'}; 29 | var message = 'sos'; 30 | var timeouter = function(fn, ms) { setTimeout(fn, ms * 50); }; 31 | var toggle = function() { 32 | lightbulb.toggle(); 33 | } 34 | 35 | transmitter({ 36 | codes: codes, 37 | message: message, 38 | timeouter: timeouter, 39 | toggle: toggle 40 | }, function(err) { 41 | console.log('message sent!'); 42 | }); 43 | 44 | 45 | ``` 46 | 47 | More info: https://en.wikipedia.org/wiki/Morse_code 48 | -------------------------------------------------------------------------------- /morse-code/codes.json: -------------------------------------------------------------------------------- 1 | { 2 | "0": "-----", 3 | "1": ".----", 4 | "2": "..---", 5 | "3": "...--", 6 | "4": "....-", 7 | "5": ".....", 8 | "6": "-....", 9 | "7": "--...", 10 | "8": "---..", 11 | "9": "----.", 12 | "a": ".-", 13 | "b": "-...", 14 | "c": "-.-.", 15 | "d": "-..", 16 | "e": ".", 17 | "f": "..-.", 18 | "g": "--.", 19 | "h": "....", 20 | "i": "..", 21 | "j": ".---", 22 | "k": "-.-", 23 | "l": ".-..", 24 | "m": "--", 25 | "n": "-.", 26 | "o": "---", 27 | "p": ".--.", 28 | "q": "--.-", 29 | "r": ".-.", 30 | "s": "...", 31 | "t": "-", 32 | "u": "..-", 33 | "v": "...-", 34 | "w": ".--", 35 | "x": "-..-", 36 | "y": "-.--", 37 | "z": "--..", 38 | ".": ".-.-.-", 39 | ",": "--..--", 40 | "?": "..--..", 41 | "!": "-.-.--", 42 | "-": "-....-", 43 | "/": "-..-.", 44 | "@": ".--.-.", 45 | "(": "-.--.", 46 | ")": "-.--.-" 47 | } 48 | -------------------------------------------------------------------------------- /morse-code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /morse-code/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var sinon = require('sinon'); 3 | var transmitter = require('./'); 4 | var codes = require('./codes'); 5 | 6 | describe('transmitter', function() { 7 | 8 | var clock; 9 | 10 | before(function () { clock = sinon.useFakeTimers(); }); 11 | after(function () { clock.restore(); }); 12 | 13 | var toggledHistory; 14 | var toggle; 15 | var startTime; 16 | var timeouter; 17 | 18 | beforeEach(function() { 19 | toggledHistory = []; 20 | startTime = new Date(); 21 | toggle = function() { 22 | toggledHistory.push(new Date() - startTime); 23 | }; 24 | timeouter = function(fn, ms) { 25 | setTimeout(function() { 26 | fn(); 27 | }, ms * 50); 28 | }; 29 | }); 30 | 31 | it('transmits a letter in morse code', function(done) { 32 | var options = { 33 | codes: codes, 34 | message: 's', 35 | toggle: toggle, 36 | timeouter: timeouter 37 | } 38 | transmitter(options, function() { 39 | assert.deepEqual(toggledHistory, [ 40 | 0, 50, 100, 150, 200, 250 41 | ]); 42 | done(); 43 | }); 44 | clock.tick(300); 45 | }); 46 | 47 | 48 | it('transmits a word in morse code', function(done) { 49 | var options = { 50 | codes: codes, 51 | message: 'sos', 52 | toggle: toggle, 53 | timeouter: timeouter 54 | } 55 | transmitter(options, function() { 56 | assert.deepEqual(toggledHistory, [ 57 | 0, 50, 100, 150, 200, 250, 400, 550, 600, 750, 800, 58 | 950, 1100, 1150, 1200, 1250, 1300, 1350 59 | ]); 60 | done(); 61 | }); 62 | clock.tick(1400); 63 | }); 64 | 65 | it('transmits a message in morse code', function(done) { 66 | var options = { 67 | codes: codes, 68 | message: 'this is a message', 69 | toggle: toggle, 70 | timeouter: timeouter 71 | } 72 | transmitter(options, function() { 73 | assert.deepEqual(toggledHistory, [ 74 | 0, 150, 300, 350, 400, 450, 500, 550, 600, 650, 75 | 800, 850, 900, 950, 1100, 1150, 1200, 1250, 1300, 76 | 1350, 1700, 1750, 1800, 1850, 2000, 2050, 2100, 77 | 2150, 2200, 2250, 2600, 2650, 2700, 2850, 3200, 78 | 3350, 3400, 3550, 3700, 3750, 3900, 3950, 4000, 79 | 4050, 4100, 4150, 4300, 4350, 4400, 4450, 4500, 80 | 4550, 4700, 4750, 4800, 4950, 5100, 5250, 5300, 81 | 5450, 5500, 5550, 5700, 5750 82 | ]); 83 | done(); 84 | }); 85 | clock.tick(5800); 86 | }); 87 | 88 | 89 | }); 90 | -------------------------------------------------------------------------------- /once/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var once = require('./') // <- this is the file you make; 5 | 6 | function bootstrapApp() { 7 | console.log('this should be shown once'); 8 | return 22; 9 | } 10 | 11 | var initialize = once(bootstrapApp); 12 | console.log(initialize()); 13 | console.log(initialize()); 14 | 15 | // the console should show: 16 | // this should be shown once 17 | // 22 18 | // 22 19 | 20 | ``` 21 | 22 | More info: http://underscorejs.org/#once 23 | -------------------------------------------------------------------------------- /once/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /once/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var once = require('./'); 3 | 4 | describe('once', function() { 5 | it("won't execute more than once", function() { 6 | var called = 0; 7 | var init = once(function() { 8 | return ++called; 9 | }); 10 | init(); 11 | init(); 12 | init(); 13 | assert.equal(called, 1); 14 | }); 15 | 16 | it('will return the value from the original call for later calls', function() { 17 | var t = 10; 18 | var init = once(function() { 19 | return ++t; 20 | }); 21 | var ret = init(); 22 | assert.deepEqual(ret, 11); 23 | assert.deepEqual(init(), ret); 24 | assert.deepEqual(init(), ret); 25 | }); 26 | 27 | it('gets called with context', function() { 28 | var ctx; 29 | var init = once(function() { 30 | ctx = this; 31 | }); 32 | init.call(11); 33 | init.call(22); 34 | assert.equal(ctx, 11); 35 | }); 36 | 37 | it('gets called with arguments', function() { 38 | var args; 39 | var init = once(function() { 40 | args = [].slice.call(arguments); 41 | }); 42 | init(11, 22, 33); 43 | init(22, 33, 44); 44 | assert.deepEqual(args, [11, 22, 33]); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "exercises", 3 | "private": true, 4 | "description": "some javascript exercises", 5 | "scripts": { 6 | "test": "echo \"Error: run npm test from inside the challenge directories or do 'npm run test-all'\" && exit 1", 7 | "test-all": "mocha '**/test.js'" 8 | }, 9 | "author": "Moshe Kolodny", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "mocha": "^2.2.4", 13 | "sinon": "^1.15.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sort/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var sort = require('./') // <- this is the file you make; 5 | 6 | var arr = [5, 1, 4, 2, 3]; 7 | 8 | var sorted = sort(arr); 9 | console.log(sorted); // [1, 2, 3, 4, 5] 10 | ``` 11 | 12 | Obviously using the native sort function is cheating, so don't use it 13 | 14 | More info: http://en.wikipedia.org/wiki/Sorting_algorithm 15 | -------------------------------------------------------------------------------- /sort/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /sort/test.js: -------------------------------------------------------------------------------- 1 | var nativeSortCalled; 2 | var oldSort = Array.prototype.sort; 3 | Array.prototype.sort = function() { 4 | nativeSortCalled = true; 5 | return oldSort.apply(this, arguments); 6 | }; 7 | 8 | var assert = require('assert'); 9 | var sort = require('./'); 10 | 11 | describe('sort', function() { 12 | 13 | it('will sort an array', function() { 14 | var arr = [5, 1, 2, 4, 3]; 15 | nativeSortCalled = false; 16 | var sorted = sort(arr); 17 | assert(!nativeSortCalled); 18 | assert.deepEqual(sorted, [1, 2, 3, 4, 5]); 19 | }); 20 | 21 | it('sorts better than n^2', function() { 22 | var arr = []; 23 | for (var i = 0; i < 10000; i++) { 24 | arr.push(Math.random()); 25 | } 26 | nativeSortCalled = false; 27 | sorted = sort(arr); 28 | var lastNumber = sorted[0]; 29 | for (i = 1; i < 10000; i++) { 30 | if (lastNumber > sorted[i]) { 31 | throw new Error("array isn't sorted"); 32 | } 33 | lastNumber = sorted[i]; 34 | } 35 | assert(!nativeSortCalled); 36 | }); 37 | 38 | }); 39 | -------------------------------------------------------------------------------- /throttle-promises/README.md: -------------------------------------------------------------------------------- 1 | Assume we have an array of functions that start an operation that does something async: 2 | 3 | ```js 4 | var nextValue = 0; 5 | var asyncFactory = function() { 6 | var resolveWith = nextValue++; 7 | return new Promise(function(resolve) { 8 | setTimeout(function() { 9 | resolve(resolveWith + '!'); 10 | }, Math.random() * 100); 11 | }); 12 | }; 13 | ``` 14 | 15 | Here's how we would use this without throttling: 16 | 17 | ```js 18 | Promise.all([ asyncFactory(), asyncFactory(), asyncFactory(), asyncFactory() ]).then(function(results) { 19 | console.log(results); // ['0!', '1!', '2!', '3!']; 20 | }); 21 | ``` 22 | 23 | Let's say we need to throttle this to only have a certain amount of promises running at a time 24 | 25 | 26 | Here's the basic usage of the file that you'll be creating: 27 | 28 | ```js 29 | var throttlePromises = require('./') // <- this is the file you make; 30 | 31 | var nextValue = 0; 32 | var asyncFactory = function() { 33 | var resolveWith = nextValue++; 34 | return new Promise(function(resolve) { 35 | setTimeout(function() { 36 | resolve(resolveWith + '!'); 37 | }, Math.random() * 100); 38 | }); 39 | }; 40 | 41 | var arr = []; 42 | for (var i = 0; i < 100; i++) { 43 | arr.push(asyncFactory); // push the factory but don't instantiated since that would start it now 44 | } 45 | 46 | // this is the solution function you'll write 47 | throttlePromises(5, arr).then(function(results) { 48 | console.log('only 5 promises were ever executing in parallel'); 49 | }); 50 | -------------------------------------------------------------------------------- /throttle-promises/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /throttle-promises/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var throttlePromises = require('./'); 3 | 4 | describe('throttle-promises', function() { 5 | it("doesn't run more than `limit` promises in parallel", function(done) { 6 | var passed = true; 7 | var limit = 5; 8 | var currentlyExecuting = 0; 9 | var currentlyExecutingHistory = [currentlyExecuting]; 10 | var nextValue = 0; 11 | var asyncFactory = function() { 12 | return new Promise(function(resolve) { 13 | var resolveWith = nextValue++; 14 | currentlyExecuting++; 15 | currentlyExecutingHistory.push(currentlyExecuting); 16 | if (currentlyExecuting > limit) { 17 | passed = false; 18 | } 19 | setTimeout(function() { 20 | resolve(resolveWith + '!'); 21 | currentlyExecuting--; 22 | currentlyExecutingHistory.push(currentlyExecuting); 23 | }, Math.random() * 100); 24 | }); 25 | }; 26 | 27 | var arr = []; 28 | for (var i = 0; i < 100; i++) { 29 | arr.push(asyncFactory); 30 | } 31 | 32 | throttlePromises(5, arr).then(function(results) { 33 | var expectedResults = Array(101).join('.').split('').map(function(dot, index) { 34 | return index + '!'; 35 | }); 36 | var expectedHistory = Array(202).join('.').split('').map(function(dot, index) { 37 | return index < 5 ? index : index >= 200 - 5 ? 200 - index : index % 2 ? 5 : 4; 38 | }); 39 | 40 | assert(passed, 'more than ' + limit + ' promises ran in parallel'); 41 | assert.deepEqual(results, expectedResults); 42 | assert.deepEqual(currentlyExecutingHistory, expectedHistory); 43 | done(); 44 | }); 45 | 46 | 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /throttle/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var throttle = require('./') // <- this is the file you make; 5 | 6 | var sayHi = function() { 7 | console.log('hi'); 8 | }; 9 | 10 | var throttled = throttle(sayHi, 100); 11 | 12 | throttled(); 13 | throttled(); 14 | throttled(); 15 | throttled(); 16 | 17 | // there should only be two 'hi' messages on the console 18 | ``` 19 | 20 | More info: http://underscorejs.org/#throttle 21 | 22 | 23 | ### [Difference between debounce and throttle](https://github.com/kolodny/exercises/tree/master/debounce#difference-between-debounce-and-throttle) 24 | -------------------------------------------------------------------------------- /throttle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /throttle/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var sinon = require('sinon'); 3 | var throttle = require('./'); 4 | 5 | describe('throttle', function() { 6 | var clock; 7 | 8 | beforeEach(function () { clock = sinon.useFakeTimers(); }); 9 | afterEach(function () { clock.restore(); }); 10 | 11 | it('executes right away', function() { 12 | var passed = false; 13 | var throttled = throttle(function() { 14 | passed = true; 15 | }, 10); 16 | throttled(); 17 | assert(passed); 18 | clock.tick(100); 19 | }); 20 | 21 | it("won't execute more than once within the threshold", function(done) { 22 | var called = 0; 23 | var throttled = throttle(function() { 24 | called++; 25 | }, 10); 26 | throttled(); 27 | throttled(); 28 | throttled(); 29 | setTimeout(function() { 30 | assert.equal(called, 1); 31 | done(); 32 | }, 5); 33 | clock.tick(100); 34 | }); 35 | 36 | it("will execute at least once more to make up for swallowed calls", function(done) { 37 | var called = 0; 38 | var throttled = throttle(function() { 39 | called++; 40 | }, 10); 41 | throttled(); 42 | throttled(); 43 | setTimeout(function() { 44 | assert.equal(called, 2); 45 | done(); 46 | }, 15); 47 | clock.tick(100); 48 | }); 49 | 50 | it('will execute every threshold ms', function(done) { 51 | var startTime = new Date(); 52 | var calledTimes = []; 53 | var throttled = throttle(function() { 54 | calledTimes.push(new Date() - startTime); 55 | }, 10); 56 | throttled(); //start now 57 | var interval = setInterval(throttled, 1); 58 | setTimeout(function() { 59 | clearInterval(interval); 60 | 61 | assert.deepEqual(calledTimes, [0, 11, 22, 33, 44, 55]); 62 | 63 | done(); 64 | }, 59); 65 | clock.tick(100); 66 | 67 | }); 68 | 69 | it('gets called with context', function() { 70 | var ctx; 71 | var throttled = throttle(function() { 72 | ctx = this; 73 | }, 10); 74 | throttled.call(22); 75 | assert.equal(ctx, 22); 76 | }); 77 | 78 | it('gets called with arguments', function() { 79 | var args; 80 | var throttled = throttle(function() { 81 | args = [].slice.call(arguments); 82 | }, 10); 83 | throttled(22, 33, 44); 84 | assert.deepEqual(args, [22, 33, 44]); 85 | }); 86 | 87 | it('gets called with the later arguments', function(done) { 88 | var args; 89 | var throttled = throttle(function() { 90 | args = [].slice.call(arguments); 91 | }, 10); 92 | throttled(11, 22, 33); 93 | throttled(22, 33, 44); 94 | throttled(33, 44, 55); 95 | setTimeout(function() { 96 | assert.deepEqual(args, [33, 44, 55]); 97 | done(); 98 | }, 15) 99 | clock.tick(100); 100 | }); 101 | 102 | 103 | }); 104 | -------------------------------------------------------------------------------- /update/README.md: -------------------------------------------------------------------------------- 1 | Immutability is all the rage nowadays. Let's explain what it is and then implement out version of Facebook's [Immutability Helpers](https://facebook.github.io/react/docs/update.html) 2 | 3 | The basic idea is to have all object immutable, which means that once an object is created it can never be change. At this point you're wondering how is it possible to build an anything with this restriction. Well instead of mutating an object we switch bindings to a very similar but slightly different object. Switching bindings is just a fancy way of saying making a variable refer to a different object 4 | 5 | Let's say we have an App that contains something like this: 6 | 7 | ```js 8 | var state = { 9 | myName: 'Alice', 10 | todos: [ 'shopping', 'cleaning' ], 11 | }; 12 | ``` 13 | 14 | Now if we wanted to change the name instead of just changing `state.myName` we would return a new object with just the `myName` property changed: 15 | 16 | ```js 17 | var newState = { 18 | myName: 'Bob', 19 | todos: state.todos, 20 | }; 21 | ``` 22 | 23 | The problem is, this can start to get annoying, let's instead follow Facebook's method and create an update function that takes [certain commands](https://facebook.github.io/react/docs/update.html#available-commands) 24 | 25 | Here's the basic usage of the file that you'll be creating: 26 | 27 | ```js 28 | var update = require('./') // <- this is the file you make; 29 | 30 | var state = { name: 'Alice', todos: [] }; 31 | var nextState = update(state, { 32 | name: {$set: 'Bob'} 33 | }); 34 | console.log(state.todos === nextState.todos); // true 35 | ``` 36 | 37 | More info: https://facebook.github.io/react/docs/update.html#available-commands 38 | -------------------------------------------------------------------------------- /update/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /update/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var update = require('./'); 3 | 4 | describe('update', function() { 5 | 6 | describe('has a #$set method that', function() { 7 | var state; 8 | var commands; 9 | var nextState; 10 | beforeEach(function() { 11 | state = { 12 | a: { 13 | b: 22, 14 | c: 33 15 | }, 16 | unChanged: {}, 17 | }; 18 | commands = { a: { c: {$set: 44} } }; 19 | nextState = update(state, commands); 20 | }); 21 | 22 | it('changes the tree on the directive', function() { 23 | assert(state.a.c !== nextState.a.c); 24 | }) 25 | 26 | it('reuses state on different branches', function() { 27 | assert(state.unChanged === nextState.unChanged); 28 | }); 29 | 30 | it('reuses state on same level', function() { 31 | assert(state.a.b === state.a.b); 32 | }); 33 | 34 | 35 | }); 36 | 37 | 38 | 39 | describe("can pass react's test suite", function() { 40 | 41 | 42 | it('should support push', function() { 43 | assert.deepEqual(update([1], {$push: [7]}), [1, 7]); 44 | }); 45 | 46 | it('should support unshift', function() { 47 | assert.deepEqual(update([1], {$unshift: [7]}), [7, 1]); 48 | }); 49 | 50 | it('should support splice', function() { 51 | assert.deepEqual(update([1, 4, 3], {$splice: [[1, 1, 2]]}), [1, 2, 3]); 52 | }); 53 | 54 | it('should support merge', function() { 55 | assert.deepEqual(update({a: 'b'}, {$merge: {c: 'd'}}), {a: 'b', c: 'd'}); 56 | }); 57 | 58 | it('should support set', function() { 59 | assert.deepEqual(update({a: 'b'}, {$set: {c: 'd'}}), {c: 'd'}); 60 | }); 61 | 62 | it('should support apply', function() { 63 | assert.equal(update(2, {$apply: function(x) { return x * 2; }}), 4); 64 | }); 65 | 66 | it('should support deep updates', function() { 67 | assert.deepEqual(update({a: 'b', c: {d: 'e'}}, {c: {d: {$set: 'f'}}}), { 68 | a: 'b', 69 | c: {d: 'f'}, 70 | }); 71 | }); 72 | 73 | it('should perform safe hasOwnProperty check', function() { 74 | assert.deepEqual(update({}, {'hasOwnProperty': {$set: 'a'}}), { 75 | 'hasOwnProperty': 'a', 76 | }); 77 | }); 78 | }); 79 | 80 | 81 | }); 82 | -------------------------------------------------------------------------------- /value/README.md: -------------------------------------------------------------------------------- 1 | Here's the basic usage of the file that you'll be creating: 2 | 3 | ```js 4 | var value = require('./') // <- this is the file you make; 5 | 6 | var scalar = 'foo'; 7 | var fn = function() { return 'bar'; }; 8 | var fnTwice = function() { 9 | return fn; 10 | }; 11 | var fnThrice = function() { 12 | return fnTwice; 13 | }; 14 | 15 | var whoa = function() { 16 | return function() { 17 | return function() { 18 | return function() { 19 | return function() { 20 | return function() { 21 | return function() { 22 | return function() { 23 | return function() { 24 | return 'hi'; 25 | }; 26 | }; 27 | }; 28 | }; 29 | }; 30 | }; 31 | }; 32 | }; 33 | }; 34 | 35 | value(scalar); // should be 'foo' 36 | value(fn); // should be 'bar' 37 | value(fnTwice); // should also be 'bar' 38 | value(fnThrice); // should also be 'bar' 39 | 40 | value(whoa); // should be 'hi' 41 | 42 | ``` 43 | -------------------------------------------------------------------------------- /value/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "test": "node ../node_modules/mocha/bin/mocha" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /value/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var value = require('./'); 3 | 4 | describe('value', function() { 5 | 6 | it('works on simple values', function() { 7 | assert.equal(value(2), 2); 8 | }); 9 | 10 | it('works on functions', function() { 11 | var fn = function() { return 3; }; 12 | assert.equal(value(fn), 3); 13 | }); 14 | 15 | it('works on nested functions', function() { 16 | var fn = function() { return function() { return 4; }; }; 17 | assert.equal(value(fn), 4); 18 | }); 19 | 20 | it('works on nested functions WOW!', function() { 21 | var fn = function() { return function() { return function() { return 5; }; }; }; 22 | assert.equal(value(fn), 5); 23 | }); 24 | 25 | }); 26 | --------------------------------------------------------------------------------