├── hello-world ├── hello-world.js └── hello-world.spec.js ├── two-fer ├── two-fer.js ├── two-fer.spec.js └── README.md ├── reverse-string ├── reverse-string.js ├── reverse-string.spec.js └── README.md ├── isogram ├── isogram.js └── README.md ├── leap ├── leap.js ├── README.md └── leap.spec.js ├── accumulate ├── accumulate.js ├── README.md └── accumulate.spec.js ├── acronym ├── package.json ├── acronym.js ├── README.md └── acronym.spec.js ├── armstrong-numbers ├── armstrong-numbers.js ├── armstrong-numbers.spec.js └── README.md ├── flatten-array ├── flatten-array.js ├── flatten-array.spec.js └── README.md ├── pangram ├── pangram.js ├── README.md └── pangram.spec.js ├── hamming ├── hamming.js └── hamming.spec.js ├── gigasecond ├── gigasecond.js ├── README.md └── gigasecond.spec.js ├── grains ├── grains.js └── README.md ├── run-length-encoding ├── run-length-encoding.js └── run-length-encoding.spec.js ├── collatz-conjecture ├── collatz-conjecture.js └── collatz-conjecture.spec.js ├── matrix ├── matrix.js ├── matrix.spec.js └── README.md ├── difference-of-squares ├── difference-of-squares.js ├── README.md └── difference-of-squares.spec.js ├── octal ├── octal.js └── octal.spec.js ├── pig-latin ├── pig-latin.js ├── README.md └── pig-latin.spec.js ├── sieve ├── sieve.js ├── sieve.spec.js └── README.md ├── sum-of-multiples ├── sum-of-multiples.js ├── README.md └── sum-of-multiples.spec.js ├── binary ├── binary.js ├── binary.spec.js └── README.md ├── etl ├── etl.js └── etl.spec.js ├── trinary ├── trinary.js ├── trinary.spec.js └── README.md ├── strain ├── strain.js └── README.md ├── word-count ├── word-count.js └── README.md ├── bob ├── bob.js └── README.md ├── isbn-verifier └── isbn-verifier.js ├── nth-prime ├── nth-prime.js ├── nth-prime.spec.js └── README.md ├── rna-transcription ├── rna-transcription.js ├── rna-transcription.spec.js └── README.md ├── atbash-cipher ├── atbash-cipher.js ├── atbash-cipher.spec.js └── README.md ├── prime-factors ├── prime-factors.js ├── prime-factors.spec.js └── README.md ├── raindrops ├── raindrops.js └── README.md ├── transpose └── transpose.js ├── bracket-push ├── bracket-push.js ├── README.md └── bracket-push.spec.js ├── series ├── series.js └── README.md ├── pascals-triangle ├── pascals-triangle.js ├── pascals-triangle.spec.js └── README.md ├── grade-school └── grade-school.js ├── binary-search-tree └── binary-search-tree.js ├── diamond ├── diamond.js └── diamond.spec.js ├── scrabble-score ├── scrabble-score.js ├── scrabble-score.spec.js └── README.md ├── rotational-cipher └── rotational-cipher.js ├── triangle ├── triangle.js └── README.md ├── hexadecimal ├── hexadecimal.js ├── README.md └── hexadecimal.spec.js ├── proverb └── proverb.js ├── anagram ├── anagram.js └── README.md ├── custom-set └── README.md ├── allergies ├── allergies.js └── README.md ├── palindrome-products ├── README.md └── palindrome-products.js ├── roman-numerals └── roman-numerals.js ├── clock ├── README.md └── clock.js ├── secret-handshake ├── secret-handshake.js ├── README.md └── secret-handshake.spec.js ├── binary-search ├── binary-search.js └── binary-search.spec.js ├── space-age ├── space-age.js ├── README.md └── space-age.spec.js ├── perfect-numbers └── perfect-numbers.js ├── pythagorean-triplet ├── README.md ├── pythagorean-triplet.js └── pythagorean-triplet.spec.js ├── nucleotide-count ├── nucleotide-count.js ├── nucleotide-count.spec.js └── README.md ├── diffie-hellman └── diffie-hellman.js ├── largest-series-product ├── largest-series-product.js └── README.md ├── protein-translation └── protein-translation.js ├── phone-number ├── phone-number.js ├── phone-number.spec.js └── README.md ├── variable-length-quantity └── variable-length-quantity.js ├── robot-name ├── robot-name.js └── README.md ├── queen-attack ├── queen-attack.js └── README.md ├── kindergarten-garden └── kindergarten-garden.js ├── all-your-base └── all-your-base.js ├── saddle-points ├── saddle-points.js ├── README.md └── saddle-points.spec.js ├── list-ops ├── README.md └── list-ops.js ├── simple-cipher └── simple-cipher.js ├── luhn ├── luhn.js └── luhn.spec.js ├── minesweeper ├── minesweeper.js └── README.md ├── meetup ├── README.md └── meetup.js ├── spiral-matrix ├── spiral-matrix.js ├── README.md └── spiral-matrix.spec.js ├── wordy └── wordy.js ├── sublist ├── sublist.js └── README.md ├── word-search ├── README.md └── word-search.js ├── beer-song └── beer-song.js ├── two-bucket └── two-bucket.js ├── crypto-square └── crypto-square.js ├── house ├── house.js └── package.json ├── complex-numbers └── complex-numbers.js ├── connect └── connect.js ├── twelve-days └── twelve-days.js ├── change └── change.js ├── circular-buffer └── circular-buffer.js ├── react ├── README.md └── react.js ├── forth ├── README.md ├── forth.js └── package.json ├── rectangles ├── README.md └── rectangles.js ├── say └── say.js ├── rational-numbers └── rational-numbers.js ├── alphametics └── README.md ├── food-chain └── food-chain.js ├── ocr-numbers └── ocr-numbers.js └── zipper └── zipper.js /hello-world/hello-world.js: -------------------------------------------------------------------------------- 1 | var HelloWorld = function () {}; 2 | 3 | HelloWorld.prototype.hello = function () { 4 | return "Hello, World!"; 5 | }; 6 | 7 | module.exports = HelloWorld; 8 | -------------------------------------------------------------------------------- /two-fer/two-fer.js: -------------------------------------------------------------------------------- 1 | var TwoFer = function () {}; 2 | 3 | TwoFer.prototype.twoFer = function (who="you") { 4 | return "One for " + who.toString() + ", one for me."; 5 | }; 6 | 7 | module.exports = TwoFer; 8 | -------------------------------------------------------------------------------- /reverse-string/reverse-string.js: -------------------------------------------------------------------------------- 1 | module.exports = function(chars) { 2 | var srahc = ''; 3 | for(var i = chars.length-1; 0 <= i; i--) { 4 | srahc += chars[i]; 5 | } 6 | return srahc; 7 | }; 8 | -------------------------------------------------------------------------------- /isogram/isogram.js: -------------------------------------------------------------------------------- 1 | class Isogram { 2 | constructor(word) { 3 | this.word = word; 4 | } 5 | 6 | isIsogram() { 7 | return !/([^-\s]).*\1/i.test(this.word); 8 | } 9 | } 10 | 11 | module.exports = Isogram; 12 | -------------------------------------------------------------------------------- /leap/leap.js: -------------------------------------------------------------------------------- 1 | var Year = function(year) { 2 | this.year = year; 3 | } 4 | 5 | Year.prototype.isLeap = function() { 6 | /* Test if year is leap year */ 7 | return this.year%4==0 && (this.year%100!=0 || this.year%400==0); 8 | } 9 | 10 | module.exports = Year; 11 | -------------------------------------------------------------------------------- /accumulate/accumulate.js: -------------------------------------------------------------------------------- 1 | function accumulate(items, func) { 2 | /* Applies a function to every item in a list */ 3 | var modified = []; 4 | for(var i = 0; i < items.length; i++) 5 | modified.push( func(items[i]) ); 6 | return modified; 7 | } 8 | 9 | module.exports = accumulate; -------------------------------------------------------------------------------- /acronym/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "javascript", 3 | "version": "0.0.0", 4 | "description": "JavaScript assignments", 5 | "private": true, 6 | "scripts": { 7 | "test": "make test" 8 | }, 9 | "devDependencies": { 10 | "jasmine-node": "*" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /armstrong-numbers/armstrong-numbers.js: -------------------------------------------------------------------------------- 1 | function validate(number) { 2 | var digits = number.toString().split(''); 3 | return number == digits.reduce((acc, num) => 4 | Math.pow(parseInt(num), digits.length) + acc, 0); 5 | } 6 | 7 | module.exports = {validate: validate}; 8 | -------------------------------------------------------------------------------- /hello-world/hello-world.spec.js: -------------------------------------------------------------------------------- 1 | var HelloWorld = require('./hello-world'); 2 | 3 | describe('Hello World', function () { 4 | var helloWorld = new HelloWorld(); 5 | 6 | it('says hello world', function () { 7 | expect(helloWorld.hello()).toEqual('Hello, World!'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /flatten-array/flatten-array.js: -------------------------------------------------------------------------------- 1 | class Flattener { 2 | flatten(array) { 3 | if(array == null) { return [] } 4 | else if(array.constructor != Array) { return [array] } 5 | return array.reduce((acc, elem) => acc.concat(this.flatten(elem)), []); 6 | } 7 | } 8 | 9 | module.exports = Flattener; 10 | -------------------------------------------------------------------------------- /pangram/pangram.js: -------------------------------------------------------------------------------- 1 | Pangram = function(text) { 2 | this.text = text 3 | } 4 | 5 | Pangram.prototype.alphabet = [..."abcdefghijklmnopqrstuvwxyz"]; 6 | 7 | Pangram.prototype.isPangram = function() { 8 | var letters = this.text.toLowerCase(); 9 | return this.alphabet.every((letter) => letters.includes(letter)); 10 | } 11 | 12 | module.exports = Pangram 13 | -------------------------------------------------------------------------------- /hamming/hamming.js: -------------------------------------------------------------------------------- 1 | var Hamming = function() {}; 2 | 3 | Hamming.prototype.compute = function( dnaA, dnaB ) { 4 | var sum = 0; 5 | if (dnaA.length !== dnaB.length) { throw 'DNA strands must be of equal length.'} 6 | for( var i = 0; i <= dnaA.length; i++) { 7 | if (dnaA.charAt(i) !== dnaB.charAt(i)) {sum++} 8 | } 9 | return sum; 10 | } 11 | 12 | module.exports = Hamming; 13 | -------------------------------------------------------------------------------- /gigasecond/gigasecond.js: -------------------------------------------------------------------------------- 1 | // milliseconds 2 | var GIGASECOND = Math.pow(10, 12); 3 | 4 | var Gigasecond = function(day) { 5 | /* Adds a gigasecond (10^9 seconds) to a date */ 6 | this.day = day; 7 | }; 8 | 9 | // The date a gigasecond later 10 | Gigasecond.prototype.date = function() { 11 | return new Date(this.day.getTime() + GIGASECOND); 12 | }; 13 | 14 | module.exports = Gigasecond; 15 | -------------------------------------------------------------------------------- /grains/grains.js: -------------------------------------------------------------------------------- 1 | var BigInt = require('./big-integer.js'); 2 | 3 | module.exports = function () { 4 | /* Counts the number of grains of rice on squares of a chess board */ 5 | return { 6 | // Grains start at 1 and double every square 7 | square: function (n) { return BigInt(2).pow(n - 1).toString() }, 8 | total: function() { return BigInt(2).pow(64).prev().toString() }, 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /run-length-encoding/run-length-encoding.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | encode: function(text) { 3 | return text.replace(/([\w])\1+/g, function(group, chr) { 4 | return group.length + chr}); 5 | }, 6 | decode: function(text) { 7 | return text.replace(/(\d+)(\w)/g, function(_, number, chr) { 8 | return chr.repeat(number); 9 | }); 10 | }}; 11 | -------------------------------------------------------------------------------- /collatz-conjecture/collatz-conjecture.js: -------------------------------------------------------------------------------- 1 | class CollatzConjecture { 2 | steps(num) { 3 | if(num <= 0) { 4 | throw new Error('Only positive numbers are allowed'); 5 | } 6 | for(var i = 0; 1 < num; i++) { 7 | num = num % 2 == 0 ? num / 2 : num * 3 + 1; 8 | } 9 | return i; 10 | } 11 | } 12 | 13 | module.exports = CollatzConjecture; 14 | -------------------------------------------------------------------------------- /matrix/matrix.js: -------------------------------------------------------------------------------- 1 | var Matrix = function(matrix) { 2 | /* A 2d matrix class */ 3 | this.rows = matrix.split("\n").map( function(row) { 4 | return row.split(" ").map(function(e) { return parseInt(e) }); 5 | }); 6 | 7 | this.columns = Object.keys(this.rows[0]).map( function(colNum) { 8 | return this.map(function(row) { return row[colNum] }); 9 | }, this.rows); 10 | } 11 | 12 | module.exports = Matrix; -------------------------------------------------------------------------------- /difference-of-squares/difference-of-squares.js: -------------------------------------------------------------------------------- 1 | var Squares = function(num) { 2 | /* Calculates sums of difference functions */ 3 | // Based on triangle numbers and square pyramid numbers 4 | this.squareOfSums = (Math.pow(num, 2) * Math.pow(num + 1, 2)) / 4; 5 | this.sumOfSquares = num * (num + 1) * (2 * num + 1) / 6; 6 | this.difference = this.squareOfSums - this.sumOfSquares; 7 | } 8 | 9 | module.exports = Squares; -------------------------------------------------------------------------------- /octal/octal.js: -------------------------------------------------------------------------------- 1 | 2 | /* Octal number class */ 3 | var Octal = function(oct) { this.oct = oct } 4 | 5 | Octal.prototype.toDecimal = function() { 6 | /* Converts octal to decimal */ 7 | if(this.oct.match(/[^0-7]/)) 8 | return 0; 9 | return this.oct 10 | .split('') 11 | .reverse() 12 | .reduce(function(total, elem, i) { 13 | return total + elem * Math.pow(8, i); 14 | }, 0); 15 | }; 16 | 17 | module.exports = Octal; -------------------------------------------------------------------------------- /pig-latin/pig-latin.js: -------------------------------------------------------------------------------- 1 | function translate(phrase) { 2 | /* Translates a phrase into piglatin */ 3 | return phrase.split(' ').map(pigize).join(' '); 4 | } 5 | 6 | function pigize(word) { 7 | /* Turns a word into piglatin */ 8 | var matches = word.match(/^([^aeioy]*qu)(.*)$/) || 9 | word.match(/^(.*?)([aeiouy].*)$/) 10 | return matches ? matches[2] + matches[1] + "ay" : word; 11 | }; 12 | 13 | module.exports = { translate: translate } -------------------------------------------------------------------------------- /sieve/sieve.js: -------------------------------------------------------------------------------- 1 | var Sieve = function(size) { 2 | /* Finds prime numers */ 3 | 4 | this.primes = [2]; 5 | var sieve = Array.apply(null, new Array(size+1)) 6 | .map(Boolean.prototype.valueOf, true); 7 | 8 | for(var n = 3; n < sieve.length ; n += 2) { 9 | if(sieve[n]) { 10 | this.primes.push(n); 11 | for( var m = n*n; m < sieve.length; m += n) 12 | sieve[m] = false; 13 | } 14 | } 15 | } 16 | 17 | module.exports = Sieve; 18 | -------------------------------------------------------------------------------- /acronym/acronym.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parse: parse 3 | }; 4 | 5 | function parse(phrase) { 6 | return phrase.split(/\W+/) 7 | .map(function(word) { 8 | firstChar = word.charAt(0).toUpperCase(); 9 | if(word.match(/^[A-Z]+$/)) { 10 | return firstChar; 11 | } 12 | return firstChar + word.slice(1).replace(/[^A-Z]/g,""); 13 | }) 14 | .join(''); 15 | }; 16 | -------------------------------------------------------------------------------- /sum-of-multiples/sum-of-multiples.js: -------------------------------------------------------------------------------- 1 | function to(num) { 2 | /* Sum of numbers that are divisible by at least one of the multiples */ 3 | return Array.apply(null, Array(num)).reduce(function(total, _, n) { 4 | if(this.some(function(mul) { return n % mul == 0 })) 5 | return total + n; 6 | return total; 7 | }.bind(this.factors), 0) 8 | }; 9 | 10 | module.exports = function(factors) { 11 | return { factors: factors || [3,5], 12 | to: to }; 13 | }; -------------------------------------------------------------------------------- /binary/binary.js: -------------------------------------------------------------------------------- 1 | var Binary = function(binString) { this.binString = binString }; 2 | 3 | Binary.prototype.toDecimal = function() { 4 | /* Converts a binary string to decimal */ 5 | 6 | // Validate string 7 | if ( this.binString.match(/[^10]/) ) 8 | return 0; 9 | 10 | return this.binString.split('').reverse().reduce( 11 | function(total, bit, power) { 12 | return total + bit * Math.pow(2, power); 13 | }, 0 14 | ); 15 | }; 16 | 17 | module.exports = Binary; -------------------------------------------------------------------------------- /etl/etl.js: -------------------------------------------------------------------------------- 1 | var ETL = function() {} 2 | 3 | ETL.prototype.transform = function(oldSturct) { 4 | /* Converts an old data structure to a new format */ 5 | var newStruct = {}; 6 | // Iterates over keys 7 | Object.keys(oldSturct).forEach( function(key) { 8 | // Iterates over values 9 | oldSturct[key].forEach(function(val) { 10 | // Assign value to old key 11 | newStruct[val.toLowerCase()] = parseInt(key); 12 | }); 13 | }); 14 | return newStruct; 15 | } 16 | 17 | module.exports = ETL 18 | -------------------------------------------------------------------------------- /trinary/trinary.js: -------------------------------------------------------------------------------- 1 | var Trinary = function(digits) { 2 | /* Trinary number class */ 3 | this.digits = digits; 4 | } 5 | 6 | Trinary.prototype.toDecimal = function() { 7 | /* Converts trinary number to decimal */ 8 | var decimal = this.digits 9 | .split('') 10 | .reverse() 11 | .reduce( 12 | function(total, digit, power) { 13 | if(digit.match(/[012]/)) 14 | return total + digit * Math.pow(3, power); 15 | }, 0); 16 | return decimal || 0; 17 | }; 18 | 19 | module.exports = Trinary; -------------------------------------------------------------------------------- /strain/strain.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | keep: keep, 3 | discard: discard, 4 | } 5 | 6 | function keep(items, func) { 7 | /* Selects items for which a function is True */ 8 | var kept = []; 9 | for(var i = 0; i < items.length; i++) 10 | if( func( items[i] ) ) 11 | kept.push( items[i] ); 12 | return kept; 13 | } 14 | 15 | function discard(items, func) { 16 | /* Selects items for which a function is False */ 17 | var not_func = function(item) { return !func(item) }; 18 | return keep(items, not_func); 19 | } -------------------------------------------------------------------------------- /word-count/word-count.js: -------------------------------------------------------------------------------- 1 | var Words = function() {} 2 | 3 | Words.prototype.count = function( wordString ) { 4 | /* Counts the number of times each words appears */ 5 | var wordCount = {}; 6 | // Splits words on whitespace 7 | wordString.trim().split(/\s+/).forEach( function(word) { 8 | if(word in wordCount && !isNaN(wordCount[word])) 9 | wordCount[word]++; 10 | else 11 | // Initilize new word 12 | wordCount[word] = 1; 13 | }); 14 | return wordCount; 15 | }; 16 | 17 | module.exports = Words; 18 | -------------------------------------------------------------------------------- /bob/bob.js: -------------------------------------------------------------------------------- 1 | var Bob = function() {}; 2 | 3 | Bob.prototype.hey = function(input) { 4 | /* A teenager */ 5 | // Shouting (all upper case) 6 | if ( input.toUpperCase() === input && /[A-Z]/.test(input)) 7 | return "Whoa, chill out!"; 8 | // Question (ends with a question mark) 9 | if ( /\?\s*$/.test(input) ) 10 | return "Sure."; 11 | // Nothing (al white space) 12 | if ( /^\s*$/.test(input)) 13 | return "Fine. Be that way!"; 14 | // Default 15 | return "Whatever."; 16 | }; 17 | 18 | module.exports = Bob; 19 | -------------------------------------------------------------------------------- /isbn-verifier/isbn-verifier.js: -------------------------------------------------------------------------------- 1 | export default class ISBN { 2 | constructor(digits) { 3 | this.digits = digits; 4 | } 5 | 6 | isValid() { 7 | let sum = 0; 8 | let digit = 10; 9 | for (let d of this.digits.split('')) { 10 | if (digit <= 0) { return false; } 11 | if ((d >= '0' && d <= '9') || 12 | (digit === 1 && d === 'X')) { 13 | sum += digit * (d === 'X' ? 10 : d - '0'); 14 | digit -= 1; 15 | } 16 | } 17 | return digit === 0 && sum % 11 === 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /matrix/matrix.spec.js: -------------------------------------------------------------------------------- 1 | var Matrix = require('./matrix'); 2 | 3 | describe('Matrix', function () { 4 | 5 | it('can extract a row', function () { 6 | expect(new Matrix('1 2\n10 20').rows[0]).toEqual([1, 2]); 7 | }); 8 | 9 | it('can extract the other row', function () { 10 | expect(new Matrix('9 8 7\n19 18 17').rows[1]).toEqual([19, 18, 17]); 11 | }); 12 | 13 | it('can extract a column', function () { 14 | expect(new Matrix('89 1903 3\n18 3 1\n9 4 800').columns[1]) 15 | .toEqual([1903, 3, 4]); 16 | }); 17 | 18 | }); 19 | -------------------------------------------------------------------------------- /nth-prime/nth-prime.js: -------------------------------------------------------------------------------- 1 | var primes = [2, 3, 5, 7, 11, 13]; 2 | 3 | function find_nth_prime(n) { 4 | /* Finds the nth prime number */ 5 | if(n <= 0) 6 | throw new Error("Prime is not possible"); 7 | 8 | for(var num = 15; primes.length < n; num += 2) { 9 | var is_prime = true; 10 | for(var p = 0; is_prime && Math.pow(primes[p], 2) <= num; p++) { 11 | if(num % primes[p] == 0) 12 | is_prime = false; 13 | } 14 | if(is_prime) 15 | primes.push(num); 16 | } 17 | return primes[n-1]; 18 | }; 19 | 20 | module.exports = {nth: find_nth_prime}; -------------------------------------------------------------------------------- /rna-transcription/rna-transcription.js: -------------------------------------------------------------------------------- 1 | var DnaTranscriber = function() {}; 2 | 3 | DnaTranscriber.prototype.toRna = function(dna) { 4 | /* Tranlates dna to RNA */ 5 | var dnaToRna = makeHash("GCTA".split(''), "CGAU".split('')); 6 | return dna.split('').map(function(e) { return dnaToRna[e] } ).join(''); 7 | } 8 | 9 | function makeHash(keys, values) { 10 | /* Makes a hash table from key and value arrays */ 11 | var hash = {}; 12 | keys.forEach(function(key, index) { hash[key] = values[index] }); 13 | return hash; 14 | } 15 | 16 | module.exports = DnaTranscriber; 17 | -------------------------------------------------------------------------------- /atbash-cipher/atbash-cipher.js: -------------------------------------------------------------------------------- 1 | a_ascii = 'a'.charCodeAt(0); 2 | z_ascii = 'z'.charCodeAt(0); 3 | 4 | function encode(clear_text) { 5 | return clear_text.split('') 6 | .reduce(shift_char, '') 7 | .match(/.{1,5}/g) 8 | .join(' '); 9 | } 10 | 11 | var shift_char = function(text, char) { 12 | var ascii = char.toLowerCase().charCodeAt(0); 13 | if(char.match(/[a-zA-Z]/)) 14 | text += String.fromCharCode(z_ascii - (ascii - a_ascii)); 15 | else if(char.match(/[0-9]/)) 16 | text += char; 17 | return text; 18 | } 19 | 20 | module.exports.encode = encode; 21 | -------------------------------------------------------------------------------- /prime-factors/prime-factors.js: -------------------------------------------------------------------------------- 1 | function primeFactors(remainder) { 2 | /* Finds all prime factors of a number */ 3 | // Finds factors smallest to largest adding to the list of factors 4 | var factor = 2; 5 | var factors = []; 6 | while(Math.pow(factor, 2) <= remainder) { 7 | while(remainder % factor == 0) { 8 | factors.push(factor); 9 | remainder /= factor; 10 | } 11 | // factor += 1; 12 | factor += factor == 2 ? 1 : 2; 13 | } 14 | if(remainder != 1) 15 | factors.push(remainder); 16 | return factors; 17 | } 18 | 19 | module.exports = {for: primeFactors}; 20 | -------------------------------------------------------------------------------- /raindrops/raindrops.js: -------------------------------------------------------------------------------- 1 | var Raindrops = function() {}; 2 | 3 | Raindrops.prototype.convert = function(number) { 4 | /* Converts a number to a series of sounds based on factors */ 5 | var factorToSound = { 6 | 3: "Pling", 7 | 5: "Plang", 8 | 7: "Plong", 9 | }; 10 | 11 | function makeSounds(sounds, factor) { 12 | if(number % factor == 0) 13 | sounds += factorToSound[factor]; 14 | return sounds; 15 | } 16 | 17 | var raindrops = Object.keys(factorToSound).sort().reduce(makeSounds, ''); 18 | return raindrops || number.toString() ; 19 | }; 20 | 21 | module.exports = Raindrops; -------------------------------------------------------------------------------- /transpose/transpose.js: -------------------------------------------------------------------------------- 1 | module.exports = function(start) { 2 | const max_col = start.reduce((max, row) => Math.max(max, row.length), 0); 3 | result = []; 4 | for(var c = 0; c < max_col; c++) { 5 | var prefix = true; 6 | result.push([]); 7 | for(var r = 0; r < start.length; r++) { 8 | result[c][r] = start[r][c]; 9 | if(prefix && result[c][r] == undefined) 10 | result[c][r] = ' '; 11 | else 12 | prefix = false; 13 | } 14 | } 15 | return result.map(row => row.join('')); 16 | } 17 | -------------------------------------------------------------------------------- /bracket-push/bracket-push.js: -------------------------------------------------------------------------------- 1 | var bracket_pairs = {"{": "}", "[":"]", "(":")"}; 2 | 3 | function bracket(brackets) { 4 | /* checks that bracket syntax is correct */ 5 | var stack = []; 6 | for( var i = 0; i < brackets.length; i++) { 7 | var letter = brackets[i]; 8 | if( bracket_pairs.hasOwnProperty(letter) ) { 9 | stack.push(bracket_pairs[letter]); 10 | } else if( stack.length != 0 && stack[stack.length - 1] == letter) { 11 | stack.pop(); 12 | } else { 13 | return false; 14 | } 15 | } 16 | return stack.length == 0; 17 | }; 18 | 19 | module.exports = bracket; 20 | -------------------------------------------------------------------------------- /series/series.js: -------------------------------------------------------------------------------- 1 | var Series = function(digits) { 2 | /* Mannipulates a series of digits */ 3 | this.digits = digits.split('') 4 | .map(function(e) { return parseInt(e) } ); 5 | } 6 | 7 | Series.prototype.slices = function(sliceSize) { 8 | /* Slices digits into arrays of a certain size */ 9 | var num_slices = this.digits.length - sliceSize + 1 10 | if(num_slices <= 0) 11 | throw new Error("Slice size is too big."); 12 | return Array.apply(null, new Array(num_slices)) 13 | .map(function(_, i) { 14 | return this.slice(i, i + sliceSize); 15 | }, this.digits); 16 | }; 17 | 18 | module.exports = Series; -------------------------------------------------------------------------------- /two-fer/two-fer.spec.js: -------------------------------------------------------------------------------- 1 | 2 | var TwoFer = require('./two-fer'); 3 | 4 | describe('Two Fer', function () { 5 | var twoFer = new TwoFer(); 6 | 7 | it('gives one to you if no parameter given', function () { 8 | expect(twoFer.twoFer()).toEqual('One for you, one for me.'); 9 | }); 10 | 11 | it('gives one to Alice if \'Alice\' is given', function () { 12 | expect(twoFer.twoFer('Alice')).toEqual('One for Alice, one for me.'); 13 | }); 14 | 15 | it('gives one to Bob if \'Bob\' is given', function () { 16 | expect(twoFer.twoFer('Bob')).toEqual('One for Bob, one for me.'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /pascals-triangle/pascals-triangle.js: -------------------------------------------------------------------------------- 1 | var Triangle = function(nRows) { 2 | /* Builds Pascals Triangle */ 3 | this.rows = []; 4 | var row = [1]; 5 | for(var i = 0; i < nRows; i++ ) { 6 | this.rows.push(row); 7 | this.lastRow = row; 8 | row = next_row(row); 9 | } 10 | } 11 | 12 | function next_row(current_row) { 13 | /* Builds the next row of Pascals Triangle */ 14 | var row = []; 15 | var prev = 0; 16 | for(var j = 0; j < current_row.length; j++) { 17 | row.push(current_row[j] + prev); 18 | prev = current_row[j]; 19 | } 20 | row.push(prev); 21 | return row; 22 | } 23 | 24 | module.exports = Triangle; -------------------------------------------------------------------------------- /grade-school/grade-school.js: -------------------------------------------------------------------------------- 1 | /* A class to keep track of students in a school */ 2 | var School = function() { this.class = {} }; 3 | 4 | // The entire school 5 | School.prototype.roster = function() { return this.class }; 6 | // A grade level of the school 7 | School.prototype.grade = function(grade) { return this.class[grade] || [] }; 8 | 9 | School.prototype.add = function(student, grade) { 10 | /* Adds students to the school */ 11 | if( grade in this.class ) { 12 | this.class[grade].push(student); 13 | this.class[grade].sort(); 14 | } 15 | else 16 | this.class[grade] = [student]; 17 | }; 18 | 19 | module.exports = School; -------------------------------------------------------------------------------- /nth-prime/nth-prime.spec.js: -------------------------------------------------------------------------------- 1 | var prime = require ('./nth-prime'); 2 | 3 | describe('Prime',function() { 4 | 5 | it('first',function(){ 6 | expect(prime.nth(1)).toEqual(2); 7 | }); 8 | 9 | it('second',function(){ 10 | expect(prime.nth(2)).toEqual(3); 11 | }); 12 | 13 | it('sixth',function(){ 14 | expect(prime.nth(6)).toEqual(13); 15 | }); 16 | 17 | it('big prime',function(){ 18 | expect(prime.nth(10001)).toEqual(104743); 19 | }); 20 | 21 | it('weird case',function() { 22 | expect( function () { 23 | prime.nth(0); 24 | }).toThrow(new Error('Prime is not possible')); 25 | }); 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /binary-search-tree/binary-search-tree.js: -------------------------------------------------------------------------------- 1 | var BinaryTree = function(data) { 2 | /* Tree data structure */ 3 | this.data = data; 4 | this.left = null; 5 | this.right = null; 6 | } 7 | 8 | BinaryTree.prototype.insert = function(data) { 9 | /* Adds a value to the tree */ 10 | var node = (data <= this.data) ? 'left' : 'right'; 11 | if( this[node] ) 12 | this[node].insert(data); 13 | else 14 | this[node] = new BinaryTree(data); 15 | }; 16 | 17 | BinaryTree.prototype.each = function(func) { 18 | /* Sorted elements in the tree */ 19 | if(this.left) this.left.each(func); 20 | func(this.data); 21 | if(this.right) this.right.each(func); 22 | }; 23 | 24 | module.exports = BinaryTree; -------------------------------------------------------------------------------- /diamond/diamond.js: -------------------------------------------------------------------------------- 1 | const Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split('').map((letter, i) => 2 | new Array(26).fill(' ').map((_, e) => i == e ? letter : ' ') 3 | ); 4 | 5 | class Diamond { 6 | makeDiamond(letter) { 7 | const lineLen = letter.charCodeAt(0) - 'A'.charCodeAt(0) + 1; 8 | const upperDiamond = Letters 9 | .slice(0, lineLen) 10 | .map(line => line.slice(0,lineLen).reverse().join('') 11 | + line.slice(1,lineLen).join('')); 12 | return upperDiamond.concat(upperDiamond.slice(0, -1).reverse()) 13 | .join('\n') + '\n'; 14 | } 15 | } 16 | 17 | module.exports = Diamond; 18 | -------------------------------------------------------------------------------- /scrabble-score/scrabble-score.js: -------------------------------------------------------------------------------- 1 | /* Value of letters in scrabble */ 2 | var letterVal = { 3 | 'A': 1, 'E': 1, 'I': 1, 'O': 1, 'U': 1, 'L': 1, 4 | 'N': 1, 'R': 1, 'S': 1, 'T': 1, 'D': 2, 'G': 2, 5 | 'B': 3, 'C': 3, 'M': 3, 'P': 3, 6 | 'F': 4, 'H': 4, 'V': 4, 'W': 4, 'Y': 4, 7 | 'K': 5, 8 | 'J': 8, 'X': 8, 9 | 'Q': 10, 'Z': 10, 10 | } 11 | 12 | function score(word) { 13 | /* The score of a scrabble word */ 14 | // Words with invalid characters are worth zero 15 | if( ! (word && word.match(/^[a-zA-Z]+$/) ) ) 16 | return 0; 17 | return word.toUpperCase().split('').reduce( 18 | function(sum, e) { 19 | return sum + letterVal[e]; 20 | }, 0); 21 | }; 22 | 23 | module.exports = score; -------------------------------------------------------------------------------- /bracket-push/README.md: -------------------------------------------------------------------------------- 1 | # Bracket Push 2 | 3 | Make sure the braces all match. 4 | 5 | Ensure that all the curly braces and square brackets are matched correctly, 6 | and nested correctly. 7 | 8 | ## Setup 9 | 10 | Go through the setup instructions for JavaScript to 11 | install the necessary dependencies: 12 | 13 | http://help.exercism.io/getting-started-with-javascript.html 14 | 15 | ## Making the Test Suite Pass 16 | 17 | Execute the tests with: 18 | 19 | ```bash 20 | $ jasmine-node . 21 | ``` 22 | 23 | In many test suites all but the first test have been skipped. 24 | 25 | Once you get a test passing, you can unskip the next one by 26 | changing `xit` to `it`. 27 | 28 | 29 | ## Source 30 | 31 | Ginna Baker [view source]() 32 | -------------------------------------------------------------------------------- /rotational-cipher/rotational-cipher.js: -------------------------------------------------------------------------------- 1 | var shift = function(chr, diff) { 2 | return String.fromCharCode(chr.charCodeAt(0) + diff); 3 | } 4 | 5 | class RotationalCipher { 6 | rotate(text, diff) { 7 | var encoded = ''; 8 | for(var c = 0; c < text.length; c++) { 9 | var rot = 0; 10 | if("a" <= text[c] && text[c] <= "z") { 11 | rot = (shift(text[c], diff) <= "z") ? diff : (diff - 26); 12 | } else if("A" <= text[c] && text[c] <= "Z") { 13 | rot = (shift(text[c], diff) <= "Z") ? diff : (diff - 26); 14 | } 15 | encoded += shift(text[c], rot); 16 | } 17 | return encoded; 18 | } 19 | } 20 | 21 | module.exports = RotationalCipher; 22 | -------------------------------------------------------------------------------- /triangle/triangle.js: -------------------------------------------------------------------------------- 1 | /* Classifies triangles */ 2 | var Triangle = function(a,b,c) { this.triangle = [a,b,c].sort() } 3 | 4 | Triangle.prototype.kind = function() { 5 | /* Determines the type of triangle */ 6 | 7 | // Sides from smallest to largest 8 | var a = this.triangle[0]; 9 | var b = this.triangle[1]; 10 | var c = this.triangle[2]; 11 | 12 | // Validation 13 | if (a < 0) 14 | throw "Only positive length sides are allowed"; 15 | if (a * b * c == 0) 16 | throw "Triangle cannot have zero area"; 17 | if (a + b < c) 18 | throw "Not a valid triangle"; 19 | 20 | // Classification 21 | if (a === b && b === c) 22 | return "equilateral"; 23 | if(a === b || b === c) 24 | return "isosceles"; 25 | return "scalene"; 26 | }; 27 | 28 | module.exports = Triangle; 29 | -------------------------------------------------------------------------------- /hexadecimal/hexadecimal.js: -------------------------------------------------------------------------------- 1 | var Hexadecimal = function(hex_digits) { 2 | /* Hexadeciaml number class */ 3 | this.hex_digits = hex_digits; 4 | } 5 | 6 | Hexadecimal.prototype.toDecimal = function() { 7 | /* Converts hexadecimal to decimal */ 8 | return this.hex_digits 9 | .split('') 10 | .reverse() 11 | .map(hex_digit_value) 12 | .reduce(function(total, digit, power) { 13 | return total + digit * Math.pow(16, power); 14 | }, 0) || 0; 15 | }; 16 | 17 | function hex_digit_value(digit) { 18 | /* Converts a hexadecimal digit to integer or NaN*/ 19 | if(digit.match(/[0-9]/)) 20 | return parseInt(digit) 21 | else if(digit.match(/[a-f]/)) 22 | return digit.charCodeAt(0) - 'a'.charCodeAt(0) + 10; 23 | else 24 | return NaN 25 | } 26 | 27 | module.exports = Hexadecimal; 28 | -------------------------------------------------------------------------------- /proverb/proverb.js: -------------------------------------------------------------------------------- 1 | class Proverb { 2 | constructor() { 3 | var args = Array.from(arguments); 4 | this.words = args.filter(arg => typeof arg == 'string'); 5 | var nargs = args.filter(arg => typeof arg == 'object')[0]; 6 | this.qualifier = nargs && nargs.qualifier; 7 | } 8 | 9 | toString() { 10 | var result = []; 11 | for(var i = 1; i < this.words.length; i++) { 12 | result.push( 'For want of a ' + this.words[i-1] + 13 | ' the ' + this.words[i] + ' was lost.'); 14 | } 15 | result.push('And all for the want of a ' + 16 | (this.qualifier ? this.qualifier + ' ' : '') + this.words[0] + '.'); 17 | return result.join('\n'); 18 | } 19 | } 20 | 21 | module.exports = Proverb; 22 | -------------------------------------------------------------------------------- /scrabble-score/scrabble-score.spec.js: -------------------------------------------------------------------------------- 1 | var score = require('./scrabble-score'); 2 | 3 | describe('Scrabble', function() { 4 | it('scores an empty word as zero',function() { 5 | expect(score('')).toEqual(0); 6 | }); 7 | 8 | it('scores a null as zero',function() { 9 | expect(score(null)).toEqual(0); 10 | }); 11 | 12 | it('scores a very short word',function() { 13 | expect(score('a')).toEqual(1); 14 | }); 15 | 16 | it('scores the word by the number of letters',function() { 17 | expect(score('street')).toEqual(6); 18 | }); 19 | 20 | it('scores more complicated words with more',function() { 21 | expect(score('quirky')).toEqual(22); 22 | }); 23 | 24 | it('scores case insensitive words',function() { 25 | expect(score('MULTIBILLIONAIRE')).toEqual(20); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /acronym/README.md: -------------------------------------------------------------------------------- 1 | # Acronym 2 | 3 | Convert a long phrase to its acronym 4 | 5 | Techies love their TLA (Three Letter Acronyms)! 6 | 7 | Help generate some jargon by writing a program that converts a long name 8 | like Portable Network Graphics to its acronym (PNG). 9 | 10 | 11 | ## Setup 12 | 13 | Go through the setup instructions for JavaScript to 14 | install the necessary dependencies: 15 | 16 | http://exercism.io/languages/javascript 17 | 18 | ## Making the Test Suite Pass 19 | 20 | Execute the tests with: 21 | 22 | ```bash 23 | $ jasmine-node . 24 | ``` 25 | 26 | In many test suites all but the first test have been skipped. 27 | 28 | Once you get a test passing, you can unskip the next one by 29 | changing `xit` to `it`. 30 | 31 | 32 | ## Source 33 | 34 | Julien Vanier [view source](https://github.com/monkbroc) 35 | -------------------------------------------------------------------------------- /anagram/anagram.js: -------------------------------------------------------------------------------- 1 | var anagram = function( word ) { return new Anagram(word); }; 2 | 3 | /* Class for finding anagrams */ 4 | function Anagram(word) { this.word = word.toLowerCase(); }; 5 | 6 | Anagram.prototype.matches = function( wordList) { 7 | /* Finds all anagrams of the word */ 8 | if(! (wordList instanceof Array)) { 9 | // If the arguments aren't in list form 10 | var myArgs = arguments; 11 | wordList = Object.keys(myArgs).map(function(key) {return myArgs[key] } ); 12 | } 13 | var makeLetters = function(word) { return word.toLowerCase().split('').sort().join('') }; 14 | var letters = makeLetters(this.word), anagram = this.word; 15 | var anagrams = function(word) { return makeLetters(word) === letters && word.toLowerCase() !== anagram }; 16 | return wordList.filter( anagrams ); 17 | }; 18 | 19 | module.exports = anagram; -------------------------------------------------------------------------------- /gigasecond/README.md: -------------------------------------------------------------------------------- 1 | # Gigasecond 2 | 3 | Write a program that will calculate the date that someone turned or will celebrate their 1 Gs anniversary. 4 | 5 | A gigasecond is one billion (10**9) seconds. 6 | 7 | ## Setup 8 | 9 | Go through the setup instructions for JavaScript to 10 | install the necessary dependencies: 11 | 12 | http://help.exercism.io/getting-started-with-javascript.html 13 | 14 | ## Making the Test Suite Pass 15 | 16 | Execute the tests with: 17 | 18 | ```bash 19 | $ jasmine-node . 20 | ``` 21 | 22 | In many test suites all but the first test have been skipped. 23 | 24 | Once you get a test passing, you can unskip the next one by 25 | changing `xit` to `it`. 26 | 27 | 28 | ## Source 29 | 30 | Chapter 9 in Chris Pine's online Learn to Program tutorial. [view source](http://pine.fm/LearnToProgram/?Chapter=09) 31 | -------------------------------------------------------------------------------- /custom-set/README.md: -------------------------------------------------------------------------------- 1 | # Custom Set 2 | 3 | Create a custom set type. 4 | 5 | Sometimes it is necessary to define a custom data structure of some 6 | type, like a set. In this exercise you will define your own set. How it 7 | works internally doesn't matter, as long as it behaves like a set of 8 | unique elements. 9 | 10 | ## Setup 11 | 12 | Go through the setup instructions for JavaScript to 13 | install the necessary dependencies: 14 | 15 | http://help.exercism.io/getting-started-with-javascript.html 16 | 17 | ## Making the Test Suite Pass 18 | 19 | Execute the tests with: 20 | 21 | ```bash 22 | $ jasmine-node . 23 | ``` 24 | 25 | In many test suites all but the first test have been skipped. 26 | 27 | Once you get a test passing, you can unskip the next one by 28 | changing `xit` to `it`. 29 | 30 | 31 | ## Source 32 | 33 | [view source]() 34 | -------------------------------------------------------------------------------- /word-count/README.md: -------------------------------------------------------------------------------- 1 | # Word Count 2 | 3 | Write a program that given a phrase can count the occurrences of each word in that phrase. 4 | 5 | For example for the input `"olly olly in come free"` 6 | 7 | ```plain 8 | olly: 2 9 | in: 1 10 | come: 1 11 | free: 1 12 | ``` 13 | 14 | 15 | ## Setup 16 | 17 | Go through the setup instructions for JavaScript to 18 | install the necessary dependencies: 19 | 20 | http://help.exercism.io/getting-started-with-javascript.html 21 | 22 | ## Making the Test Suite Pass 23 | 24 | Execute the tests with: 25 | 26 | ```bash 27 | $ jasmine-node . 28 | ``` 29 | 30 | In many test suites all but the first test have been skipped. 31 | 32 | Once you get a test passing, you can unskip the next one by 33 | changing `xit` to `it`. 34 | 35 | 36 | ## Source 37 | 38 | The golang tour [view source](http://tour.golang.org) 39 | -------------------------------------------------------------------------------- /allergies/allergies.js: -------------------------------------------------------------------------------- 1 | var Allergies = function(code) { 2 | /* Uses a binary code to determine what someone is allergic to */ 3 | this.code = code; 4 | this.allergieList = [ 5 | 'eggs', 6 | 'peanuts', 7 | 'shellfish', 8 | 'strawberries', 9 | 'tomatoes' , 10 | 'chocolate' , 11 | 'pollen' , 12 | 'cats', 13 | ]; 14 | } 15 | 16 | Allergies.prototype.list = function() { 17 | /* A list of thing the person is allergic to */ 18 | return this.allergieList.filter( this.allergicTo, this); 19 | }; 20 | 21 | Allergies.prototype.allergicTo = function(item, index) { 22 | /* If the person is allergic to an item */ 23 | // Filter alread knows the index other look it up 24 | if(typeof index === 'undefined') 25 | index = this.allergieList.indexOf(item); 26 | return Boolean(this.code & (1 << index)); 27 | }; 28 | 29 | module.exports = Allergies; 30 | -------------------------------------------------------------------------------- /palindrome-products/README.md: -------------------------------------------------------------------------------- 1 | # Palindrome Products 2 | 3 | Write a program that can detect palindrome products in a given range. 4 | 5 | A palindromic number reads the same both ways. The largest palindrome 6 | made from the product of two 2-digit numbers is 9009 = 91 x 99. 7 | 8 | ## Setup 9 | 10 | Go through the setup instructions for JavaScript to 11 | install the necessary dependencies: 12 | 13 | http://help.exercism.io/getting-started-with-javascript.html 14 | 15 | ## Making the Test Suite Pass 16 | 17 | Execute the tests with: 18 | 19 | ```bash 20 | $ jasmine-node . 21 | ``` 22 | 23 | In many test suites all but the first test have been skipped. 24 | 25 | Once you get a test passing, you can unskip the next one by 26 | changing `xit` to `it`. 27 | 28 | 29 | ## Source 30 | 31 | Problem 4 at Project Euler [view source](http://projecteuler.net/problem=4) 32 | -------------------------------------------------------------------------------- /roman-numerals/roman-numerals.js: -------------------------------------------------------------------------------- 1 | /* Decimal to Roman numeral mapping */ 2 | var decToNum = { 1: "I", 4:"IV", 5: "V", 9:"IX", 3 | 10: "X", 40:"XL", 50: "L", 90:"XC", 4 | 100: "C", 400:"CD", 500: "D", 900:"CM", 5 | 1000: "M", 6 | }; 7 | 8 | function toRoman(remainer) { 9 | /* Convert a decimal number to a roman numeral */ 10 | 11 | function appendNumerals(total, decimal) { 12 | /* Appends as many roman numerals as possible */ 13 | while (remainer >= decimal) { 14 | remainer -= decimal; 15 | total += decToNum[decimal]; 16 | } 17 | return total; 18 | }; 19 | 20 | var decimalNumbers = Object.keys(decToNum).sort(reverseNumeric); 21 | return decimalNumbers.reduce( appendNumerals, ''); 22 | }; 23 | 24 | function reverseNumeric(a, b){ 25 | /* Reverse sort a numeric array */ 26 | return b - a; 27 | }; 28 | 29 | module.exports = toRoman; -------------------------------------------------------------------------------- /clock/README.md: -------------------------------------------------------------------------------- 1 | # Clock 2 | 3 | Implement a clock that handles times without dates. 4 | 5 | Create a clock that is independent of date. 6 | 7 | You should be able to add and subtract minutes to it. 8 | 9 | Two clocks that represent the same time should be equal to each other. 10 | 11 | ## Setup 12 | 13 | Go through the setup instructions for JavaScript to 14 | install the necessary dependencies: 15 | 16 | http://help.exercism.io/getting-started-with-javascript.html 17 | 18 | ## Making the Test Suite Pass 19 | 20 | Execute the tests with: 21 | 22 | ```bash 23 | $ jasmine-node . 24 | ``` 25 | 26 | In many test suites all but the first test have been skipped. 27 | 28 | Once you get a test passing, you can unskip the next one by 29 | changing `xit` to `it`. 30 | 31 | 32 | ## Source 33 | 34 | Pairing session with Erin Drummond [view source](https://twitter.com/ebdrummond) 35 | -------------------------------------------------------------------------------- /diamond/diamond.spec.js: -------------------------------------------------------------------------------- 1 | var Diamond = require('./diamond'); 2 | 3 | describe('Diamond', function () { 4 | var diamond = new Diamond(); 5 | 6 | it('test letter A', function () { 7 | var result = 'A\n'; 8 | expect(diamond.makeDiamond('A')).toEqual(result); 9 | }); 10 | 11 | it('test letter C', function () { 12 | var result = [' A ', 13 | ' B B ', 14 | 'C C', 15 | ' B B ', 16 | ' A '].join('\n') + '\n'; 17 | expect(diamond.makeDiamond('C')).toEqual(result); 18 | }); 19 | 20 | it('test letter E', function () { 21 | var result = [' A ', 22 | ' B B ', 23 | ' C C ', 24 | ' D D ', 25 | 'E E', 26 | ' D D ', 27 | ' C C ', 28 | ' B B ', 29 | ' A '].join('\n') + '\n'; 30 | expect(diamond.makeDiamond('E')).toEqual(result); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /secret-handshake/secret-handshake.js: -------------------------------------------------------------------------------- 1 | var SecretHandshake = function(code) { 2 | /* Creates a secret handshake based on the Mary Poppins code */ 3 | if(code.toString().match(/[^0-9]/)) 4 | throw new Error("Handshake must be a number"); 5 | this.code = code; 6 | this.handshakeSteps = [ function(shake) { shake.push("wink")}, 7 | function(shake) { shake.push("double blink")}, 8 | function(shake) { shake.push("close your eyes")}, 9 | function(shake) { shake.push("jump")}, 10 | function(shake) { shake.reverse()} 11 | ]; 12 | } 13 | 14 | SecretHandshake.prototype.commands = function() { 15 | /* Encodes the handshake */ 16 | return this.handshakeSteps.reduce(function(handshake, func, i) { 17 | if(this.code & 1 << i) 18 | func(handshake); 19 | return handshake; 20 | }.bind(this), []); 21 | }; 22 | 23 | module.exports = SecretHandshake; -------------------------------------------------------------------------------- /pangram/README.md: -------------------------------------------------------------------------------- 1 | # Pangram 2 | 3 | Determine if a sentence is a pangram. 4 | 5 | Determine if a sentence is a pangram. A pangram (Greek: παν γράμμα, pan gramma, 6 | "every letter") is a sentence using every letter of the alphabet at least once. 7 | The best known English pangram is "The quick brown fox jumps over the lazy dog." 8 | 9 | ## Setup 10 | 11 | Go through the setup instructions for JavaScript to 12 | install the necessary dependencies: 13 | 14 | http://exercism.io/languages/javascript 15 | 16 | ## Making the Test Suite Pass 17 | 18 | Execute the tests with: 19 | 20 | ```bash 21 | $ jasmine-node . 22 | ``` 23 | 24 | In many test suites all but the first test have been skipped. 25 | 26 | Once you get a test passing, you can unskip the next one by 27 | changing `xit` to `it`. 28 | 29 | 30 | ## Source 31 | 32 | Wikipedia [view source](https://en.wikipedia.org/wiki/Pangram) 33 | -------------------------------------------------------------------------------- /rna-transcription/rna-transcription.spec.js: -------------------------------------------------------------------------------- 1 | var DnaTranscriber = require('./rna-transcription'); 2 | var dnaTranscriber = new DnaTranscriber(); 3 | 4 | describe('toRna()', function() { 5 | 6 | it('transcribes cytosine to guanine', function() { 7 | expect(dnaTranscriber.toRna('C')).toEqual('G'); 8 | }); 9 | 10 | it('transcribes guanine to cytosine', function() { 11 | expect(dnaTranscriber.toRna('G')).toEqual('C'); 12 | }); 13 | 14 | it('transcribes adenine to uracil', function() { 15 | expect(dnaTranscriber.toRna('A')).toEqual('U'); 16 | }); 17 | 18 | it('transcribes thymine to adenine', function() { 19 | expect(dnaTranscriber.toRna('T')).toEqual('A'); 20 | }); 21 | 22 | it('transcribes all dna nucleotides to their rna complements', function() { 23 | expect(dnaTranscriber.toRna('ACGTGGTCTTAA')) 24 | .toEqual('UGCACCAGAAUU'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /binary-search/binary-search.js: -------------------------------------------------------------------------------- 1 | function BinarySearch(items) { 2 | this.items = items; 3 | }; 4 | 5 | BinarySearch.prototype = { 6 | get array() { 7 | if ( this.sorted !== undefined ){ 8 | return this.sorted; 9 | } 10 | for ( var i = 1; i < this.items.length; i++ ) { 11 | if ( this.items[i] < this.items[i-1] ) { 12 | return undefined; 13 | } 14 | } 15 | this.sorted = true; 16 | return this.items; 17 | }, 18 | 19 | indexOf: function(item) { 20 | for (var min = -1, max = this.items.length; min < max;) { 21 | mid = Math.floor(( min + max) / 2); 22 | if (this.items[mid] < item ) { 23 | min = mid + 1; 24 | } else if (item < this.items[mid]) { 25 | max = mid - 1; 26 | } else { 27 | return mid; 28 | } 29 | } 30 | return -1; 31 | }, 32 | }; 33 | 34 | module.exports = BinarySearch; 35 | -------------------------------------------------------------------------------- /anagram/README.md: -------------------------------------------------------------------------------- 1 | # Anagram 2 | 3 | Write a program that, given a word and a list of possible anagrams, selects the correct sublist. 4 | 5 | Given `"listen"` and a list of candidates like `"enlists" "google" 6 | "inlets" "banana"` the program should return a list containing 7 | `"inlets"`. 8 | 9 | ## Setup 10 | 11 | Go through the setup instructions for JavaScript to 12 | install the necessary dependencies: 13 | 14 | http://help.exercism.io/getting-started-with-javascript.html 15 | 16 | ## Making the Test Suite Pass 17 | 18 | Execute the tests with: 19 | 20 | ```bash 21 | $ jasmine-node . 22 | ``` 23 | 24 | In many test suites all but the first test have been skipped. 25 | 26 | Once you get a test passing, you can unskip the next one by 27 | changing `xit` to `it`. 28 | 29 | 30 | ## Source 31 | 32 | Inspired by the Extreme Startup game [view source](https://github.com/rchatley/extreme_startup) 33 | -------------------------------------------------------------------------------- /space-age/space-age.js: -------------------------------------------------------------------------------- 1 | // Your age on another planet 2 | function SpaceAge(seconds) { this.seconds = seconds } 3 | 4 | function onAny(ratio) { 5 | /* Generates a function that calculates an age in plnet years */ 6 | var earthYear = 31557600; // seconds 7 | return function() { 8 | /* The age of an individual on a planet */ 9 | var age = this.seconds / (earthYear * ratio); 10 | return parseFloat(age.toFixed(2)); 11 | } 12 | } 13 | 14 | // Your age on a planet 15 | SpaceAge.prototype.onMercury = onAny(0.2408467); 16 | SpaceAge.prototype.onVenus = onAny(0.61519726); 17 | SpaceAge.prototype.onEarth = onAny(1.0); 18 | SpaceAge.prototype.onMars = onAny(1.8808158); 19 | SpaceAge.prototype.onJupiter = onAny(11.862615); 20 | SpaceAge.prototype.onSaturn = onAny(29.447498); 21 | SpaceAge.prototype.onUranus = onAny(84.016846); 22 | SpaceAge.prototype.onNeptune = onAny(164.79132); 23 | 24 | module.exports = SpaceAge; -------------------------------------------------------------------------------- /perfect-numbers/perfect-numbers.js: -------------------------------------------------------------------------------- 1 | function factors(number) { 2 | var result = number == 1 ? [] : [1]; 3 | for(var f = 2; f*f <= number; f++) { 4 | if(number % f == 0) { 5 | result.push(f); 6 | if(f * f != number) { 7 | result.push(number / f); 8 | } 9 | } 10 | } 11 | return result; 12 | } 13 | 14 | class PerfectNumbers { 15 | classify(number) { 16 | if(number <= 0) { 17 | return "Classification is only possible for natural numbers."; 18 | } 19 | 20 | var factorSum = factors(number).reduce((a, b) => a + b, 0); 21 | if(number < factorSum) { 22 | return "abundant"; 23 | } else if(number > factorSum) { 24 | return "deficient"; 25 | } else { 26 | return "perfect"; 27 | } 28 | } 29 | } 30 | 31 | module.exports = PerfectNumbers; 32 | -------------------------------------------------------------------------------- /pythagorean-triplet/README.md: -------------------------------------------------------------------------------- 1 | # Pythagorean Triplet 2 | 3 | There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the product a * b * c. 4 | 5 | A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for 6 | which, 7 | 8 | a**2 + b**2 = c**2 9 | 10 | For example, 3**2 + 4**2 = 9 + 16 = 25 = 5**2. 11 | 12 | ## Setup 13 | 14 | Go through the setup instructions for JavaScript to 15 | install the necessary dependencies: 16 | 17 | http://help.exercism.io/getting-started-with-javascript.html 18 | 19 | ## Making the Test Suite Pass 20 | 21 | Execute the tests with: 22 | 23 | ```bash 24 | $ jasmine-node . 25 | ``` 26 | 27 | In many test suites all but the first test have been skipped. 28 | 29 | Once you get a test passing, you can unskip the next one by 30 | changing `xit` to `it`. 31 | 32 | 33 | ## Source 34 | 35 | Problem 9 at Project Euler [view source](http://projecteuler.net/problem=9) 36 | -------------------------------------------------------------------------------- /nucleotide-count/nucleotide-count.js: -------------------------------------------------------------------------------- 1 | var dna = function(sequence) { 2 | /* Analyzes DNA sequences */ 3 | var sprintf = require('util').format; 4 | dna.sequence = sequence || ''; 5 | dna.nucleotides = "GTAC".split(''); 6 | 7 | // Validates DNA sequence 8 | var vaildDNA = new RegExp(sprintf("^[%s]+$", dna.nucleotides.join(''))); 9 | if (sequence && !sequence.match(vaildDNA)) 10 | throw new Error("Not a valid DNA sequence"); 11 | return { 12 | count: count, 13 | histogram: histogram, 14 | }; 15 | }; 16 | 17 | function count(letter) { 18 | /* Counts the occurences of a single dna sequence */ 19 | var matches = dna.sequence.match(new RegExp(letter, "g")) || []; 20 | return matches.length; 21 | }; 22 | 23 | function histogram() { 24 | /* Counts the occurences of all dna sequences */ 25 | var counts = {}; 26 | dna.nucleotides.forEach(function(e) {counts[e] = count(e)}); 27 | return counts; 28 | }; 29 | 30 | module.exports = dna; 31 | -------------------------------------------------------------------------------- /diffie-hellman/diffie-hellman.js: -------------------------------------------------------------------------------- 1 | class DiffieHellman { 2 | constructor(p, g) { 3 | if( !(1 <= p && p < 9999) || !(1 <= g && g < 9999)) 4 | throw new Error("Arguments are out of range"); 5 | else if(!this.prime(p) || !this.prime(g)) 6 | throw new Error("Arguments are not prime"); 7 | 8 | this.p = p; 9 | this.g = g; 10 | } 11 | 12 | getPublicKeyFromPrivateKey(a) { 13 | if(!(2 <= a && a < this.p)) throw new Error("Not a valid private key"); 14 | 15 | return Math.pow(this.g, a) % this.p; 16 | } 17 | 18 | getSharedSecret(priv, pub) { 19 | return Math.pow(pub, priv) % this.p; 20 | } 21 | 22 | prime(num) { 23 | if(num % 2 == 0) 24 | return false; 25 | for(var f = 3; f * f <= num; f += 2) 26 | if(num % f == 0) return false; 27 | 28 | return true; 29 | } 30 | } 31 | 32 | module.exports = DiffieHellman; 33 | -------------------------------------------------------------------------------- /largest-series-product/largest-series-product.js: -------------------------------------------------------------------------------- 1 | var Series = function(digits) { 2 | /* A series of digits */ 3 | this.digits = digits.split('').map(function(e) {return parseInt(e)}); 4 | } 5 | 6 | Series.prototype.slices = function(size) { 7 | /* Slices digits into groups */ 8 | if(this.digits.length < size) 9 | throw new Error("Slice size is too big."); 10 | var slices = []; 11 | for(var i = 0; i + size <= this.digits.length; i++ ) { 12 | slices.push(this.digits.slice(i, i + size)); 13 | } 14 | return slices; 15 | }; 16 | 17 | Series.prototype.largestProduct = function(size) { 18 | /* The largest group when digits of slices are multipled together */ 19 | return Math.max.apply(null, this.slices(size).map(multiply)); 20 | }; 21 | 22 | function multiply(numbers) { 23 | /* Multiples all elements of an array together */ 24 | return numbers.reduce(function(total, element) { 25 | return total * element; 26 | }, 1); 27 | } 28 | 29 | module.exports = Series; -------------------------------------------------------------------------------- /nth-prime/README.md: -------------------------------------------------------------------------------- 1 | # Nth Prime 2 | 3 | Write a program that can tell you what the nth prime is. 4 | 5 | By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that 6 | the 6th prime is 13. 7 | 8 | If your language provides methods in the standard library to deal with prime 9 | numbers, pretend it doesn't exist and implement it yourself. 10 | 11 | ## Setup 12 | 13 | Go through the setup instructions for JavaScript to 14 | install the necessary dependencies: 15 | 16 | http://help.exercism.io/getting-started-with-javascript.html 17 | 18 | ## Making the Test Suite Pass 19 | 20 | Execute the tests with: 21 | 22 | ```bash 23 | $ jasmine-node . 24 | ``` 25 | 26 | In many test suites all but the first test have been skipped. 27 | 28 | Once you get a test passing, you can unskip the next one by 29 | changing `xit` to `it`. 30 | 31 | 32 | ## Source 33 | 34 | A variation on Problem 7 at Project Euler [view source](http://projecteuler.net/problem=7) 35 | -------------------------------------------------------------------------------- /protein-translation/protein-translation.js: -------------------------------------------------------------------------------- 1 | const translate = { 2 | 3 | 'AUG': 'Methionine', 4 | 'UCA': 'Serine', 5 | 'UCC': 'Serine', 6 | 'UCG': 'Serine', 7 | 'UCU': 'Serine', 8 | 'UUA': 'Leucine', 9 | 'UUC': 'Phenylalanine', 10 | 'UUG': 'Leucine', 11 | 'UUU': 'Phenylalanine', 12 | 'UAU': 'Tyrosine', 13 | 'UAC': 'Tyrosine', 14 | 'UGC': 'Cysteine', 15 | 'UGU': 'Cysteine', 16 | 'UGG': 'Tryptophan', 17 | 'UAA': 'STOP', 18 | 'UAG': 'STOP', 19 | 'UGA': 'STOP', 20 | } 21 | 22 | 23 | module.exports = function(rna) { 24 | var result = []; 25 | var protein; 26 | for(var r = 3; r <= (rna == undefined ? 0 : rna.length); r += 3) { 27 | protein = translate[rna.slice(r-3, r)]; 28 | if(protein == 'STOP') 29 | break; 30 | if(protein == undefined) 31 | throw new Error('Invalid codon'); 32 | result.push(protein); 33 | } 34 | return result; 35 | } 36 | -------------------------------------------------------------------------------- /phone-number/phone-number.js: -------------------------------------------------------------------------------- 1 | var PhoneNumber = function(phoneNumber) { 2 | /* Formats phone numbers */ 3 | // Start, optional 1, area code, first set of digits, second set of digits 4 | // Can have any non digits in between 5 | var reNumber = new RegExp(["^", "1?", "(\\d{3})", "(\\d{3})", "(\\d{4})", "$"].join("\\D*")); 6 | var match = phoneNumber.match(reNumber); 7 | if(match) { 8 | this.area = match[1]; 9 | this.first = match[2]; 10 | this.second = match[3]; 11 | this.num = match[1] + match[2] + match[3]; 12 | } 13 | else { 14 | // Should probably throw an error here 15 | this.num = Array(11).join('0'); 16 | } 17 | } 18 | 19 | var sprintf = require('util').format; 20 | PhoneNumber.prototype.number = function() { return this.num } 21 | PhoneNumber.prototype.areaCode = function() { return this.area } 22 | PhoneNumber.prototype.toString = function() { return sprintf("(%s) %s-%s", this.area, this.first, this.second) } 23 | 24 | module.exports = PhoneNumber; -------------------------------------------------------------------------------- /acronym/acronym.spec.js: -------------------------------------------------------------------------------- 1 | var Acronyms = require('./acronym'); 2 | 3 | describe('Acronyms are produced from', function(){ 4 | it('title cased phrases', function() { 5 | expect(Acronyms.parse('Portable Network Graphics')).toEqual('PNG'); 6 | }); 7 | 8 | it('other title cased phrases', function(){ 9 | expect(Acronyms.parse('Ruby on Rails')).toEqual('ROR'); 10 | }); 11 | 12 | it('inconsistently cased phrases', function(){ 13 | expect(Acronyms.parse('HyperText Markup Language')).toEqual('HTML'); 14 | }); 15 | 16 | it('phrases with punctuation', function() { 17 | expect(Acronyms.parse('First In, First Out')).toEqual('FIFO'); 18 | }); 19 | 20 | it('other phrases with punctuation', function() { 21 | expect(Acronyms.parse('PHP: Hypertext Preprocessor')).toEqual('PHP'); 22 | }); 23 | 24 | it('phrases with punctuation and sentence casing', function() { 25 | expect(Acronyms.parse('Complementary metal-oxide semiconductor')).toEqual('CMOS'); 26 | }); 27 | }); 28 | 29 | -------------------------------------------------------------------------------- /pascals-triangle/pascals-triangle.spec.js: -------------------------------------------------------------------------------- 1 | var Triangle = require('./pascals-triangle'); 2 | 3 | describe('Triangle', function () { 4 | 5 | it('with one row', function () { 6 | expect(new Triangle(1).rows).toEqual([[1]]); 7 | }); 8 | 9 | it('with two rows', function () { 10 | expect(new Triangle(2).rows).toEqual([[1], [1, 1]]); 11 | }); 12 | 13 | it('with three rows', function () { 14 | expect(new Triangle(3).rows).toEqual([[1], [1, 1], [1, 2, 1]]); 15 | }); 16 | 17 | it('last row', function () { 18 | expect(new Triangle(4).lastRow).toEqual([1, 3, 3, 1]); 19 | }); 20 | 21 | it('fifth row', function () { 22 | expect(new Triangle(5).lastRow).toEqual([1, 4, 6, 4 ,1]); 23 | }); 24 | 25 | it('twentieth row', function () { 26 | var twentieth = [1, 19, 171, 969, 3876, 11628, 27132, 50388, 75582, 92378, 92378, 75582, 50388, 27132, 11628, 3876, 969, 171, 19, 1]; 27 | expect(new Triangle(20).lastRow) 28 | .toEqual(twentieth); 29 | }); 30 | 31 | }); 32 | -------------------------------------------------------------------------------- /pascals-triangle/README.md: -------------------------------------------------------------------------------- 1 | # Pascals Triangle 2 | 3 | Write a program that computes Pascal's triangle up to a given number of rows. 4 | 5 | In Pascal's Triangle each number is computed by adding the numbers to 6 | the right and left of the current position in the previous row. 7 | 8 | ```plain 9 | 1 10 | 1 1 11 | 1 2 1 12 | 1 3 3 1 13 | 1 4 6 4 1 14 | # ... etc 15 | ``` 16 | 17 | ## Setup 18 | 19 | Go through the setup instructions for JavaScript to 20 | install the necessary dependencies: 21 | 22 | http://help.exercism.io/getting-started-with-javascript.html 23 | 24 | ## Making the Test Suite Pass 25 | 26 | Execute the tests with: 27 | 28 | ```bash 29 | $ jasmine-node . 30 | ``` 31 | 32 | In many test suites all but the first test have been skipped. 33 | 34 | Once you get a test passing, you can unskip the next one by 35 | changing `xit` to `it`. 36 | 37 | 38 | ## Source 39 | 40 | Pascal's Triangle at Wolfram Math World [view source](http://mathworld.wolfram.com/PascalsTriangle.html) 41 | -------------------------------------------------------------------------------- /variable-length-quantity/variable-length-quantity.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | encode(number) { 3 | const result = []; 4 | for (let remaining of number) { 5 | const set = []; 6 | while (remaining > 0) { 7 | set.unshift(remaining & 0x7f | 0x80); 8 | remaining >>>= 7; 9 | } 10 | if (set.length === 0) { 11 | set.push(0); 12 | } else { 13 | set[set.length - 1] &= 0x7f; 14 | } 15 | result.push(...set); 16 | } 17 | return result; 18 | }, 19 | decode(bytes) { 20 | const result = []; 21 | let value = 0; 22 | let processed = 0; 23 | for (const b of bytes) { 24 | value = (value << 7) | (b & 0x7f); 25 | processed += 1; 26 | if (!((b & 0x80) > 0)) { 27 | result.push(value>>>0); 28 | value = 0; 29 | processed = 0; 30 | } 31 | } 32 | if (processed !== 0) { 33 | throw new Error('Incomplete sequence'); 34 | } 35 | return result; 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /robot-name/robot-name.js: -------------------------------------------------------------------------------- 1 | var Robot = function() { this.name = getName() } 2 | 3 | Robot.prototype.reset = function() { 4 | /* Give the robot a new name */ 5 | var oldName = this.name; 6 | this.name = getName(); 7 | }; 8 | 9 | // Keeps track of robot names in use 10 | var namesInUse = new Set(); 11 | 12 | function getName() { 13 | /* Generates a robot name */ 14 | // Functions for different parts of the name 15 | var rand = function(max, min) { return Math.floor(Math.random() * (max - min + 1) + min)}; 16 | var letter = function() { return rand('Z'.charCodeAt(0), 'A'.charCodeAt(0)) }; 17 | var digit = function() { return rand('9'.charCodeAt(0), '0'.charCodeAt(0)) }; 18 | // Gets name that is not in use 19 | // Potential for infinite loop if there are lots (676000) of robots 20 | do { 21 | var name = String.fromCharCode(letter(), letter(), digit(), digit(), digit()); 22 | } while( namesInUse.has(name) ); 23 | 24 | namesInUse.add(name); 25 | return name; 26 | } 27 | 28 | module.exports = Robot; 29 | -------------------------------------------------------------------------------- /reverse-string/reverse-string.spec.js: -------------------------------------------------------------------------------- 1 | var reverseString = require('./reverse-string'); 2 | 3 | describe('ReverseString', function () { 4 | it('empty string', function () { 5 | var expected = ''; 6 | var actual = reverseString(''); 7 | expect(actual).toEqual(expected); 8 | }); 9 | 10 | it('a word', function () { 11 | var expected = 'tobor'; 12 | var actual = reverseString('robot'); 13 | expect(actual).toEqual(expected); 14 | }); 15 | 16 | it('a capitalized word', function () { 17 | var expected = 'nemaR'; 18 | var actual = reverseString('Ramen'); 19 | expect(actual).toEqual(expected); 20 | }); 21 | 22 | it('a sentence with punctuation', function () { 23 | var expected = '!yrgnuh ma I'; 24 | var actual = reverseString('I am hungry!'); 25 | expect(actual).toEqual(expected); 26 | }); 27 | 28 | it('a palindrome', function () { 29 | var expected = 'racecar'; 30 | var actual = reverseString('racecar'); 31 | expect(actual).toEqual(expected); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /sum-of-multiples/README.md: -------------------------------------------------------------------------------- 1 | # Sum Of Multiples 2 | 3 | Write a program that, given a number, can find the sum of all the multiples of 3 or 5 up to but not including that number. 4 | 5 | If we list all the natural numbers below 10 that are multiples of 3 or 6 | 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. 7 | 8 | Allow the program to be configured to find the sum of multiples of 9 | numbers other than 3 and 5. 10 | 11 | ## Setup 12 | 13 | Go through the setup instructions for JavaScript to 14 | install the necessary dependencies: 15 | 16 | http://help.exercism.io/getting-started-with-javascript.html 17 | 18 | ## Making the Test Suite Pass 19 | 20 | Execute the tests with: 21 | 22 | ```bash 23 | $ jasmine-node . 24 | ``` 25 | 26 | In many test suites all but the first test have been skipped. 27 | 28 | Once you get a test passing, you can unskip the next one by 29 | changing `xit` to `it`. 30 | 31 | 32 | ## Source 33 | 34 | A variation on Problem 1 at Project Euler [view source](http://projecteuler.net/problem=1) 35 | -------------------------------------------------------------------------------- /triangle/README.md: -------------------------------------------------------------------------------- 1 | # Triangle 2 | 3 | Write a program that can tell you if a triangle is equilateral, isosceles, or scalene. 4 | 5 | The program should raise an error if the triangle cannot exist. 6 | 7 | Tests are provided, delete one `skip` at a time. 8 | 9 | ## Hint 10 | 11 | The sum of the lengths of any two sides of a triangle always exceeds the 12 | length of the third side, a principle known as the _triangle 13 | inequality_. 14 | 15 | ## Setup 16 | 17 | Go through the setup instructions for JavaScript to 18 | install the necessary dependencies: 19 | 20 | http://help.exercism.io/getting-started-with-javascript.html 21 | 22 | ## Making the Test Suite Pass 23 | 24 | Execute the tests with: 25 | 26 | ```bash 27 | $ jasmine-node . 28 | ``` 29 | 30 | In many test suites all but the first test have been skipped. 31 | 32 | Once you get a test passing, you can unskip the next one by 33 | changing `xit` to `it`. 34 | 35 | 36 | ## Source 37 | 38 | The Ruby Koans triangle project, parts 1 & 2 [view source](http://rubykoans.com) 39 | -------------------------------------------------------------------------------- /queen-attack/queen-attack.js: -------------------------------------------------------------------------------- 1 | var Queens = function(args) { 2 | /* Queens on a chess board */ 3 | this.white = (args && args.white) || [0, 3]; 4 | this.black = (args && args.black) || [7, 3]; 5 | if(this.white === this.black) 6 | throw "Queens cannot share the same space"; 7 | } 8 | 9 | Queens.prototype.toString = function() { 10 | /* String representation of the chess board */ 11 | return Array.apply(null, Array(8)).map(function(_, i) { 12 | return Array.apply(null, Array(8)).map(function(_, j) { 13 | if(this.white[0] == i && this.white[1] == j) 14 | return "W"; 15 | if(this.black[0] == i && this.black[1] == j) 16 | return "B"; 17 | return "_"; 18 | }, this).join(" ") + "\n"; 19 | }, this).join(""); 20 | }; 21 | 22 | Queens.prototype.canAttack = function() { 23 | /* Can the queens attack eachother */ 24 | var diffs = this.black.map(function(e, i) { 25 | return Math.abs(e - this.white[i]) 26 | }, this); 27 | return (Math.min.apply(null, diffs) == 0 || diffs[0] == diffs[1]); 28 | }; 29 | 30 | module.exports = Queens; 31 | -------------------------------------------------------------------------------- /kindergarten-garden/kindergarten-garden.js: -------------------------------------------------------------------------------- 1 | var DEFAULT_STUDENTS = [ 'Alice', 'Bob', 'Charlie', 'David', 'Eve', 2 | 'Fred', 'Ginny', 'Harriet', 'Ileana', 'Joseph', 'Kincaid', 'Larry']; 3 | 4 | var Garden = function(garden, students) { 5 | /* A Kindergarden plant garden */ 6 | students ? students.sort(): (students = DEFAULT_STUDENTS); 7 | garden = garden.split('\n').map(MakeRow); 8 | 9 | for(var i = 0; i < students.length; i++) { 10 | this[students[i].toLowerCase()] = StudentsGarden(garden, i); 11 | } 12 | }; 13 | 14 | function MakeRow(row) { 15 | /* Converts a row of plant letters to an array of plants */ 16 | plant_map = {'C': 'clover', 'G': 'grass', 'R': 'radishes', 'V': 'violets'}; 17 | return row.split('').map( function(plant) { return plant_map[plant] }); 18 | } 19 | 20 | function StudentsGarden(garden, position) { 21 | /* The plants owned by an individual student */ 22 | return garden.reduce(function(plants, row) { 23 | return plants.concat(row.slice(2 * position, 2 * position + 2)); 24 | }, []); 25 | } 26 | 27 | module.exports = Garden; -------------------------------------------------------------------------------- /all-your-base/all-your-base.js: -------------------------------------------------------------------------------- 1 | class Converter { 2 | convert(digits, from, to) { 3 | if(from == null || from <= 1 || Math.floor(from) != from) { 4 | throw new Error('Wrong input base'); 5 | } 6 | if(to == null || to <= 1 || Math.floor(to) != to) { 7 | throw new Error('Wrong output base'); 8 | } 9 | if(digits.length == 0 || (1 < digits.length && digits[0] == 0)) { 10 | throw new Error('Input has wrong format'); 11 | } 12 | 13 | var total = digits.map(d => parseInt(d)).reduce((total, digit) => { 14 | if(digit < 0 || from <= digit) { 15 | throw new Error('Input has wrong format'); 16 | } 17 | return total * from + digit; 18 | }); 19 | 20 | var result = []; 21 | while(1 <= total) { 22 | result.unshift(total % to); 23 | total = Math.floor(total / to); 24 | } 25 | return result.length != 0 ? result : [0]; 26 | } 27 | } 28 | 29 | module.exports = Converter; 30 | -------------------------------------------------------------------------------- /hexadecimal/README.md: -------------------------------------------------------------------------------- 1 | # Hexadecimal 2 | 3 | Write a program that will convert a hexadecimal number, represented as a string (e.g. "10af8c"), to its decimal equivalent using first principles (i.e. no, you may not use built-in ruby libraries or gems to accomplish the conversion). 4 | 5 | On the web we use hexadecimal to represent colors, e.g. green: 008000, 6 | teal: 008080, navy: 000080). 7 | 8 | The program should handle invalid hexadecimal strings. 9 | 10 | ## Setup 11 | 12 | Go through the setup instructions for JavaScript to 13 | install the necessary dependencies: 14 | 15 | http://help.exercism.io/getting-started-with-javascript.html 16 | 17 | ## Making the Test Suite Pass 18 | 19 | Execute the tests with: 20 | 21 | ```bash 22 | $ jasmine-node . 23 | ``` 24 | 25 | In many test suites all but the first test have been skipped. 26 | 27 | Once you get a test passing, you can unskip the next one by 28 | changing `xit` to `it`. 29 | 30 | 31 | ## Source 32 | 33 | All of Computer Science [view source](http://www.wolframalpha.com/examples/NumberBases.html) 34 | -------------------------------------------------------------------------------- /saddle-points/saddle-points.js: -------------------------------------------------------------------------------- 1 | var Matrix = function(matrix) { 2 | /* Two dimentional matrix class */ 3 | this.rows = matrix.split("\n").map(function(row) { 4 | return row.replace(/^\s+|\s+&/g,'').split(' ') 5 | .map(function(e) { return parseInt(e)}); 6 | }); 7 | 8 | this.columns = Array.apply(null, new Array(this.rows[0].length)) 9 | .map(function(_, col) { 10 | return this.map(function(row) { return row[col] }); 11 | }, this.rows 12 | ); 13 | this.saddlePoints = saddlePoints(this); 14 | } 15 | 16 | function saddlePoints(matrix) { 17 | /* Finds saddle points in a matrix */ 18 | var row_max = matrix.rows.map(function(row){ return Math.max.apply(null, row)}); 19 | var column_min = matrix.columns.map(function(column){ return Math.min.apply(null, column)}); 20 | var saddlePoints = []; 21 | matrix.rows.forEach(function(row, r) { 22 | row.forEach(function(element, c) { 23 | if(element == row_max[r] && element == column_min[c]) 24 | saddlePoints.push([r,c]); 25 | }) 26 | }); 27 | return saddlePoints; 28 | }; 29 | 30 | module.exports = Matrix; -------------------------------------------------------------------------------- /series/README.md: -------------------------------------------------------------------------------- 1 | # Series 2 | 3 | Write a program that will take a string of digits and give you all the possible consecutive number series of length `n` in that string. 4 | 5 | For example, the string "01234" has the following 3-digit series: 6 | 7 | - 012 8 | - 123 9 | - 234 10 | 11 | And the following 4-digit series: 12 | 13 | - 0123 14 | - 1234 15 | 16 | And if you ask for a 6-digit series from a 5-digit string, you deserve 17 | whatever you get. 18 | 19 | ## Setup 20 | 21 | Go through the setup instructions for JavaScript to 22 | install the necessary dependencies: 23 | 24 | http://help.exercism.io/getting-started-with-javascript.html 25 | 26 | ## Making the Test Suite Pass 27 | 28 | Execute the tests with: 29 | 30 | ```bash 31 | $ jasmine-node . 32 | ``` 33 | 34 | In many test suites all but the first test have been skipped. 35 | 36 | Once you get a test passing, you can unskip the next one by 37 | changing `xit` to `it`. 38 | 39 | 40 | ## Source 41 | 42 | A subset of the Problem 8 at Project Euler [view source](http://projecteuler.net/problem=8) 43 | -------------------------------------------------------------------------------- /pangram/pangram.spec.js: -------------------------------------------------------------------------------- 1 | var Pangram = require('./pangram'); 2 | 3 | describe('Pangram()', function() { 4 | 5 | it('empty sentence', function() { 6 | var pangram = new Pangram(''); 7 | expect(pangram.isPangram()).toBe(false); 8 | }); 9 | 10 | it('pangram with only lower case', function() { 11 | var pangram = new Pangram("the quick brown fox jumps over the lazy dog"); 12 | expect(pangram.isPangram()).toBe(true); 13 | }); 14 | 15 | it("missing character 'x'", function() { 16 | var pangram = new Pangram("a quick movement of the enemy will jeopardize five gunboats"); 17 | expect(pangram.isPangram()).toBe(false); 18 | }); 19 | 20 | it('pangram with mixed case and punctuation', function() { 21 | var pangram = new Pangram("\"Five quacking Zephyrs jolt my wax bed.\""); 22 | expect(pangram.isPangram()).toBe(true); 23 | }); 24 | 25 | it('pangram with non-ascii characters', function() { 26 | var pangram = new Pangram("Victor jagt zwölf Boxkämpfer quer über den großen Sylter Deich."); 27 | expect(pangram.isPangram()).toBe(true); 28 | }); 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /binary-search/binary-search.spec.js: -------------------------------------------------------------------------------- 1 | var BinarySearch = require('./binary-search'); 2 | 3 | describe('BinarySearch', function() { 4 | 5 | var sortedArray = [1, 2, 3, 4, 5, 6]; 6 | var sortedArrayOfOddLength = [0, 1, 2, 2, 3, 10, 12]; 7 | var unsortedArray = [10, 2, 5, 1]; 8 | 9 | it ('should require a sorted array', function() { 10 | var invalidBinarySearch = new BinarySearch(unsortedArray); 11 | var validBinarySearch = new BinarySearch(sortedArray); 12 | 13 | expect(typeof invalidBinarySearch.array).toEqual('undefined'); 14 | expect(Array.isArray(validBinarySearch.array)).toEqual(true); 15 | }); 16 | 17 | it('should find the correct index of an included value', function() { 18 | expect(2).toEqual(new BinarySearch(sortedArray).indexOf(3)); 19 | }); 20 | 21 | it('should search the middle of the array', function() { 22 | expect(3).toEqual(new BinarySearch(sortedArrayOfOddLength).indexOf(2)); 23 | }); 24 | 25 | it('should return -1 for a value not in the array', function() { 26 | expect(-1).toEqual(new BinarySearch(sortedArray).indexOf(10)); 27 | }); 28 | }); 29 | 30 | -------------------------------------------------------------------------------- /sum-of-multiples/sum-of-multiples.spec.js: -------------------------------------------------------------------------------- 1 | var SumOfMultiples = require('./sum-of-multiples'); 2 | 3 | describe('SumOfMultiples', function () { 4 | it('to 1', function () { 5 | expect(SumOfMultiples().to(1)).toBe(0); 6 | }); 7 | 8 | it('to 3', function () { 9 | expect(SumOfMultiples().to(4)).toBe(3); 10 | }); 11 | 12 | it('to 10', function () { 13 | expect(SumOfMultiples().to(10)).toBe(23); 14 | }); 15 | 16 | it('to 100', function () { 17 | expect(SumOfMultiples().to(100)).toBe(2318); 18 | }); 19 | 20 | it('to 1000', function () { 21 | expect(SumOfMultiples().to(1000)).toBe(233168); 22 | }); 23 | 24 | it('[7, 13, 17] to 20', function () { 25 | expect(SumOfMultiples([7, 13, 17]).to(20)).toBe(51); 26 | }); 27 | 28 | it('[4, 6] to 15', function () { 29 | expect(SumOfMultiples([4, 6]).to(15)).toBe(30); 30 | }); 31 | 32 | it('[5, 6, 8] to 150', function () { 33 | expect(SumOfMultiples([5, 6, 8]).to(150)).toBe(4419); 34 | }); 35 | 36 | it('[43, 47] to 10000', function () { 37 | expect(SumOfMultiples([43, 47]).to(10000)).toBe(2203160); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /list-ops/README.md: -------------------------------------------------------------------------------- 1 | # List Ops 2 | 3 | Implement basic list operations. 4 | 5 | In functional languages list operations like `length`, `map`, and 6 | `reduce` are very common. Implement a series of basic list operations, 7 | without using existing functions. 8 | 9 | ## Setup 10 | 11 | Go through the setup instructions for JavaScript to install the 12 | necessary dependencies: 13 | 14 | http://exercism.io/languages/javascript/installation 15 | 16 | ## Running the test suite 17 | 18 | The provided test suite uses [Jasmine](https://jasmine.github.io/). 19 | You can install it by opening a terminal window and running the 20 | following command: 21 | 22 | ```sh 23 | npm install -g jasmine 24 | ``` 25 | 26 | Run the test suite from the exercise directory with: 27 | 28 | ```sh 29 | jasmine list-ops.spec.js 30 | ``` 31 | 32 | In many test suites all but the first test have been marked "pending". 33 | Once you get a test passing, activate the next one by changing `xit` to `it`. 34 | 35 | ## Submitting Incomplete Solutions 36 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 37 | -------------------------------------------------------------------------------- /pythagorean-triplet/pythagorean-triplet.js: -------------------------------------------------------------------------------- 1 | /* Finds pythagorean triplets */ 2 | var Triplet = function(a,b,c) { this.a = a, this.b = b, this.c = c } 3 | 4 | /* Sum of the sides of a triple */ 5 | Triplet.prototype.sum = function() { return this.a + this.b + this.c }; 6 | 7 | /* Product of the sides of a triple */ 8 | Triplet.prototype.product = function() { return this.a * this.b * this.c }; 9 | 10 | Triplet.prototype.isPythagorean = function() { 11 | /* Tests if the set of number is pythagorean */ 12 | return (this.a*this.a) + (this.b*this.b) == (this.c*this.c); 13 | }; 14 | 15 | Triplet.where = function(params) { 16 | /* Finds triplets with certain characterstics */ 17 | var triplets = []; 18 | for(var a = params.minFactor || 1; a <= params.maxFactor; a++) { 19 | for(var b = a; b <= params.maxFactor; b++) { 20 | for(var c = b; c <= params. maxFactor; c++) { 21 | var t = new Triplet(a,b,c); 22 | var checkSums = !params.sum || (params.sum == t.sum()); 23 | if(t.isPythagorean() && checkSums ) 24 | triplets.push(t); 25 | } 26 | } 27 | } 28 | return triplets; 29 | }; 30 | 31 | module.exports = Triplet; -------------------------------------------------------------------------------- /simple-cipher/simple-cipher.js: -------------------------------------------------------------------------------- 1 | var Cipher = function(key) { 2 | /* Simple Caesarian shift Cipher */ 3 | if( key !== undefined && !key.match(/^[a-z]+$/) ) 4 | throw Error("Bad key"); 5 | this.key = key || "aaaaaaaaaa"; 6 | } 7 | 8 | var a_ascii = 'a'.charCodeAt(0); 9 | var z_ascii = 'z'.charCodeAt(0); 10 | 11 | Cipher.prototype.encode = function(plainText) { 12 | /* Encode a message */ 13 | return plainText.split('') 14 | .map(function(letter, index) { 15 | var c = letter.charCodeAt(0) + this.key[index % this.key.length].charCodeAt(0); 16 | c -= 2 * a_ascii 17 | c %= z_ascii - a_ascii + 1; 18 | c += a_ascii; 19 | return String.fromCharCode(c); 20 | }, this) 21 | .join(''); 22 | }; 23 | 24 | Cipher.prototype.decode = function(cipherText) { 25 | /* Decode a message */ 26 | return cipherText.split('') 27 | .map(function(letter, index) { 28 | var c = letter.charCodeAt(0) - this.key[index % this.key.length].charCodeAt(0); 29 | c %= z_ascii - a_ascii + 1; 30 | c += a_ascii; 31 | return String.fromCharCode(c); 32 | }, this) 33 | .join(''); 34 | }; 35 | 36 | module.exports = Cipher; -------------------------------------------------------------------------------- /difference-of-squares/README.md: -------------------------------------------------------------------------------- 1 | # Difference Of Squares 2 | 3 | Find the difference between the sum of the squares and the square of the sums of the first N natural numbers. 4 | 5 | The sum of the squares of the first ten natural numbers is, 6 | 7 | 1**2 + 2**2 + ... + 10**2 = 385 8 | 9 | The square of the sum of the first ten natural numbers is, 10 | 11 | (1 + 2 + ... + 10)**2 = 55**2 = 3025 12 | 13 | Hence the difference between the sum of the squares of the first ten 14 | natural numbers and the square of the sum is 3025 - 385 = 2640. 15 | 16 | ## Setup 17 | 18 | Go through the setup instructions for JavaScript to 19 | install the necessary dependencies: 20 | 21 | http://help.exercism.io/getting-started-with-javascript.html 22 | 23 | ## Making the Test Suite Pass 24 | 25 | Execute the tests with: 26 | 27 | ```bash 28 | $ jasmine-node . 29 | ``` 30 | 31 | In many test suites all but the first test have been skipped. 32 | 33 | Once you get a test passing, you can unskip the next one by 34 | changing `xit` to `it`. 35 | 36 | 37 | ## Source 38 | 39 | Problem 6 at Project Euler [view source](http://projecteuler.net/problem=6) 40 | -------------------------------------------------------------------------------- /largest-series-product/README.md: -------------------------------------------------------------------------------- 1 | # Largest Series Product 2 | 3 | Write a program that, when given a string of digits, can calculate the largest product for a series of consecutive digits of length n. 4 | 5 | For example, for the input `'0123456789'`, the largest product for a 6 | series of 3 digits is 504 (7 * 8 * 9), and the largest product for a 7 | series of 5 digits is 15120 (5 * 6 * 7 * 8 * 9). 8 | 9 | For the input `'73167176531330624919225119674426574742355349194934'`, 10 | the largest product for a series of 6 digits is 23520. 11 | 12 | ## Setup 13 | 14 | Go through the setup instructions for JavaScript to 15 | install the necessary dependencies: 16 | 17 | http://help.exercism.io/getting-started-with-javascript.html 18 | 19 | ## Making the Test Suite Pass 20 | 21 | Execute the tests with: 22 | 23 | ```bash 24 | $ jasmine-node . 25 | ``` 26 | 27 | In many test suites all but the first test have been skipped. 28 | 29 | Once you get a test passing, you can unskip the next one by 30 | changing `xit` to `it`. 31 | 32 | 33 | ## Source 34 | 35 | A variation on Problem 8 at Project Euler [view source](http://projecteuler.net/problem=8) 36 | -------------------------------------------------------------------------------- /luhn/luhn.js: -------------------------------------------------------------------------------- 1 | var Luhn = function(code) { 2 | /* Validates and does Luhn code operations */ 3 | this.code = code; 4 | this.checkDigit = this.code % 10; 5 | this.addends = this._addends(); 6 | this.checksum = this._checksum(); 7 | this.valid = (this.checksum % 10 == 0); 8 | } 9 | 10 | Luhn.create = function(code) { 11 | /* Creates a valid Luhn code */ 12 | code *= 10; 13 | var luhn = new Luhn(code); 14 | return code + (10 - luhn.checksum % 10) % 10; 15 | }; 16 | 17 | Luhn.prototype._addends = function() { 18 | /* Doubles odd digits from the right 19 | Subtracts 9 if they are 10 or bigger */ 20 | return this.code 21 | .toString() 22 | .split('') 23 | .reverse() 24 | .map(function(e) {return parseInt(e)}) 25 | .map(function(digit, index) { 26 | if(index % 2 == 0) 27 | return digit 28 | else if( digit < 5 ) 29 | return 2 * digit; 30 | else 31 | return 2 * digit - 9; 32 | }) 33 | .reverse(); 34 | }; 35 | 36 | Luhn.prototype._checksum = function() { 37 | /* Digit sum of a Luhn encoded number */ 38 | return this 39 | ._addends() 40 | .reduce(function(total, item) { 41 | return total + item; 42 | }, 0); 43 | } 44 | 45 | module.exports = Luhn; -------------------------------------------------------------------------------- /trinary/trinary.spec.js: -------------------------------------------------------------------------------- 1 | var Trinary = require('./trinary'); 2 | 3 | describe('Trinary', function () { 4 | 5 | it('1 is decimal 1', function() { 6 | expect(1).toEqual(new Trinary('1').toDecimal()); 7 | }); 8 | 9 | it('2 is decimal 2', function() { 10 | expect(2).toEqual(new Trinary('2').toDecimal()); 11 | }); 12 | 13 | it('10 is decimal 3', function() { 14 | expect(3).toEqual(new Trinary('10').toDecimal()); 15 | }); 16 | 17 | it('11 is decimal 4', function() { 18 | expect(4).toEqual(new Trinary('11').toDecimal()); 19 | }); 20 | 21 | it('100 is decimal 9', function() { 22 | expect(9).toEqual(new Trinary('100').toDecimal()); 23 | }); 24 | 25 | it('112 is decimal 14', function() { 26 | expect(14).toEqual(new Trinary('112').toDecimal()); 27 | }); 28 | 29 | it('222 is 26', function() { 30 | expect(26).toEqual(new Trinary('222').toDecimal()); 31 | }); 32 | 33 | it('1122000120 is 32091', function() { 34 | expect(32091).toEqual(new Trinary('1122000120').toDecimal()); 35 | }); 36 | 37 | it('invalid trinary is decimal 0', function() { 38 | expect(0).toEqual(new Trinary('carrot').toDecimal()); 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /atbash-cipher/atbash-cipher.spec.js: -------------------------------------------------------------------------------- 1 | var atbash = require('./atbash-cipher'); 2 | 3 | describe('encode', function() { 4 | 5 | it('encodes no', function() { 6 | expect(atbash.encode('no')).toEqual('ml'); 7 | }); 8 | 9 | it('encodes yes', function() { 10 | expect(atbash.encode('yes')).toEqual('bvh'); 11 | }); 12 | 13 | it('encodes OMG', function() { 14 | expect(atbash.encode('OMG')).toEqual('lnt'); 15 | }); 16 | 17 | it('encodes O M G', function() { 18 | expect(atbash.encode('O M G')).toEqual('lnt'); 19 | }); 20 | 21 | it('encodes long words', function() { 22 | expect(atbash.encode('mindblowingly')).toEqual('nrmwy oldrm tob'); 23 | }); 24 | 25 | it('encodes numbers', function() { 26 | expect(atbash.encode('Testing, 1 2 3, testing.')) 27 | .toEqual('gvhgr mt123 gvhgr mt'); 28 | }); 29 | 30 | it('encodes sentences', function() { 31 | expect(atbash.encode('Truth is fiction.')).toEqual('gifgs rhurx grlm'); 32 | }); 33 | 34 | it('encodes all the things', function() { 35 | expect(atbash.encode('The quick brown fox jumps over the lazy dog.')) 36 | .toEqual('gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt'); 37 | }); 38 | 39 | }); 40 | -------------------------------------------------------------------------------- /collatz-conjecture/collatz-conjecture.spec.js: -------------------------------------------------------------------------------- 1 | var CollatzConjecture = require('./collatz-conjecture'); 2 | 3 | describe('CollatzConjecture', function () { 4 | var collatz = new CollatzConjecture(); 5 | 6 | it('test zero steps for one', function () { 7 | var expected = 0; 8 | expect(collatz.steps(1)).toEqual(expected); 9 | }); 10 | 11 | it('test divide if even steps', function () { 12 | var expected = 4; 13 | expect(collatz.steps(16)).toEqual(expected); 14 | }); 15 | 16 | it('test even and odd steps', function () { 17 | var expected = 9; 18 | expect(collatz.steps(12)).toEqual(expected); 19 | }); 20 | 21 | it('test large number of even and odd steps', function () { 22 | var expected = 152; 23 | expect(collatz.steps(1000000)).toEqual(expected); 24 | }); 25 | 26 | it('test zero is an error', function () { 27 | expect(function () { 28 | collatz.steps(0); 29 | }).toThrow(new Error('Only positive numbers are allowed')); 30 | }); 31 | 32 | it('test negative value is an error', function () { 33 | expect(function () { 34 | collatz.steps(-1); 35 | }).toThrow(new Error('Only positive numbers are allowed')); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /clock/clock.js: -------------------------------------------------------------------------------- 1 | var Clock = function (hours, minutes) { 2 | // Keeps track of clock time 3 | minutes = minutes || 0; 4 | this.minutes = minutes % 60 + (minutes < 0 ? +60 : 0); 5 | this.hours = (hours + Math.floor(minutes / 60)) % 24; 6 | this.hours += this.hours < 0 ? +24 : 0; 7 | }; 8 | 9 | Clock.at = function(hour, minutes) { 10 | /* Creates a clock at a time */ 11 | return new Clock(hour, minutes); 12 | }; 13 | 14 | Clock.prototype.minus = function(minutes) { 15 | /* Adds time */ 16 | return new Clock(this.hours, this.minutes - minutes) 17 | }; 18 | 19 | Clock.prototype.plus = function(minutes) { 20 | /* Subtracts time */ 21 | return new Clock(this.hours, this.minutes + minutes) 22 | }; 23 | 24 | Clock.prototype.equals = function(other) { 25 | /* Compares two clocks */ 26 | // Could also compare using the toString method 27 | return this.hours == other.hours && 28 | this.minutes == other.minutes; 29 | }; 30 | 31 | Clock.prototype.toString = function() { 32 | /* Standard 24h clock display */ 33 | // Zero padded 34 | var hours = ("00" + this.hours).slice(-2); 35 | var minutes = ("00" + this.minutes).slice(-2); 36 | return hours + ":" + minutes; 37 | }; 38 | 39 | module.exports = Clock; -------------------------------------------------------------------------------- /bracket-push/bracket-push.spec.js: -------------------------------------------------------------------------------- 1 | var bracket = require('./bracket-push'); 2 | 3 | describe('bracket push', function() { 4 | it('checks for appropriate bracketing in a set of brackets', function() { 5 | expect(bracket('{}')).toEqual(true); 6 | }); 7 | 8 | it('returns false for unclosed brackets', function() { 9 | expect(bracket('{{')).toEqual(false); 10 | }); 11 | 12 | it('returns false if brackets are out of order', function() { 13 | expect(bracket('}{')).toEqual(false); 14 | }); 15 | 16 | it('checks bracketing in more than one pair of brackets', function() { 17 | expect(bracket('{}[]')).toEqual(true); 18 | }); 19 | 20 | it('checks bracketing in nested brackets', function() { 21 | expect(bracket('{[]}')).toEqual(true); 22 | }); 23 | 24 | it('rejects brackets that are properly balanced but improperly nested', function() { 25 | expect(bracket('{[}]')).toEqual(false); 26 | }); 27 | 28 | it('checks bracket closure with deeper nesting', function() { 29 | expect(bracket('{[)][]}')).toEqual(false); 30 | }); 31 | 32 | it('checks bracket closure in a long string of brackets', function() { 33 | expect(bracket('{[]([()])}')).toEqual(true); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /rna-transcription/README.md: -------------------------------------------------------------------------------- 1 | # Rna Transcription 2 | 3 | Write a program that, given a DNA strand, returns its RNA complement (per RNA transcription). 4 | 5 | Both DNA and RNA strands are a sequence of nucleotides. 6 | 7 | The four nucleotides found in DNA are adenine (**A**), cytosine (**C**), 8 | guanine (**G**) and thymidine (**T**). 9 | 10 | The four nucleotides found in RNA are adenine (**A**), cytosine (**C**), 11 | guanine (**G**) and uracil (**U**). 12 | 13 | Given a DNA strand, its transcribed RNA strand is formed by replacing 14 | each nucleotide with its complement: 15 | 16 | * `G` -> `C` 17 | * `C` -> `G` 18 | * `T` -> `A` 19 | * `A` -> `U` 20 | 21 | ## Setup 22 | 23 | Go through the setup instructions for JavaScript to 24 | install the necessary dependencies: 25 | 26 | http://help.exercism.io/getting-started-with-javascript.html 27 | 28 | ## Making the Test Suite Pass 29 | 30 | Execute the tests with: 31 | 32 | ```bash 33 | $ jasmine-node . 34 | ``` 35 | 36 | In many test suites all but the first test have been skipped. 37 | 38 | Once you get a test passing, you can unskip the next one by 39 | changing `xit` to `it`. 40 | 41 | 42 | ## Source 43 | 44 | Rosalind [view source](http://rosalind.info/problems/rna) 45 | -------------------------------------------------------------------------------- /minesweeper/minesweeper.js: -------------------------------------------------------------------------------- 1 | class Minesweeper { 2 | static countMines(field, row, col) { 3 | const diffs = [ 4 | [row+1,col+1], [row+1,col], [row+1,col-1], 5 | [row ,col+1], [row ,col-1], 6 | [row-1,col+1], [row-1,col], [row-1,col-1]]; 7 | var total = 0; 8 | for(var d = 0; d < diffs.length; d++) { 9 | if(0 <= diffs[d][0] && diffs[d][0] < field.length && 10 | 0 <= diffs[d][1] && diffs[d][1] < field[diffs[d][0]].length && 11 | field[diffs[d][0]][diffs[d][1]] == '*') { 12 | total++; 13 | } 14 | } 15 | return total; 16 | } 17 | 18 | annotate(field) { 19 | field = field.map(row => row.split('')); 20 | var mines; 21 | for(var r = 0; r < field.length; r++) { 22 | for(var c = 0; c < field[r].length; c++) { 23 | if(field[r][c] == ' ') { 24 | mines = Minesweeper.countMines(field, r, c); 25 | field[r][c] = mines == 0 ? ' ' : mines; 26 | } 27 | } 28 | } 29 | return field.map(row => row.join('')); 30 | } 31 | } 32 | 33 | module.exports = Minesweeper 34 | -------------------------------------------------------------------------------- /meetup/README.md: -------------------------------------------------------------------------------- 1 | # Meetup 2 | 3 | Calculate the date of meetups. 4 | 5 | Typically meetups happen on the same day of the week. 6 | 7 | Examples are 8 | 9 | - the first Monday 10 | - the third Tuesday 11 | - the Wednesteenth 12 | - the last Thursday 13 | 14 | Note that "Monteenth", "Tuesteenth", etc are all made up words. There 15 | was a meetup whose members realised that there are exactly 7 days that 16 | end in '-teenth'. Therefore, one is guaranteed that each day of the week 17 | (Monday, Tuesday, ...) will have exactly one date that is named with '-teenth' 18 | in every month. 19 | 20 | ## Setup 21 | 22 | Go through the setup instructions for JavaScript to 23 | install the necessary dependencies: 24 | 25 | http://help.exercism.io/getting-started-with-javascript.html 26 | 27 | ## Making the Test Suite Pass 28 | 29 | Execute the tests with: 30 | 31 | ```bash 32 | $ jasmine-node . 33 | ``` 34 | 35 | In many test suites all but the first test have been skipped. 36 | 37 | Once you get a test passing, you can unskip the next one by 38 | changing `xit` to `it`. 39 | 40 | 41 | ## Source 42 | 43 | Jeremy Hinegardner mentioned a Boulder meetup that happens on the Wednesteenth of every month [view source](https://twitter.com/copiousfreetime) 44 | -------------------------------------------------------------------------------- /spiral-matrix/spiral-matrix.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ofSize(limit) { 3 | const bound = { 4 | row: { upper: limit - 1, lower: 1 }, 5 | col: { upper: limit - 1, lower: 0 } }; 6 | 7 | const result = new Array(limit).fill(null).map(() => new Array(limit)); 8 | 9 | let position = { row: 0, col: 0 }; 10 | let diff = { row: 0, col: 1 }; 11 | 12 | for (let n = 1; n <= limit * limit; n += 1) { 13 | if (position.row >= result.length) { result.push([]); } 14 | 15 | result[position.row][position.col] = n; 16 | 17 | if (diff.col === 1 && position.col >= bound.col.upper) { 18 | bound.col.upper -= 1; 19 | diff = { row: 1, col: 0 }; 20 | } else if (diff.row === 1 && position.row >= bound.row.upper) { 21 | bound.row.upper -= 1; 22 | diff = { row: 0, col: -1 }; 23 | } else if (diff.col === -1 && position.col <= bound.col.lower) { 24 | bound.col.lower += 1; 25 | diff = { row: -1, col: 0 }; 26 | } else if (diff.row === -1 && position.row <= bound.row.lower) { 27 | bound.row.lower += 1; 28 | diff = { row: 0, col: 1 }; 29 | } 30 | position = { row: position.row + diff.row, col: position.col + diff.col }; 31 | } 32 | return result; 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /gigasecond/gigasecond.spec.js: -------------------------------------------------------------------------------- 1 | var Gigasecond = require('./gigasecond'); 2 | 3 | describe('Gigasecond', function() { 4 | 5 | it('tells a gigasecond anniversary since midnight', function() { 6 | var gs = new Gigasecond(new Date(Date.UTC(2015, 8, 14))); 7 | var expectedDate = new Date(Date.UTC(2047, 4, 23, 1, 46, 40)); 8 | expect(gs.date()).toEqual(expectedDate); 9 | }); 10 | 11 | it('tells the anniversary is next day when you are born at night', function() { 12 | var gs = new Gigasecond(new Date(Date.UTC(2015, 8, 14, 23, 59, 59))); 13 | var expectedDate = new Date(Date.UTC(2047, 4, 24, 1, 46, 39)); 14 | expect(gs.date()).toEqual(expectedDate); 15 | }); 16 | 17 | it('even works before 1970 (beginning of Unix epoch)', function() { 18 | var gs = new Gigasecond(new Date(Date.UTC(1959, 6, 19, 5, 13, 45))); 19 | var expectedDate = new Date(Date.UTC(1991, 2, 27, 7, 0, 25)); 20 | expect(gs.date()).toEqual(expectedDate); 21 | }); 22 | 23 | it('make sure calling "date" doesn\'t mutate value', function() { 24 | var gs = new Gigasecond(new Date(Date.UTC(1959, 6, 19, 5, 13, 45))); 25 | var expectedDate = new Date(Date.UTC(1991, 2, 27, 7, 0, 25)); 26 | gs.date(); 27 | expect(gs.date()).toEqual(expectedDate); 28 | }); 29 | }); 30 | 31 | 32 | -------------------------------------------------------------------------------- /reverse-string/README.md: -------------------------------------------------------------------------------- 1 | # Reverse String 2 | 3 | Reverse a string 4 | 5 | For example: 6 | input: "cool" 7 | output: "looc" 8 | 9 | ## Setup 10 | 11 | Go through the setup instructions for JavaScript to install the 12 | necessary dependencies: 13 | 14 | http://exercism.io/languages/javascript/installation 15 | 16 | ## Running the test suite 17 | 18 | The provided test suite uses [Jasmine](https://jasmine.github.io/). 19 | You can install it by opening a terminal window and running the 20 | following command: 21 | 22 | ```sh 23 | npm install -g jasmine 24 | ``` 25 | 26 | Run the test suite from the exercise directory with: 27 | 28 | ```sh 29 | jasmine reverse-string.spec.js 30 | ``` 31 | 32 | In many test suites all but the first test have been marked "pending". 33 | Once you get a test passing, activate the next one by changing `xit` to `it`. 34 | 35 | ## Source 36 | 37 | Introductory challenge to reverse an input string [https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb](https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb) 38 | 39 | ## Submitting Incomplete Solutions 40 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 41 | -------------------------------------------------------------------------------- /sieve/sieve.spec.js: -------------------------------------------------------------------------------- 1 | var Sieve = require('./sieve'); 2 | 3 | describe('Sieve', function() { 4 | 5 | it('finds primes up to 10', function() { 6 | expect(new Sieve(10).primes).toEqual([2, 3, 5, 7]); 7 | }); 8 | 9 | it('finds primes up to 13, and considers the limit passed in', function() { 10 | expect(new Sieve(13).primes).toEqual([2, 3, 5, 7, 11, 13]); 11 | }); 12 | 13 | it('finds primes up to 1000', function() { 14 | expect(new Sieve(1000).primes).toEqual([2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]); 15 | }); 16 | 17 | }); 18 | -------------------------------------------------------------------------------- /octal/octal.spec.js: -------------------------------------------------------------------------------- 1 | var Octal = require('./octal'); 2 | 3 | describe('octal', function() { 4 | 5 | it('1 is decimal 1', function() { 6 | expect(new Octal('1').toDecimal()).toEqual(1); 7 | }); 8 | 9 | it('10 is decimal 8', function() { 10 | expect(new Octal('10').toDecimal()).toEqual(8); 11 | }); 12 | 13 | it('17 is decimal 15', function() { 14 | expect(new Octal('17').toDecimal()).toEqual(15); 15 | }); 16 | 17 | it('11 is decimal 9', function() { 18 | expect(new Octal('11').toDecimal()).toEqual(9); 19 | }); 20 | 21 | it('130 is decimal 88', function() { 22 | expect(new Octal('130').toDecimal()).toEqual(88); 23 | }); 24 | 25 | it('2047 is decimal 1063', function() { 26 | expect(new Octal('2047').toDecimal()).toEqual(1063); 27 | }); 28 | 29 | it('7777 is decimal 4095', function() { 30 | expect(new Octal('7777').toDecimal()).toEqual(4095); 31 | }); 32 | 33 | it('1234567 is decimal 342391', function() { 34 | expect(new Octal('1234567').toDecimal()).toEqual(342391); 35 | }); 36 | 37 | it('invalid is decimal 0', function() { 38 | expect(new Octal('carrot').toDecimal()).toEqual(0); 39 | }); 40 | 41 | it('considers the digit 8 as invalid', function() { 42 | expect(new Octal('12345678').toDecimal()).toEqual(0); 43 | }); 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /list-ops/list-ops.js: -------------------------------------------------------------------------------- 1 | class List { 2 | constructor(values=[]) { 3 | this.values = values; 4 | this.push = function(value) { this.values.push(value); return this }; 5 | }; 6 | 7 | append(list) { 8 | Array.prototype.push.apply(this.values, list.values); 9 | return this; 10 | }; 11 | 12 | concat(list) { return this.append(list); }; 13 | 14 | filter(func) { 15 | return this.foldl( 16 | function(val, acc) { return func(val) ? acc.push(val) : acc }, 17 | new List()); 18 | }; 19 | 20 | map(func) { 21 | return this.foldl( 22 | function(val, acc) { return acc.push(func(val)); }, 23 | new List()); 24 | } 25 | 26 | foldl(func, acc) { 27 | for(var i = 0; i < this.values.length; i++) { 28 | acc = func(this.values[i], acc); 29 | } 30 | return acc; 31 | } 32 | 33 | foldr(func, acc) { 34 | for(var i = this.values.length-1; i >= 0; i--) { 35 | acc = func(this.values[i], acc); 36 | } 37 | return acc; 38 | } 39 | 40 | reverse() { 41 | return this.foldr( function(val, acc) { return acc.push(val) }, new List()); 42 | } 43 | 44 | length() { return this.values.length; } 45 | }; 46 | 47 | module.exports = List; 48 | -------------------------------------------------------------------------------- /phone-number/phone-number.spec.js: -------------------------------------------------------------------------------- 1 | var PhoneNumber = require('./phone-number'); 2 | 3 | describe('PhoneNumber()', function() { 4 | it('cleans the number (123) 456-7890', function() { 5 | var phone = new PhoneNumber('(123) 456-7890'); 6 | expect(phone.number()).toEqual('1234567890'); 7 | }); 8 | 9 | it('cleans numbers with dots', function() { 10 | var phone = new PhoneNumber('123.456.7890'); 11 | expect(phone.number()).toEqual('1234567890'); 12 | }); 13 | 14 | it('valid when 11 digits and first digit is 1', function() { 15 | var phone = new PhoneNumber('11234567890'); 16 | expect(phone.number()).toEqual('1234567890'); 17 | }); 18 | 19 | it('invalid when 11 digits', function() { 20 | var phone = new PhoneNumber('21234567890'); 21 | expect(phone.number()).toEqual('0000000000'); 22 | }); 23 | 24 | it('invalid when 9 digits', function() { 25 | var phone = new PhoneNumber('123456789'); 26 | expect(phone.number()).toEqual('0000000000'); 27 | }); 28 | 29 | it('has an area code', function() { 30 | var phone = new PhoneNumber('1234567890'); 31 | expect(phone.areaCode()).toEqual('123'); 32 | }); 33 | 34 | it('formats a number', function() { 35 | var phone = new PhoneNumber('1234567890'); 36 | expect(phone.toString()).toEqual('(123) 456-7890'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /run-length-encoding/run-length-encoding.spec.js: -------------------------------------------------------------------------------- 1 | var RLE = require('./run-length-encoding'); 2 | 3 | describe('Run-length encoding', function () { 4 | it('encode empty string', function () { 5 | expect(RLE.encode('')).toEqual(''); 6 | }); 7 | 8 | it('encode single characters only', function () { 9 | expect(RLE.encode('XYZ')).toEqual('XYZ'); 10 | }); 11 | 12 | it('decode empty string', function () { 13 | expect(RLE.decode('')).toEqual(''); 14 | }); 15 | 16 | it('decode single characters only', function () { 17 | expect(RLE.decode('XYZ')).toEqual('XYZ'); 18 | }); 19 | 20 | it('encode simple', function () { 21 | expect(RLE.encode('AABBBCCCC')).toEqual('2A3B4C'); 22 | }); 23 | 24 | it('decode simple', function () { 25 | expect(RLE.decode('2A3B4C')).toEqual('AABBBCCCC'); 26 | }); 27 | 28 | it('encode with single values', function () { 29 | expect(RLE.encode('WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB')).toEqual('12WB12W3B24WB'); 30 | }); 31 | 32 | it('decode with single values', function () { 33 | expect(RLE.decode('12WB12W3B24WB')).toEqual('WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB'); 34 | }); 35 | 36 | it('decode(encode(...))combination', function () { 37 | expect(RLE.decode(RLE.encode('zzz ZZ zZ'))).toEqual('zzz ZZ zZ'); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /flatten-array/flatten-array.spec.js: -------------------------------------------------------------------------------- 1 | var Flattener = require('./flatten-array.js'); 2 | 3 | describe('FlattenArray', function () { 4 | var flattener = new Flattener(); 5 | it('flattens a nested list', function () { 6 | expect(flattener.flatten([[]])).toEqual([]); 7 | }); 8 | it('flattens a 2 level nested list', function () { 9 | expect(flattener.flatten([1, [2, 3, 4], 5])).toEqual([1, 2, 3, 4, 5]); 10 | }); 11 | it('flattens a 3 level nested list', function () { 12 | expect(flattener.flatten([1, [2, 3, 4], 5, [6, [7, 8]]])).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); 13 | }); 14 | it('flattens a 5 level nested list', function () { 15 | expect(flattener.flatten([0, 2, [[2, 3], 8, 100, 4, [[[50]]]], -2])).toEqual([0, 2, 2, 3, 8, 100, 4, 50, -2]); 16 | }); 17 | it('flattens a 6 level nest list', function () { 18 | expect(flattener.flatten([1, [2, [[3]], [4, [[5]]], 6, 7], 8])).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); 19 | }); 20 | it('flattens a 6 level nest list with null values', function () { 21 | expect(flattener.flatten([0, 2, [[2, 3], 8, [[100]], null, [[null]]], -2])).toEqual([0, 2, 2, 3, 8, 100, -2]); 22 | }); 23 | it('returns an empty list if all values in nested list are null', function () { 24 | expect(flattener.flatten([null, [[[null]]], null, null, [[null, null], null], null])).toEqual([]); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /hamming/hamming.spec.js: -------------------------------------------------------------------------------- 1 | var Hamming = require('./hamming'); 2 | 3 | describe('Hamming', function () { 4 | var hamming = new Hamming(); 5 | 6 | it('no difference between identical strands', function () { 7 | expect(hamming.compute('A', 'A')).toEqual(0); 8 | }); 9 | 10 | it('complete hamming distance for single nucleotide strand', function () { 11 | expect(hamming.compute('A','G')).toEqual(1); 12 | }); 13 | 14 | it('complete hamming distance for small strand', function () { 15 | expect(hamming.compute('AG','CT')).toEqual(2); 16 | }); 17 | 18 | it('small hamming distance', function () { 19 | expect(hamming.compute('AT','CT')).toEqual(1); 20 | }); 21 | 22 | it('small hamming distance in longer strand', function () { 23 | expect(hamming.compute('GGACG', 'GGTCG')).toEqual(1); 24 | }); 25 | 26 | it('large hamming distance', function () { 27 | expect(hamming.compute('GATACA', 'GCATAA')).toEqual(4); 28 | }); 29 | 30 | it('hamming distance in very long strand', function () { 31 | expect(hamming.compute('GGACGGATTCTG', 'AGGACGGATTCT')).toEqual(9); 32 | }); 33 | 34 | it('throws error when strands are not equal length', function() { 35 | expect(function() { hamming.compute('GGACGGATTCTG', 'AGGAC'); }).toThrow( 36 | new Error('DNA strands must be of equal length.') 37 | ); 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /prime-factors/prime-factors.spec.js: -------------------------------------------------------------------------------- 1 | var primeFactors = require('./prime-factors'); 2 | 3 | describe('primeFactors', function() { 4 | 5 | it('returns an empty array for 1', function() { 6 | expect(primeFactors.for(1)).toEqual([]); 7 | }); 8 | 9 | it('factors 2', function() { 10 | expect(primeFactors.for(2)).toEqual([2]); 11 | }); 12 | 13 | it('factors 3', function() { 14 | expect(primeFactors.for(3)).toEqual([3]); 15 | }); 16 | 17 | it('factors 4', function() { 18 | expect(primeFactors.for(4)).toEqual([2, 2]); 19 | }); 20 | 21 | it('factors 6', function() { 22 | expect(primeFactors.for(6)).toEqual([2, 3]); 23 | }); 24 | 25 | it('factors 8', function() { 26 | expect(primeFactors.for(8)).toEqual([2, 2, 2]); 27 | }); 28 | 29 | it('factors 9', function() { 30 | expect(primeFactors.for(9)).toEqual([3, 3]); 31 | }); 32 | 33 | it('factors 27', function() { 34 | expect(primeFactors.for(27)).toEqual([3, 3, 3]); 35 | }); 36 | 37 | it('factors 625', function() { 38 | expect(primeFactors.for(625)).toEqual([5, 5, 5, 5]); 39 | }); 40 | 41 | it('factors 901255', function() { 42 | expect(primeFactors.for(901255)).toEqual([5, 17, 23, 461]); 43 | }); 44 | 45 | it('factors 93819012551', function() { 46 | expect(primeFactors.for(93819012551)).toEqual([11, 9539, 894119]); 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /wordy/wordy.js: -------------------------------------------------------------------------------- 1 | var WordProblem = function(text) { 2 | /* Solves mathmatical word problems */ 3 | this.text = text; 4 | this.operators = { 5 | "plus": '+', 6 | "minus": '-', 7 | "multiplied by": "*", 8 | "divided by": "/", 9 | }; 10 | this.form = new RegExp("^What is (-?\\d+(?: (?:" + Object.keys(this.operators).join("|") + ") -?\\d+)+)\\?$"); 11 | } 12 | 13 | WordProblem.prototype.answer = function() { 14 | /* Answers a mathmatical word problem */ 15 | if(!this.text.match(this.form)) 16 | throw new ArgumentError(); 17 | var tokens = this.tokenize(); 18 | return this.process(tokens); 19 | }; 20 | 21 | WordProblem.prototype.tokenize = function() { 22 | /* Splits the word problem into javascript tokens */ 23 | var text = this.text.match(this.form)[1]; 24 | for(var operator in this.operators) 25 | text = text.replace(new RegExp(operator, "g"), this.operators[operator]); 26 | return text.split(" "); 27 | }; 28 | 29 | WordProblem.prototype.process = function(tokens) { 30 | /* Evaluates the tokens in sequential order */ 31 | var total = tokens.shift(); 32 | while(tokens.length > 0) 33 | total = eval(total + " " + tokens.shift() + " " + tokens.shift()); 34 | return total; 35 | }; 36 | 37 | // An error 38 | var ArgumentError = function() {} 39 | 40 | module.exports = { 41 | WordProblem: WordProblem, 42 | ArgumentError: ArgumentError, 43 | } 44 | -------------------------------------------------------------------------------- /sublist/sublist.js: -------------------------------------------------------------------------------- 1 | class List { 2 | constructor(elements) { 3 | this.elements = elements || []; 4 | } 5 | 6 | compare(list) { 7 | var swap = false; 8 | var match = false; 9 | var elementsMatch; 10 | 11 | var listA = this.elements; 12 | var listB = list.elements; 13 | 14 | if(listB.length > listA.length) { 15 | swap = true; 16 | var listA = list.elements; 17 | var listB = this.elements; 18 | } 19 | 20 | for(var i = 0; i <= listA.length - listB.length && !match; i++) { 21 | var elementsMatch = true; 22 | for(var j = 0; j < listB.length; j++) { 23 | if(listA[i + j] != listB[j]) { 24 | elementsMatch = false; 25 | break; 26 | } 27 | } 28 | if(elementsMatch) { 29 | match = true; 30 | break; 31 | } 32 | } 33 | 34 | if(match || listB.length == 0) { 35 | if(listB.length == listA.length) { 36 | return "EQUAL"; 37 | } else if(swap) { 38 | return "SUBLIST"; 39 | } else { 40 | return "SUPERLIST"; 41 | } 42 | } 43 | return "UNEQUAL"; 44 | } 45 | } 46 | 47 | module.exports = List; 48 | -------------------------------------------------------------------------------- /word-search/README.md: -------------------------------------------------------------------------------- 1 | # Word Search 2 | 3 | In word search puzzles you get a square of letters and have to find specific 4 | words in them. 5 | 6 | For example: 7 | 8 | ```text 9 | jefblpepre 10 | camdcimgtc 11 | oivokprjsm 12 | pbwasqroua 13 | rixilelhrs 14 | wolcqlirpc 15 | screeaumgr 16 | alxhpburyi 17 | jalaycalmp 18 | clojurermt 19 | ``` 20 | 21 | There are several programming languages hidden in the above square. 22 | 23 | Words can be hidden in all kinds of directions: left-to-right, right-to-left, 24 | vertical and diagonal. 25 | 26 | Given a puzzle and a list of words return the location of the first and last 27 | letter of each word. 28 | 29 | ## Setup 30 | 31 | Go through the setup instructions for ECMAScript to 32 | install the necessary dependencies: 33 | 34 | http://exercism.io/languages/ecmascript 35 | 36 | ## Requirements 37 | 38 | Install assignment dependencies: 39 | 40 | ```bash 41 | $ npm install 42 | ``` 43 | 44 | ## Making the test suite pass 45 | 46 | Execute the tests with: 47 | 48 | ```bash 49 | $ npm test 50 | ``` 51 | 52 | In the test suites all tests but the first have been skipped. 53 | 54 | Once you get a test passing, you can enable the next one by 55 | changing `xtest` to `test`. 56 | 57 | 58 | ## Submitting Incomplete Solutions 59 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 60 | -------------------------------------------------------------------------------- /beer-song/beer-song.js: -------------------------------------------------------------------------------- 1 | var BeerSong = function() {} 2 | 3 | 4 | BeerSong.prototype.verse = function( verseNum ) { 5 | /* Sings a verse of "bottles of beer on the wall" */ 6 | // Special cases for verses that include 0 (no more) and 1 (bottle[s]) 7 | var specialLines = { 8 | 0: "No more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n", 9 | 1: "1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n", 10 | 2: "2 bottles of beer on the wall, 2 bottles of beer.\nTake one down and pass it around, 1 bottle of beer on the wall.\n", 11 | }; 12 | // A normal verse 13 | var normalLine = "%d bottles of beer on the wall, %d bottles of beer.\nTake one down and pass it around, %d bottles of beer on the wall.\n"; 14 | var sprintf = require('util').format; 15 | return verseNum in specialLines ? specialLines[verseNum] : sprintf(normalLine, verseNum, verseNum, verseNum - 1); 16 | }; 17 | 18 | BeerSong.prototype.sing = function( start, stop) { 19 | /* Sings a set of verses of "Bottle of beer on the wall" */ 20 | // Default 21 | stop || (stop = 0); 22 | var song = []; 23 | for(var verseNum = start; verseNum >= stop; verseNum--) { 24 | song.push(this.verse(verseNum)); 25 | } 26 | return song.join("\n"); 27 | }; 28 | 29 | 30 | module.exports = BeerSong; 31 | -------------------------------------------------------------------------------- /two-bucket/two-bucket.js: -------------------------------------------------------------------------------- 1 | var TwoBucket = function(sizeOne, sizeTwo, goal, starterBucket) { 2 | /* Runs the two bucket problem */ 3 | this.goalBucketSize = (starterBucket == "one" ? sizeOne : sizeTwo); 4 | this.otherBucketSize = (starterBucket == "one" ? sizeTwo : sizeOne); 5 | this.goalBucket = starterBucket; 6 | this.goal = goal; 7 | } 8 | 9 | TwoBucket.prototype.moves = function() { 10 | /* Fills buckets until one contains a certain amount of water */ 11 | this.waterInGoal = 0; 12 | this.otherBucket = 0; 13 | for(var moves = 0; this.goal != this.waterInGoal; moves++) { 14 | // Fill an empty goal bucket 15 | if(this.waterInGoal == 0) 16 | this.waterInGoal = this.goalBucketSize; 17 | // Empty a full other bucket 18 | else if(this.otherBucket == this.otherBucketSize) 19 | this.otherBucket = 0; 20 | // Pour from the goal bucket into the other bucket 21 | else { 22 | // Pour all water from the goal bucket into the other bucket 23 | if(this.otherBucket + this.waterInGoal <= this.otherBucketSize) { 24 | this.otherBucket += this.waterInGoal; 25 | this.waterInGoal = 0; 26 | } 27 | // Pour from the goal bucket until the other bucket is full 28 | else { 29 | this.waterInGoal -= this.otherBucketSize - this.otherBucket; 30 | this.otherBucket = this.otherBucketSize; 31 | } 32 | } 33 | } 34 | return moves; 35 | }; 36 | 37 | module.exports = TwoBucket; -------------------------------------------------------------------------------- /crypto-square/crypto-square.js: -------------------------------------------------------------------------------- 1 | var Crypto = function(plainText) { 2 | /* Encrypts message using the crypto-square algorithm */ 3 | this.palinText = plainText; 4 | } 5 | 6 | Crypto.prototype.normalizePlaintext = function() { 7 | /* Removes all characters except letters and numbers */ 8 | return this.palinText 9 | .toLowerCase() 10 | .split('') 11 | .filter(function(e) { return e.match(/[a-z0-9]/) }) 12 | .join(''); 13 | }; 14 | 15 | Crypto.prototype.size = function() { 16 | /* Size of the square */ 17 | return Math.ceil(Math.sqrt(this.normalizePlaintext().length)); 18 | }; 19 | 20 | Crypto.prototype.plaintextSegments = function() { 21 | /* Splits clear text into groups array*/ 22 | var text = this.normalizePlaintext(); 23 | return text.match(new RegExp(".{1," + this.size() + "}", "g")); 24 | }; 25 | 26 | Crypto.prototype.ciphertext = function() { 27 | /* Encrypts the text by selecting from column then row */ 28 | var text_blocks = this.plaintextSegments(); 29 | var cipher_text = ''; 30 | for(var i = 0; i < this.size(); i++) 31 | for(var j in text_blocks) 32 | cipher_text += text_blocks[j].charAt(i); 33 | return cipher_text; 34 | }; 35 | 36 | Crypto.prototype.normalizeCiphertext = function() { 37 | /* Splits cipher text into groups */ 38 | return this.ciphertext() 39 | .match(new RegExp(".{1," + this.size() + "}", "g")) 40 | .join(' '); 41 | }; 42 | 43 | module.exports = Crypto; 44 | -------------------------------------------------------------------------------- /leap/README.md: -------------------------------------------------------------------------------- 1 | # Leap 2 | 3 | Write a program that will take a year and report if it is a leap year. 4 | 5 | The tricky thing here is that a leap year occurs: 6 | 7 | ```plain 8 | on every year that is evenly divisible by 4 9 | except every year that is evenly divisible by 100 10 | unless the year is also evenly divisible by 400 11 | ``` 12 | 13 | For example, 1997 is not a leap year, but 1996 is. 1900 is not a leap 14 | year, but 2000 is. 15 | 16 | If your language provides a method in the standard library that does 17 | this look-up, pretend it doesn't exist and implement it yourself. 18 | 19 | ## Notes 20 | 21 | For a delightful, four minute explanation of the whole leap year 22 | phenomenon, go watch [this youtube video][video]. 23 | 24 | [video]: http://www.youtube.com/watch?v=xX96xng7sAE 25 | 26 | ## Setup 27 | 28 | Go through the setup instructions for JavaScript to 29 | install the necessary dependencies: 30 | 31 | http://help.exercism.io/getting-started-with-javascript.html 32 | 33 | ## Making the Test Suite Pass 34 | 35 | Execute the tests with: 36 | 37 | ```bash 38 | $ jasmine-node . 39 | ``` 40 | 41 | In many test suites all but the first test have been skipped. 42 | 43 | Once you get a test passing, you can unskip the next one by 44 | changing `xit` to `it`. 45 | 46 | 47 | ## Source 48 | 49 | JavaRanch Cattle Drive, exercise 3 [view source](http://www.javaranch.com/leap.jsp) 50 | -------------------------------------------------------------------------------- /accumulate/README.md: -------------------------------------------------------------------------------- 1 | # Accumulate 2 | 3 | Implement the `accumulate` operation, which, given a collection and an operation to perform on each element of the collection, returns a new collection containing the result of applying that operation to each element of the input collection. 4 | 5 | For example, given the collection of numbers: 6 | 7 | - 1, 2, 3, 4, 5 8 | 9 | And the operation: 10 | 11 | - square a number 12 | 13 | Your code should be able to produce the collection of squares: 14 | 15 | - 1, 4, 9, 16, 25 16 | 17 | ## Restrictions 18 | 19 | Keep your hands off that collect/map/fmap/whatchamacallit functionality 20 | provided by your standard library! 21 | Solve this one yourself using other basic tools instead. 22 | 23 | Elixir specific: it's perfectly fine to use `Enum.reduce` or 24 | `Enumerable.reduce`. 25 | 26 | ## Setup 27 | 28 | Go through the setup instructions for JavaScript to 29 | install the necessary dependencies: 30 | 31 | http://help.exercism.io/getting-started-with-javascript.html 32 | 33 | ## Making the Test Suite Pass 34 | 35 | Execute the tests with: 36 | 37 | ```bash 38 | $ jasmine-node . 39 | ``` 40 | 41 | In many test suites all but the first test have been skipped. 42 | 43 | Once you get a test passing, you can unskip the next one by 44 | changing `xit` to `it`. 45 | 46 | 47 | ## Source 48 | 49 | Conversation with James Edward Gray II [view source](https://twitter.com/jeg2) 50 | -------------------------------------------------------------------------------- /house/house.js: -------------------------------------------------------------------------------- 1 | const items = [ 2 | { animal: 'house that Jack built.' }, 3 | { animal: 'malt', adjective: 'lay in' }, 4 | { animal: 'rat', adjective: 'ate' }, 5 | { animal: 'cat', adjective: 'killed' }, 6 | { animal: 'dog', adjective: 'worried' }, 7 | { animal: 'cow with the crumpled horn', adjective: 'tossed' }, 8 | { animal: 'maiden all forlorn', adjective: 'milked' }, 9 | { animal: 'man all tattered and torn', adjective: 'kissed' }, 10 | { animal: 'priest all shaven and shorn', adjective: 'married' }, 11 | { animal: 'rooster that crowed in the morn', adjective: 'woke' }, 12 | { animal: 'farmer sowing his corn', adjective: 'kept' }, 13 | { animal: 'horse and the hound and the horn', adjective: 'belonged to' }, 14 | ]; 15 | 16 | function verse(ver) { 17 | if (ver === 1) { 18 | return ['This is the house that Jack built.']; 19 | } 20 | const result = [`This is the ${items[ver - 1].animal}`]; 21 | for (let v = ver - 1; v > 0; v -= 1) { 22 | // console.log(result); 23 | result.push(`that ${items[v].adjective} the ${items[v - 1].animal}`); 24 | } 25 | return result; 26 | } 27 | 28 | function verses(start, end) { 29 | const result = []; 30 | for (let v = start; v <= end; v += 1) { 31 | result.push(...verse(v)); 32 | result.push(''); 33 | } 34 | result.pop(); 35 | return result; 36 | } 37 | 38 | module.exports = { 39 | verse: verse, 40 | verses: verses 41 | } 42 | -------------------------------------------------------------------------------- /complex-numbers/complex-numbers.js: -------------------------------------------------------------------------------- 1 | class ComplexNumber { 2 | constructor(real, imag) { 3 | this.real = real; 4 | this.imag = imag; 5 | } 6 | 7 | add(other) { 8 | return new ComplexNumber(this.real + other.real, this.imag + other.imag); 9 | } 10 | 11 | sub(other) { 12 | return new ComplexNumber(this.real - other.real, this.imag - other.imag); 13 | } 14 | 15 | mul(other) { 16 | return new ComplexNumber( 17 | this.real * other.real - this.imag * other.imag, 18 | this.real * other.imag + this.imag * other.real); 19 | } 20 | 21 | div(other) { 22 | var denom = other.real * other.real + other.imag * other.imag; 23 | var realNum = this.real * other.real + this.imag * other.imag; 24 | var imagNum = other.real * this.imag - other.imag * this.real; 25 | return new ComplexNumber(realNum / denom, imagNum / denom); 26 | } 27 | 28 | get abs() { 29 | return Math.pow(Math.pow(this.real, 2) + Math.pow(this.imag, 2), 0.5); 30 | } 31 | 32 | get conj() { 33 | return new ComplexNumber(this.real, this.imag == 0 ? 0 : -this.imag); 34 | } 35 | 36 | get exp() { 37 | return new ComplexNumber( 38 | Math.exp(this.real) * Math.cos(this.imag), 39 | Math.exp(this.real) * Math.sin(this.imag)); 40 | } 41 | } 42 | 43 | module.exports = ComplexNumber; 44 | -------------------------------------------------------------------------------- /isogram/README.md: -------------------------------------------------------------------------------- 1 | # Isogram 2 | 3 | Determine if a word or phrase is an isogram. 4 | 5 | An isogram (also known as a "nonpattern word") is a word or phrase without a repeating letter, however spaces and hyphens are allowed to appear multiple times. 6 | 7 | Examples of isograms: 8 | 9 | - lumberjacks 10 | - background 11 | - downstream 12 | - six-year-old 13 | 14 | The word *isograms*, however, is not an isogram, because the s repeats. 15 | 16 | ## Setup 17 | 18 | Go through the setup instructions for JavaScript to install the 19 | necessary dependencies: 20 | 21 | http://exercism.io/languages/javascript/installation 22 | 23 | ## Running the test suite 24 | 25 | The provided test suite uses [Jasmine](https://jasmine.github.io/). 26 | You can install it by opening a terminal window and running the 27 | following command: 28 | 29 | ```sh 30 | npm install -g jasmine 31 | ``` 32 | 33 | Run the test suite from the exercise directory with: 34 | 35 | ```sh 36 | jasmine isogram.spec.js 37 | ``` 38 | 39 | In many test suites all but the first test have been marked "pending". 40 | Once you get a test passing, activate the next one by changing `xit` to `it`. 41 | 42 | ## Source 43 | 44 | Wikipedia [https://en.wikipedia.org/wiki/Isogram](https://en.wikipedia.org/wiki/Isogram) 45 | 46 | ## Submitting Incomplete Solutions 47 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 48 | -------------------------------------------------------------------------------- /matrix/README.md: -------------------------------------------------------------------------------- 1 | # Matrix 2 | 3 | Write a program that, given a string representing a matrix of numbers, can return the rows and columns of that matrix. 4 | 5 | So given a string with embedded newlines like: 6 | 7 | > 9 8 7 8 | > 5 3 2 9 | > 6 6 7 10 | 11 | representing this matrix: 12 | 13 | ```plain 14 | 0 1 2 15 | |--------- 16 | 0 | 9 8 7 17 | 1 | 5 3 2 18 | 2 | 6 6 7 19 | ``` 20 | 21 | your code should be able to spit out: 22 | 23 | - A list of the rows, reading each row left-to-right while moving 24 | top-to-bottom across the rows, 25 | - A list of the columns, reading each column top-to-bottom while moving 26 | from left-to-right. 27 | 28 | The rows for our example matrix: 29 | 30 | - 9, 8, 7 31 | - 5, 3, 2 32 | - 6, 6, 7 33 | 34 | And its columns: 35 | 36 | - 9, 5, 6 37 | - 8, 3, 6 38 | - 7, 2, 7 39 | 40 | ## Setup 41 | 42 | Go through the setup instructions for JavaScript to 43 | install the necessary dependencies: 44 | 45 | http://help.exercism.io/getting-started-with-javascript.html 46 | 47 | ## Making the Test Suite Pass 48 | 49 | Execute the tests with: 50 | 51 | ```bash 52 | $ jasmine-node . 53 | ``` 54 | 55 | In many test suites all but the first test have been skipped. 56 | 57 | Once you get a test passing, you can unskip the next one by 58 | changing `xit` to `it`. 59 | 60 | 61 | ## Source 62 | 63 | Warmup to the `saddle-points` warmup. [view source](http://jumpstartlab.com) 64 | -------------------------------------------------------------------------------- /flatten-array/README.md: -------------------------------------------------------------------------------- 1 | # Flatten Array 2 | 3 | Take a nested list and return a single flattened list with all values except nil/null. 4 | 5 | The challenge is to write a function that accepts an arbitrarily-deep nested list-like structure and returns a flattened structure without any nil/null values. 6 | 7 | For Example 8 | 9 | input: [1,[2,3,null,4],[null],5] 10 | 11 | output: [1,2,3,4,5] 12 | 13 | ## Setup 14 | 15 | Go through the setup instructions for JavaScript to install the 16 | necessary dependencies: 17 | 18 | http://exercism.io/languages/javascript/installation 19 | 20 | ## Running the test suite 21 | 22 | The provided test suite uses [Jasmine](https://jasmine.github.io/). 23 | You can install it by opening a terminal window and running the 24 | following command: 25 | 26 | ```sh 27 | npm install -g jasmine 28 | ``` 29 | 30 | Run the test suite from the exercise directory with: 31 | 32 | ```sh 33 | jasmine flatten-array.spec.js 34 | ``` 35 | 36 | In many test suites all but the first test have been marked "pending". 37 | Once you get a test passing, activate the next one by changing `xit` to `it`. 38 | 39 | ## Source 40 | 41 | Interview Question [https://reference.wolfram.com/language/ref/Flatten.html](https://reference.wolfram.com/language/ref/Flatten.html) 42 | 43 | ## Submitting Incomplete Solutions 44 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 45 | -------------------------------------------------------------------------------- /connect/connect.js: -------------------------------------------------------------------------------- 1 | function transpose(board) { 2 | return board[0].map((col, i) => board.map(row => row[i])) 3 | } 4 | 5 | function hash(element) { 6 | return element.row + ':' + element.col; 7 | } 8 | 9 | function has_path(board, letter) { 10 | var seen = new Set(); 11 | var queue = board[0].reduce((queue, e, i) => { 12 | if(e == letter) 13 | queue.push({row: 0, col:i}); 14 | return queue}, new Array()); 15 | while(0 < queue.length) { 16 | elem = queue.pop(); 17 | if(seen.has(hash(elem)) || board[elem.row] == undefined || 18 | board[elem.row][elem.col] != letter) 19 | continue; 20 | seen.add(hash(elem)); 21 | if(board.length <= elem.row + 1) 22 | return true; 23 | [{dr: -1, dc: 0}, {dr: -1, dc: +1}, 24 | {dr: 0, dc: +1}, {dr: 0, dc: -1}, 25 | {dr: +1, dc: -1}, {dr: +1, dc: 0}].map(diff => 26 | queue.push({row: elem.row + diff.dr, col: elem.col + diff.dc})); 27 | } 28 | return false; 29 | } 30 | 31 | class Board { 32 | constructor(board) { 33 | this.board = board.map(row => row.trim().split(' ')); 34 | } 35 | 36 | winner() { 37 | if(has_path(this.board, 'O')) 38 | return 'O'; 39 | if(has_path(transpose(this.board), 'X')) 40 | return 'X'; 41 | return ''; 42 | } 43 | } 44 | 45 | module.exports = Board; 46 | -------------------------------------------------------------------------------- /etl/etl.spec.js: -------------------------------------------------------------------------------- 1 | var ETL = require('./etl'); 2 | 3 | describe('Transform', function() { 4 | var etl = new ETL(); 5 | 6 | it('transforms one value', function() { 7 | var old = { 1: ['A'] }; 8 | var expected = { a: 1 }; 9 | 10 | expect(etl.transform(old)).toEqual(expected); 11 | }); 12 | 13 | it('transforms more values', function() { 14 | var old = { 1: ['A', 'E', 'I', 'O', 'U'] }; 15 | var expected = { a: 1, e: 1, i: 1, o: 1, u: 1 }; 16 | 17 | expect(etl.transform(old)).toEqual(expected); 18 | }); 19 | 20 | it('transforms more keys', function() { 21 | var old = { 1: ['A', 'E'], 2: ['D', 'G'] }; 22 | var expected = { a: 1, e: 1, d: 2, g: 2 }; 23 | 24 | expect(etl.transform(old)).toEqual(expected); 25 | }); 26 | 27 | it('transforms a full dataset', function() { 28 | var old = { 29 | 1: [ 'A', 'E', 'I', 'O', 'U', 'L', 'N', 'R', 'S', 'T' ], 30 | 2: [ 'D', 'G' ], 31 | 3: [ 'B', 'C', 'M', 'P' ], 32 | 4: [ 'F', 'H', 'V', 'W', 'Y' ], 33 | 5: [ 'K' ], 34 | 8: [ 'J', 'X' ], 35 | 10: [ 'Q', 'Z' ] 36 | }; 37 | var expected = { 38 | a: 1, b: 3, c: 3, d: 2, e: 1, 39 | f: 4, g: 2, h: 4, i: 1, j: 8, 40 | k: 5, l: 1, m: 3, n: 1, o: 1, 41 | p: 3, q: 10, r: 1, s: 1, t: 1, 42 | u: 1, v: 4, w: 4, x: 8, y: 4, 43 | z: 10 44 | }; 45 | 46 | expect(etl.transform(old)).toEqual(expected); 47 | }); 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /pig-latin/README.md: -------------------------------------------------------------------------------- 1 | # Pig Latin 2 | 3 | Implement a program that translates from English to Pig Latin 4 | 5 | Pig Latin is a made-up children's language that's intended to be 6 | confusing. It obeys a few simple rules (below), but when it's spoken 7 | quickly it's really difficult for non-children (and non-native speakers) 8 | to understand. 9 | 10 | - **Rule 1**: If a word begins with a vowel sound, add an "ay" sound to 11 | the end of the word. 12 | - **Rule 2**: If a word begins with a consonant sound, move it to the 13 | end of the word, and then add an "ay" sound to the end of the word. 14 | 15 | There are a few more rules for edge cases, and there are regional 16 | variants too. 17 | 18 | See for more details. 19 | 20 | ## Setup 21 | 22 | Go through the setup instructions for JavaScript to 23 | install the necessary dependencies: 24 | 25 | http://help.exercism.io/getting-started-with-javascript.html 26 | 27 | ## Making the Test Suite Pass 28 | 29 | Execute the tests with: 30 | 31 | ```bash 32 | $ jasmine-node . 33 | ``` 34 | 35 | In many test suites all but the first test have been skipped. 36 | 37 | Once you get a test passing, you can unskip the next one by 38 | changing `xit` to `it`. 39 | 40 | 41 | ## Source 42 | 43 | The Pig Latin exercise at Test First Teaching by Ultrasaurus [view source](https://github.com/ultrasaurus/test-first-teaching/blob/master/learn_ruby/pig_latin/) 44 | -------------------------------------------------------------------------------- /two-fer/README.md: -------------------------------------------------------------------------------- 1 | # Two Fer 2 | 3 | `Two-fer` or `2-fer` is short for two for one. One for you and one for me. 4 | 5 | ```text 6 | "One for X, one for me." 7 | ``` 8 | 9 | When X is a name or "you". 10 | 11 | If the given name is "Alice", the result should be "One for Alice, one for me." 12 | If no name is given, the result should be "One for you, one for me." 13 | 14 | 15 | ## Setup 16 | 17 | Go through the setup instructions for JavaScript to install the 18 | necessary dependencies: 19 | 20 | http://exercism.io/languages/javascript/installation 21 | 22 | ## Running the test suite 23 | 24 | The provided test suite uses [Jasmine](https://jasmine.github.io/). 25 | You can install it by opening a terminal window and running the 26 | following command: 27 | 28 | ```sh 29 | npm install -g jasmine 30 | ``` 31 | 32 | Run the test suite from the exercise directory with: 33 | 34 | ```sh 35 | jasmine two-fer.spec.js 36 | ``` 37 | 38 | In many test suites all but the first test have been marked "pending". 39 | Once you get a test passing, activate the next one by changing `xit` to `it`. 40 | 41 | ## Source 42 | 43 | This is an exercise to introduce users to basic programming constructs, just after Hello World. [https://en.wikipedia.org/wiki/Two-fer](https://en.wikipedia.org/wiki/Two-fer) 44 | 45 | ## Submitting Incomplete Solutions 46 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 47 | -------------------------------------------------------------------------------- /allergies/README.md: -------------------------------------------------------------------------------- 1 | # Allergies 2 | 3 | Write a program that, given a person's allergy score, can tell them whether or not they're allergic to a given item, and their full list of allergies. 4 | 5 | An allergy test produces a single numeric score which contains the 6 | information about all the allergies the person has (that they were 7 | tested for). 8 | 9 | The list of items (and their value) that were tested are: 10 | 11 | * eggs (1) 12 | * peanuts (2) 13 | * shellfish (4) 14 | * strawberries (8) 15 | * tomatoes (16) 16 | * chocolate (32) 17 | * pollen (64) 18 | * cats (128) 19 | 20 | So if Tom is allergic to peanuts and chocolate, he gets a score of 34. 21 | 22 | Now, given just that score of 34, your program should be able to say: 23 | 24 | - Whether Tom is allergic to any one of those allergens listed above. 25 | - All the allergens Tom is allergic to. 26 | 27 | ## Setup 28 | 29 | Go through the setup instructions for JavaScript to 30 | install the necessary dependencies: 31 | 32 | http://help.exercism.io/getting-started-with-javascript.html 33 | 34 | ## Making the Test Suite Pass 35 | 36 | Execute the tests with: 37 | 38 | ```bash 39 | $ jasmine-node . 40 | ``` 41 | 42 | In many test suites all but the first test have been skipped. 43 | 44 | Once you get a test passing, you can unskip the next one by 45 | changing `xit` to `it`. 46 | 47 | 48 | ## Source 49 | 50 | Jumpstart Lab Warm-up [view source](http://jumpstartlab.com) 51 | -------------------------------------------------------------------------------- /phone-number/README.md: -------------------------------------------------------------------------------- 1 | # Phone Number 2 | 3 | Write a program that cleans up user-entered phone numbers so that they can be sent SMS messages. 4 | 5 | The rules are as follows: 6 | 7 | - If the phone number is less than 10 digits assume that it is bad 8 | number 9 | - If the phone number is 10 digits assume that it is good 10 | - If the phone number is 11 digits and the first number is 1, trim the 1 11 | and use the last 10 digits 12 | - If the phone number is 11 digits and the first number is not 1, then 13 | it is a bad number 14 | - If the phone number is more than 11 digits assume that it is a bad 15 | number 16 | 17 | We've provided tests, now make them pass. 18 | 19 | Hint: Only make one test pass at a time. Disable the others, then flip 20 | each on in turn after you get the current failing one to pass. 21 | 22 | ## Setup 23 | 24 | Go through the setup instructions for JavaScript to 25 | install the necessary dependencies: 26 | 27 | http://help.exercism.io/getting-started-with-javascript.html 28 | 29 | ## Making the Test Suite Pass 30 | 31 | Execute the tests with: 32 | 33 | ```bash 34 | $ jasmine-node . 35 | ``` 36 | 37 | In many test suites all but the first test have been skipped. 38 | 39 | Once you get a test passing, you can unskip the next one by 40 | changing `xit` to `it`. 41 | 42 | 43 | ## Source 44 | 45 | Event Manager by JumpstartLab [view source](http://tutorials.jumpstartlab.com/projects/eventmanager.html) 46 | -------------------------------------------------------------------------------- /nucleotide-count/nucleotide-count.spec.js: -------------------------------------------------------------------------------- 1 | var dna = require('./nucleotide-count'); 2 | 3 | describe('DNA', function() { 4 | 5 | it('Empty DNA strand has no adenosine', function() { 6 | expect(dna().count('A')).toEqual(0); 7 | }); 8 | 9 | it('Repetitive cytidine gets counted', function() { 10 | expect(dna('CCCCC').count('C')).toEqual(5); 11 | }); 12 | 13 | it('Counts only thymidine', function() { 14 | expect(dna('GGGGGTAACCCGG').count('T')).toEqual(1); 15 | }); 16 | 17 | it('Counts a nucleotide only once', function() { 18 | var acid = dna('CGATTGGG'); 19 | acid.count('T'); 20 | acid.count('T'); 21 | expect(acid.count('T')).toEqual(2); 22 | }); 23 | 24 | it('Empty DNS strand has no nucleotides', function() { 25 | var expected = {A: 0, T: 0, C: 0, G: 0}; 26 | expect(dna().histogram()).toEqual(expected); 27 | }); 28 | 29 | it('Repetitive sequence has only guanosine', function() { 30 | var expected = {A: 0, T: 0, C: 0, G: 8}; 31 | expect(dna('GGGGGGGG').histogram()).toEqual(expected); 32 | }); 33 | 34 | it('Counts all nucleotides', function() { 35 | var strand = 'AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC'; 36 | var expected = {A: 20, T: 21, C: 12, G: 17}; 37 | expect(dna(strand).histogram()).toEqual(expected); 38 | }); 39 | 40 | it('Validates DNA', function() { 41 | expect(dna.bind(null, 'JOHNNYAPPLESEED')).toThrow(); 42 | }); 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /twelve-days/twelve-days.js: -------------------------------------------------------------------------------- 1 | const days = [ 2 | { day: "first", gift: "a Partridge in a Pear Tree"}, 3 | { day: "second", gift: "two Turtle Doves"}, 4 | { day: "third", gift: "three French Hens"}, 5 | { day: "fourth", gift: "four Calling Birds"}, 6 | { day: "fifth", gift: "five Gold Rings"}, 7 | { day: "sixth", gift: "six Geese-a-Laying"}, 8 | { day: "seventh", gift: "seven Swans-a-Swimming"}, 9 | { day: "eighth", gift: "eight Maids-a-Milking"}, 10 | { day: "ninth", gift: "nine Ladies Dancing"}, 11 | { day: "tenth", gift: "ten Lords-a-Leaping"}, 12 | { day: "eleventh", gift: "eleven Pipers Piping"}, 13 | { day: "twelfth", gift: "twelve Drummers Drumming"}, 14 | ]; 15 | 16 | class TwelveDays { 17 | verse(verses) { 18 | var start = verses[0] - 1; 19 | var end = verses[1] || start + 1; 20 | var result = []; 21 | for(var current = start; current < end; current++) { 22 | var line = "On the " + days[current].day; 23 | line += " day of Christmas my true love gave to me"; 24 | for(var v = current; 0 <= v; v--) { 25 | line += ", " + (v == 0 && current != 0 ? "and " : ""); 26 | line += days[v].gift; 27 | } 28 | result.push(line + ".\n"); 29 | } 30 | return result.join('\n'); 31 | } 32 | 33 | sing() { return this.verse([1, 12]); } 34 | } 35 | 36 | module.exports = TwelveDays; 37 | -------------------------------------------------------------------------------- /trinary/README.md: -------------------------------------------------------------------------------- 1 | # Trinary 2 | 3 | Write a program that will convert a trinary number, represented as a string (e.g. '102012'), to its decimal equivalent using first principles. 4 | 5 | The program should consider strings specifying an invalid trinary as the 6 | value 0. 7 | 8 | Trinary numbers contain three symbols: 0, 1, and 2. 9 | 10 | The last place in a trinary number is the 1's place. The second to last 11 | is the 3's place, the third to last is the 9's place, etc. 12 | 13 | ```bash 14 | # "102012" 15 | 1 0 2 0 1 2 # the number 16 | 1*3^5 + 0*3^4 + 2*3^3 + 0*3^2 + 1*3^1 + 2*3^0 # the value 17 | 243 + 0 + 54 + 0 + 3 + 2 = 302 18 | ``` 19 | 20 | If your language provides a method in the standard library to perform the 21 | conversion, pretend it doesn't exist and implement it yourself. 22 | 23 | ## Setup 24 | 25 | Go through the setup instructions for JavaScript to 26 | install the necessary dependencies: 27 | 28 | http://help.exercism.io/getting-started-with-javascript.html 29 | 30 | ## Making the Test Suite Pass 31 | 32 | Execute the tests with: 33 | 34 | ```bash 35 | $ jasmine-node . 36 | ``` 37 | 38 | In many test suites all but the first test have been skipped. 39 | 40 | Once you get a test passing, you can unskip the next one by 41 | changing `xit` to `it`. 42 | 43 | 44 | ## Source 45 | 46 | All of Computer Science [view source](http://www.wolframalpha.com/input/?i=binary&a=*C.binary-_*MathWorld-) 47 | -------------------------------------------------------------------------------- /word-search/word-search.js: -------------------------------------------------------------------------------- 1 | export default class WordSearch { 2 | constructor(grid) { 3 | this.grid = grid; 4 | } 5 | 6 | find(words) { 7 | let result = words.reduce((acc, w) => { acc[w] = undefined; return acc }, {}); 8 | for (let r = 0; r < this.grid.length; r++) { 9 | for (let c = 0; c < this.grid[r].length; c++) { 10 | for (let word of words) { 11 | let end = this.search(word, r, c) 12 | if (end != undefined) { 13 | result[word] = {"start": [r+1, c+1], "end": end}; 14 | } 15 | } 16 | } 17 | } 18 | return result; 19 | } 20 | 21 | search(word, row, col) { 22 | let diffs = [ 23 | {"row": 1, "col": 1}, 24 | {"row": 1, "col": 0}, 25 | {"row": 1, "col": -1}, 26 | {"row": 0, "col": 1}, 27 | {"row": 0, "col": -1}, 28 | {"row": -1, "col": 1}, 29 | {"row": -1, "col": 0}, 30 | {"row": -1, "col": -1}, 31 | ] 32 | for (let diff of diffs) { 33 | let flag = true; 34 | for (let pos = 0; pos < word.length; pos++) { 35 | if (this.grid[diff.row * pos + row] == undefined || 36 | word[pos] != this.grid[diff.row * pos + row][diff.col * pos + col]) { 37 | flag = false 38 | break 39 | } 40 | } 41 | if (flag) { 42 | return [diff.row * (word.length - 1) + row + 1, 43 | diff.col * (word.length - 1) + col + 1]; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /binary/binary.spec.js: -------------------------------------------------------------------------------- 1 | var Binary = require('./binary'); 2 | 3 | describe('binary', function() { 4 | it('0 is decimal 0', function() { 5 | expect(new Binary('0').toDecimal()).toEqual(0); 6 | }); 7 | 8 | it('1 is decimal 1', function() { 9 | expect(new Binary('1').toDecimal()).toEqual(1); 10 | }); 11 | 12 | it('10 is decimal 2', function() { 13 | expect(new Binary('10').toDecimal()).toEqual(2); 14 | }); 15 | 16 | it('11 is decimal 3', function() { 17 | expect(new Binary('11').toDecimal()).toEqual(3); 18 | }); 19 | 20 | it('100 is decimal 4', function() { 21 | expect(new Binary('100').toDecimal()).toEqual(4); 22 | }); 23 | 24 | it('1001 is decimal 9', function() { 25 | expect(new Binary('1001').toDecimal()).toEqual(9); 26 | }); 27 | 28 | it('11010 is decimal 26', function() { 29 | expect(new Binary('11010').toDecimal()).toEqual(26); 30 | }); 31 | 32 | it('10001101000 is decimal 1128', function() { 33 | expect(new Binary('10001101000').toDecimal()).toEqual(1128); 34 | }); 35 | 36 | it('00011111 is decimal 31', function() { 37 | expect(new Binary('00011111').toDecimal()).toEqual(31); 38 | }); 39 | 40 | it('invalid inputs are decimal 0', function() { 41 | expect(new Binary('carrot').toDecimal()).toEqual(0); 42 | expect(new Binary('012').toDecimal()).toEqual(0); 43 | expect(new Binary('10nope').toDecimal()).toEqual(0); 44 | expect(new Binary('nope10').toDecimal()).toEqual(0); 45 | }); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /saddle-points/README.md: -------------------------------------------------------------------------------- 1 | # Saddle Points 2 | 3 | Write a program that detects saddle points in a matrix. 4 | 5 | So say you have a matrix like so: 6 | 7 | ```plain 8 | 0 1 2 9 | |--------- 10 | 0 | 9 8 7 11 | 1 | 5 3 2 <--- saddle point at (1,0) 12 | 2 | 6 6 7 13 | ``` 14 | 15 | It has a saddle point at (1, 0). 16 | 17 | It's called a "saddle point" because it is greater than or equal to 18 | every element in its row and the less than or equal to every element in 19 | its column. 20 | 21 | A matrix may have zero or more saddle points. 22 | 23 | Your code should be able to provide the (possibly empty) list of all the 24 | saddle points for any given matrix. 25 | 26 | Note that you may find other definitions of matrix saddle points online, 27 | but the tests for this exercise follow the above unambiguous definition. 28 | 29 | ## Setup 30 | 31 | Go through the setup instructions for JavaScript to 32 | install the necessary dependencies: 33 | 34 | http://help.exercism.io/getting-started-with-javascript.html 35 | 36 | ## Making the Test Suite Pass 37 | 38 | Execute the tests with: 39 | 40 | ```bash 41 | $ jasmine-node . 42 | ``` 43 | 44 | In many test suites all but the first test have been skipped. 45 | 46 | Once you get a test passing, you can unskip the next one by 47 | changing `xit` to `it`. 48 | 49 | 50 | ## Source 51 | 52 | J Dalbey's Programming Practice problems [view source](http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html) 53 | -------------------------------------------------------------------------------- /saddle-points/saddle-points.spec.js: -------------------------------------------------------------------------------- 1 | var Matrix = require('./saddle-points'); 2 | 3 | describe('Matrix', function() { 4 | it('extracts a row', function() { 5 | var matrix = new Matrix('1 2\n10 20'); 6 | expect(matrix.rows[0]).toEqual([1,2]); 7 | }); 8 | 9 | it('extracts other row', function() { 10 | var matrix = new Matrix('9 8 7\n19 18 17'); 11 | expect(matrix.rows[1]).toEqual([19, 18, 17]); 12 | }); 13 | 14 | it('extracts a column', function() { 15 | var matrix = new Matrix('1 2 3\n4 5 6\n7 8 9\n8 7 6'); 16 | expect(matrix.columns[0]).toEqual([1, 4, 7, 8]); 17 | }); 18 | 19 | it('extracts another column', function() { 20 | var matrix = new Matrix('89 1903 3\n18 3 1\n9 4 800'); 21 | expect(matrix.columns[1]).toEqual([1903, 3, 4]); 22 | }); 23 | 24 | it('no saddle point', function() { 25 | var matrix = new Matrix('2 1\n1 2'); 26 | expect(matrix.saddlePoints).toEqual([]); 27 | }); 28 | 29 | it('a saddle point', function() { 30 | var matrix = new Matrix('1 2\n3 4'); 31 | expect(matrix.saddlePoints).toEqual([[0,1]]); 32 | }); 33 | 34 | it('another saddle point', function() { 35 | var matrix = new Matrix('18 3 39 19 91\n38 10 8 77 320\n3 4 8 6 7'); 36 | expect(matrix.saddlePoints).toEqual([[2,2]]); 37 | }); 38 | 39 | it('multiple saddle points', function() { 40 | var matrix = new Matrix('4 5 4\n3 5 5\n1 5 4'); 41 | expect(matrix.saddlePoints).toEqual([[0, 1], [1, 1], [2, 1]]); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /palindrome-products/palindrome-products.js: -------------------------------------------------------------------------------- 1 | var Palindromes = function(args) { 2 | /* Pairs of numbers that when multipied are palindromes */ 3 | this.maxFactors = args['maxFactor']; 4 | this.minFactor = args['minFactor'] || 1; 5 | } 6 | 7 | Palindromes.prototype.generate = function() { 8 | /* Finds all palindrome products in a range */ 9 | if(this.palindromes) 10 | return; 11 | this.palindromes = {}; 12 | for(var i = this.minFactor; i <= this.maxFactors; i++) { 13 | for( var j = i; j <= this.maxFactors; j++) { 14 | var product = i * j; 15 | if(is_palindrome(product.toString())) { 16 | if(!(product in this.palindromes)) 17 | this.palindromes[product] = []; 18 | this.palindromes[product].push([i,j]); 19 | } 20 | } 21 | } 22 | }; 23 | 24 | Palindromes.prototype.largest = function() { 25 | /* The largest product generated */ 26 | var keys = Object.keys(this.palindromes); 27 | var max = Math.max.apply(null, keys); 28 | return {value: max, factors: this.palindromes[max]}; 29 | }; 30 | 31 | Palindromes.prototype.smallest = function() { 32 | /* The smallest product generated */ 33 | var keys = Object.keys(this.palindromes); 34 | var min = Math.min.apply(null, keys); 35 | return {value: min, factors: this.palindromes[min]}; 36 | }; 37 | 38 | function is_palindrome(str) { 39 | for(var i = 0; i < (str.length / 2); i++) { 40 | if(str[i] != str[str.length - i - 1]) 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | module.exports = Palindromes; 47 | -------------------------------------------------------------------------------- /robot-name/README.md: -------------------------------------------------------------------------------- 1 | # Robot Name 2 | 3 | Write a program that manages robot factory settings. 4 | 5 | When robots come off the factory floor, they have no name. 6 | 7 | The first time you boot them up, a random name is generated, such as 8 | RX837 or BC811. 9 | 10 | Every once in a while we need to reset a robot to its factory settings, 11 | which means that their name gets wiped. The next time you ask, it gets a 12 | new name. 13 | 14 | 15 | ## For bonus points 16 | 17 | Did you get the tests passing and the code clean? If you want to, these 18 | are some additional things you could try: 19 | 20 | - Random names means a risk of collisions. Make sure the same name is 21 | never used twice. Feel free to introduce additional tests. 22 | 23 | Then please share your thoughts in a comment on the submission. Did this 24 | experiment make the code better? Worse? Did you learn anything from it? 25 | 26 | ## Setup 27 | 28 | Go through the setup instructions for JavaScript to 29 | install the necessary dependencies: 30 | 31 | http://help.exercism.io/getting-started-with-javascript.html 32 | 33 | ## Making the Test Suite Pass 34 | 35 | Execute the tests with: 36 | 37 | ```bash 38 | $ jasmine-node . 39 | ``` 40 | 41 | In many test suites all but the first test have been skipped. 42 | 43 | Once you get a test passing, you can unskip the next one by 44 | changing `xit` to `it`. 45 | 46 | 47 | ## Source 48 | 49 | A debugging session with Paul Blackwell at gSchool. [view source](http://gschool.it) 50 | -------------------------------------------------------------------------------- /secret-handshake/README.md: -------------------------------------------------------------------------------- 1 | # Secret Handshake 2 | 3 | Write a program that will take a decimal number, and convert it to the appropriate sequence of events for a secret handshake. 4 | 5 | > There are 10 types of people in the world: Those who understand 6 | > binary, and those who don't. 7 | 8 | You and your fellow cohort of those in the "know" when it comes to 9 | binary decide to come up with a secret "handshake". 10 | 11 | ``` 12 | 1 = wink 13 | 10 = double blink 14 | 100 = close your eyes 15 | 1000 = jump 16 | 17 | 18 | 10000 = Reverse the order of the operations in the secret handshake. 19 | ``` 20 | 21 | ``` 22 | handshake = SecretHandshake.new 9 23 | handshake.commands # => ["wink","jump"] 24 | 25 | handshake = SecretHandshake.new "11001" 26 | handshake.commands # => ["jump","wink"] 27 | ``` 28 | 29 | The program should consider strings specifying an invalid binary as the 30 | value 0. 31 | 32 | ## Setup 33 | 34 | Go through the setup instructions for JavaScript to 35 | install the necessary dependencies: 36 | 37 | http://help.exercism.io/getting-started-with-javascript.html 38 | 39 | ## Making the Test Suite Pass 40 | 41 | Execute the tests with: 42 | 43 | ```bash 44 | $ jasmine-node . 45 | ``` 46 | 47 | In many test suites all but the first test have been skipped. 48 | 49 | Once you get a test passing, you can unskip the next one by 50 | changing `xit` to `it`. 51 | 52 | 53 | ## Source 54 | 55 | Bert, in Mary Poppins [view source](http://www.imdb.com/character/ch0011238/quotes) 56 | -------------------------------------------------------------------------------- /strain/README.md: -------------------------------------------------------------------------------- 1 | # Strain 2 | 3 | Implement the `keep` and `discard` operation on collections. Given a collection and a predicate on the collection's elements, `keep` returns a new collection containing those elements where the predicate is true, while `discard` returns a new collection containing those elements where the predicate is false. 4 | 5 | For example, given the collection of numbers: 6 | 7 | - 1, 2, 3, 4, 5 8 | 9 | And the predicate: 10 | 11 | - is the number even? 12 | 13 | Then your `keep` operation should produce: 14 | 15 | - 2, 4 16 | 17 | While your `discard` operation should produce: 18 | 19 | - 1, 3, 5 20 | 21 | Note that the union of `keep` and `discard` is all the elements. 22 | 23 | ## Restrictions 24 | 25 | Keep your hands off that filter/reject/whatchamacallit functionality 26 | provided by your standard library! Solve this one yourself using other 27 | basic tools instead. 28 | 29 | ## Setup 30 | 31 | Go through the setup instructions for JavaScript to 32 | install the necessary dependencies: 33 | 34 | http://help.exercism.io/getting-started-with-javascript.html 35 | 36 | ## Making the Test Suite Pass 37 | 38 | Execute the tests with: 39 | 40 | ```bash 41 | $ jasmine-node . 42 | ``` 43 | 44 | In many test suites all but the first test have been skipped. 45 | 46 | Once you get a test passing, you can unskip the next one by 47 | changing `xit` to `it`. 48 | 49 | 50 | ## Source 51 | 52 | Conversation with James Edward Gray II [view source](https://twitter.com/jeg2) 53 | -------------------------------------------------------------------------------- /raindrops/README.md: -------------------------------------------------------------------------------- 1 | # Raindrops 2 | 3 | Write a program that converts a number to a string, the contents of which depends on the number's prime factors. 4 | 5 | - If the number contains 3 as a prime factor, output 'Pling'. 6 | - If the number contains 5 as a prime factor, output 'Plang'. 7 | - If the number contains 7 as a prime factor, output 'Plong'. 8 | - If the number does not contain 3, 5, or 7 as a prime factor, 9 | just pass the number's digits straight through. 10 | 11 | ## Examples 12 | 13 | - 28's prime-factorization is 2, 2, 7. 14 | - In raindrop-speak, this would be a simple "Plong". 15 | - 1755 prime-factorization is 3, 3, 3, 5, 13. 16 | - In raindrop-speak, this would be a "PlingPlang". 17 | - The prime factors of 34 are 2 and 17. 18 | - Raindrop-speak doesn't know what to make of that, 19 | so it just goes with the straightforward "34". 20 | 21 | ## Setup 22 | 23 | Go through the setup instructions for JavaScript to 24 | install the necessary dependencies: 25 | 26 | http://help.exercism.io/getting-started-with-javascript.html 27 | 28 | ## Making the Test Suite Pass 29 | 30 | Execute the tests with: 31 | 32 | ```bash 33 | $ jasmine-node . 34 | ``` 35 | 36 | In many test suites all but the first test have been skipped. 37 | 38 | Once you get a test passing, you can unskip the next one by 39 | changing `xit` to `it`. 40 | 41 | 42 | ## Source 43 | 44 | A variation on a famous interview question intended to weed out potential candidates. [view source](http://jumpstartlab.com) 45 | -------------------------------------------------------------------------------- /queen-attack/README.md: -------------------------------------------------------------------------------- 1 | # Queen Attack 2 | 3 | Write a program that positions two queens on a chess board and indicates whether or not they are positioned so that they can attack each other. 4 | 5 | In the game of chess, a queen can attack pieces which are on the same 6 | row, column, or diagonal. 7 | 8 | A chessboard can be represented by an 8 by 8 array. 9 | 10 | So if you're told the white queen is at (2, 3) and the black queen at 11 | (5, 6), then you'd know you've got a set-up like so: 12 | 13 | ```plain 14 | _ _ _ _ _ _ _ _ 15 | _ _ _ _ _ _ _ _ 16 | _ _ _ W _ _ _ _ 17 | _ _ _ _ _ _ _ _ 18 | _ _ _ _ _ _ _ _ 19 | _ _ _ _ _ _ B _ 20 | _ _ _ _ _ _ _ _ 21 | _ _ _ _ _ _ _ _ 22 | ``` 23 | 24 | You'd also be able to answer whether the queens can attack each other. 25 | In this case, that answer would be yes, they can, because both pieces 26 | share a diagonal. 27 | 28 | ## Setup 29 | 30 | Go through the setup instructions for JavaScript to 31 | install the necessary dependencies: 32 | 33 | http://help.exercism.io/getting-started-with-javascript.html 34 | 35 | ## Making the Test Suite Pass 36 | 37 | Execute the tests with: 38 | 39 | ```bash 40 | $ jasmine-node . 41 | ``` 42 | 43 | In many test suites all but the first test have been skipped. 44 | 45 | Once you get a test passing, you can unskip the next one by 46 | changing `xit` to `it`. 47 | 48 | 49 | ## Source 50 | 51 | J Dalbey's Programming Practice problems [view source](http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html) 52 | -------------------------------------------------------------------------------- /sieve/README.md: -------------------------------------------------------------------------------- 1 | # Sieve 2 | 3 | Write a program that uses the Sieve of Eratosthenes to find all the primes from 2 up to a given number. 4 | 5 | The Sieve of Eratosthenes is a simple, ancient algorithm for finding all 6 | prime numbers up to any given limit. It does so by iteratively marking as 7 | composite (i.e. not prime) the multiples of each prime, 8 | starting with the multiples of 2. 9 | 10 | Create your range, starting at two and continuing up to and including the given limit. (i.e. [2, limit]) 11 | 12 | The algorithm consists of repeating the following over and over: 13 | 14 | - take the next available unmarked number in your list (it is prime) 15 | - mark all the multiples of that number (they are not prime) 16 | 17 | Repeat until you have processed each number in your range. 18 | 19 | When the algorithm terminates, all the numbers in the list that have not 20 | been marked are prime. 21 | 22 | ## Setup 23 | 24 | Go through the setup instructions for JavaScript to 25 | install the necessary dependencies: 26 | 27 | http://help.exercism.io/getting-started-with-javascript.html 28 | 29 | ## Making the Test Suite Pass 30 | 31 | Execute the tests with: 32 | 33 | ```bash 34 | $ jasmine-node . 35 | ``` 36 | 37 | In many test suites all but the first test have been skipped. 38 | 39 | Once you get a test passing, you can unskip the next one by 40 | changing `xit` to `it`. 41 | 42 | 43 | ## Source 44 | 45 | Sieve of Eratosthenes at Wikipedia [view source](http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes) 46 | -------------------------------------------------------------------------------- /leap/leap.spec.js: -------------------------------------------------------------------------------- 1 | var Year = require('./leap'); 2 | 3 | describe('Leap year', function() { 4 | 5 | it('is not very common', function() { 6 | var year = new Year(2015); 7 | expect(year.isLeap()).toBe(false); 8 | }); 9 | 10 | it('is introduced every 4 years to adjust about a day', function() { 11 | var year = new Year(2016); 12 | expect(year.isLeap()).toBe(true); 13 | }); 14 | 15 | it('is skipped every 100 years to remove an extra day', function() { 16 | var year = new Year(1900); 17 | expect(year.isLeap()).toBe(false); 18 | }); 19 | 20 | it('is reintroduced every 400 years to adjust another day', function() { 21 | var year = new Year(2000); 22 | expect(year.isLeap()).toBe(true); 23 | }); 24 | 25 | // Feel free to enable the following tests to check some more examples 26 | xdescribe('Additional example of a leap year that', function () { 27 | 28 | it('is not a leap year', function () { 29 | var year = new Year(1978); 30 | expect(year.isLeap()).toBe(false); 31 | }); 32 | 33 | it('is a common leap year', function () { 34 | var year = new Year(1992); 35 | expect(year.isLeap()).toBe(true); 36 | }); 37 | 38 | it('is skipped every 100 years', function () { 39 | var year = new Year(2100); 40 | expect(year.isLeap()).toBe(false); 41 | }); 42 | 43 | it('is reintroduced every 400 years', function () { 44 | var year = new Year(2400); 45 | expect(year.isLeap()).toBe(true); 46 | }); 47 | 48 | }); 49 | 50 | }); 51 | -------------------------------------------------------------------------------- /nucleotide-count/README.md: -------------------------------------------------------------------------------- 1 | # Nucleotide Count 2 | 3 | Given a DNA string, compute how many times each nucleotide occurs in the string. 4 | 5 | DNA is represented by an alphabet of the following symbols: 'A', 'C', 6 | 'G', and 'T'. 7 | 8 | Each symbol represents a nucleotide, which is a fancy name for the 9 | particular molecules that happen to make up a large part of DNA. 10 | 11 | Shortest intro to biochemistry EVAR: 12 | 13 | - twigs are to birds nests as 14 | - nucleotides are to DNA and RNA as 15 | - amino acids are to proteins as 16 | - sugar is to starch as 17 | - oh crap lipids 18 | 19 | I'm not going to talk about lipids because they're crazy complex. 20 | 21 | So back to nucleotides. 22 | 23 | DNA contains four types of them: adenine (`A`), cytosine (`C`), guanine 24 | (`G`), and thymine (`T`). 25 | 26 | RNA contains a slightly different set of nucleotides, but we don't care 27 | about that for now. 28 | 29 | ## Setup 30 | 31 | Go through the setup instructions for JavaScript to 32 | install the necessary dependencies: 33 | 34 | http://help.exercism.io/getting-started-with-javascript.html 35 | 36 | ## Making the Test Suite Pass 37 | 38 | Execute the tests with: 39 | 40 | ```bash 41 | $ jasmine-node . 42 | ``` 43 | 44 | In many test suites all but the first test have been skipped. 45 | 46 | Once you get a test passing, you can unskip the next one by 47 | changing `xit` to `it`. 48 | 49 | 50 | ## Source 51 | 52 | The Calculating DNA Nucleotides_problem at Rosalind [view source](http://rosalind.info/problems/dna/) 53 | -------------------------------------------------------------------------------- /difference-of-squares/difference-of-squares.spec.js: -------------------------------------------------------------------------------- 1 | var Squares = require('./difference-of-squares'); 2 | 3 | describe('Squares', function () { 4 | 5 | describe('up to 5', function () { 6 | var squares = new Squares(5); 7 | 8 | it('gets the square of sums', function () { 9 | expect(squares.squareOfSums).toBe(225); 10 | }); 11 | 12 | it('gets the sum of squares', function () { 13 | expect(squares.sumOfSquares).toBe(55); 14 | }); 15 | 16 | it('gets the difference', function () { 17 | expect(squares.difference).toBe(170); 18 | }); 19 | 20 | }); 21 | 22 | describe('up to 10', function () { 23 | var squares = new Squares(10); 24 | 25 | it('gets the square of sums', function () { 26 | expect(squares.squareOfSums).toBe(3025); 27 | }); 28 | 29 | it('gets the sum of squares', function () { 30 | expect(squares.sumOfSquares).toBe(385); 31 | }); 32 | 33 | it('gets the difference', function () { 34 | expect(squares.difference).toBe(2640); 35 | }); 36 | 37 | }); 38 | 39 | describe('up to 100', function () { 40 | var squares = new Squares(100); 41 | 42 | it('gets the square of sums', function () { 43 | expect(squares.squareOfSums).toBe(25502500); 44 | }); 45 | 46 | it('gets the sum of squares', function () { 47 | expect(squares.sumOfSquares).toBe(338350); 48 | }); 49 | 50 | it('gets the difference', function () { 51 | expect(squares.difference).toBe(25164150); 52 | }); 53 | 54 | }); 55 | 56 | }); 57 | -------------------------------------------------------------------------------- /armstrong-numbers/armstrong-numbers.spec.js: -------------------------------------------------------------------------------- 1 | var ArmstrongNumber = require('./armstrong-numbers'); 2 | 3 | describe('ArmstrongNumber', function () { 4 | it('Single digit numbers are Armstrong numbers', function () { 5 | var input = 5; 6 | expect(ArmstrongNumber.validate(input)).toBe(true); 7 | }); 8 | 9 | it('There are no 2 digit Armstrong numbers', function () { 10 | var input = 10; 11 | expect(ArmstrongNumber.validate(input)).toBe(false); 12 | }); 13 | 14 | it('Three digit number that is an Armstrong number', function () { 15 | var input = 153; 16 | expect(ArmstrongNumber.validate(input)).toBe(true); 17 | }); 18 | 19 | it('Three digit number that is not an Armstrong number', function () { 20 | var input = 100; 21 | expect(ArmstrongNumber.validate(input)).toBe(false); 22 | }); 23 | 24 | it('Four digit number that is an Armstrong number', function () { 25 | var input = 9474; 26 | expect(ArmstrongNumber.validate(input)).toBe(true); 27 | }); 28 | 29 | it('Four digit number that is not an Armstrong number', function () { 30 | var input = 9475; 31 | expect(ArmstrongNumber.validate(input)).toBe(false); 32 | }); 33 | 34 | it('Seven digit number that is an Armstrong number', function () { 35 | var input = 9926315; 36 | expect(ArmstrongNumber.validate(input)).toBe(true); 37 | }); 38 | 39 | it('Seven digit number that is not an Armstrong number', function () { 40 | var input = 9926314; 41 | expect(ArmstrongNumber.validate(input)).toBe(false); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /spiral-matrix/README.md: -------------------------------------------------------------------------------- 1 | # Spiral Matrix 2 | 3 | Given the size, return a square matrix of numbers in spiral order. 4 | 5 | The matrix should be filled with natural numbers, starting from 1 6 | in the top-left corner, increasing in an inward, clockwise spiral order, 7 | like these examples: 8 | 9 | ###### Spiral matrix of size 3 10 | 11 | ```text 12 | 1 2 3 13 | 8 9 4 14 | 7 6 5 15 | ``` 16 | 17 | ###### Spiral matrix of size 4 18 | 19 | ```text 20 | 1 2 3 4 21 | 12 13 14 5 22 | 11 16 15 6 23 | 10 9 8 7 24 | ``` 25 | 26 | ## Setup 27 | 28 | Go through the setup instructions for ECMAScript to 29 | install the necessary dependencies: 30 | 31 | http://exercism.io/languages/ecmascript 32 | 33 | ## Requirements 34 | 35 | Install assignment dependencies: 36 | 37 | ```bash 38 | $ npm install 39 | ``` 40 | 41 | ## Making the test suite pass 42 | 43 | Execute the tests with: 44 | 45 | ```bash 46 | $ npm test 47 | ``` 48 | 49 | In the test suites all tests but the first have been skipped. 50 | 51 | Once you get a test passing, you can enable the next one by 52 | changing `xtest` to `test`. 53 | 54 | 55 | ## Source 56 | 57 | Reddit r/dailyprogrammer challenge #320 [Easy] Spiral Ascension. [https://www.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/](https://www.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/) 58 | 59 | ## Submitting Incomplete Solutions 60 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 61 | -------------------------------------------------------------------------------- /accumulate/accumulate.spec.js: -------------------------------------------------------------------------------- 1 | var accumulate = require('./accumulate'); 2 | 3 | describe('accumulate()', function() { 4 | 5 | it('accumulation empty', function() { 6 | var accumulator = function(e) { return e * e; }; 7 | expect(accumulate([], accumulator)).toEqual([]); 8 | }); 9 | 10 | it('accumulate squares', function() { 11 | var accumulator = function(number) { 12 | return number * number; 13 | }; 14 | 15 | var result = accumulate([1, 2, 3], accumulator); 16 | 17 | expect(result).toEqual([1, 4, 9]); 18 | }); 19 | 20 | it('accumulate upcases', function() { 21 | var accumulator = function(word) { 22 | return word.toUpperCase(); 23 | }; 24 | 25 | var result = accumulate('hello world'.split(/\s/), accumulator); 26 | 27 | expect(result).toEqual(['HELLO', 'WORLD']); 28 | }); 29 | 30 | it('accumulate reversed strings', function() { 31 | var accumulator = function(word) { 32 | return word.split('').reverse().join(''); 33 | }; 34 | 35 | var result = accumulate('the quick brown fox etc'.split(/\s/), accumulator); 36 | 37 | expect(result).toEqual(['eht', 'kciuq', 'nworb', 'xof', 'cte']); 38 | }); 39 | 40 | it('accumulate recursively', function() { 41 | var result = accumulate('a b c'.split(/\s/), function(char) { 42 | return accumulate('1 2 3'.split(/\s/), function(digit) { 43 | return char + digit; 44 | }); 45 | }); 46 | 47 | expect(result).toEqual([['a1', 'a2', 'a3'], ['b1', 'b2', 'b3'], ['c1', 'c2', 'c3']]); 48 | }); 49 | 50 | }); 51 | -------------------------------------------------------------------------------- /pythagorean-triplet/pythagorean-triplet.spec.js: -------------------------------------------------------------------------------- 1 | var Triplet = require('./pythagorean-triplet'); 2 | 3 | describe('Triplet', function () { 4 | 5 | it('calculates the sum', function () { 6 | expect(new Triplet(3, 4, 5).sum()).toBe(12); 7 | }); 8 | 9 | it('calculates the product', function () { 10 | expect(new Triplet(3, 4, 5).product()).toBe(60); 11 | }); 12 | 13 | it('can recognize a pythagorean triplet', function () { 14 | expect(new Triplet(3, 4, 5).isPythagorean()).toBe(true); 15 | }); 16 | 17 | it('can recognize a non pythagorean triplet', function () { 18 | expect(new Triplet(5, 6, 7).isPythagorean()).toBe(false); 19 | }); 20 | 21 | it('can make triplets up to 10', function () { 22 | var triplets = Triplet.where({ maxFactor: 10 }); 23 | var products = triplets.sort().map( function (triplet) { 24 | return triplet.product(); 25 | }); 26 | expect(products).toEqual([60, 480]); 27 | }); 28 | 29 | it('can make triplets 11 through 20', function () { 30 | var triplets = Triplet.where({ minFactor: 11, maxFactor: 20 }); 31 | var products = triplets.sort().map( function (triplet) { 32 | return triplet.product(); 33 | }); 34 | expect(products).toEqual([3840]); 35 | }); 36 | 37 | it('can filter on sum', function () { 38 | var triplets = Triplet.where({ sum: 180, maxFactor: 100 }); 39 | var products = triplets.sort().map( function (triplet) { 40 | return triplet.product(); 41 | }); 42 | expect(products).toEqual([118080, 168480, 202500]); 43 | }); 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /change/change.js: -------------------------------------------------------------------------------- 1 | class Change { 2 | calculate(coins, value) { 3 | if(value < 0) 4 | throw new Error("Negative totals are not allowed."); 5 | var queue = [{ 6 | change: [], 7 | purse: coins.sort((a, b) => b - a), 8 | remaining: value}]; 9 | var state, min; 10 | while(0 < queue.length) { 11 | state = queue.pop(); 12 | if(min != undefined && min.length <= state.change.length) 13 | continue; 14 | else if(state.remaining == 0) 15 | min = state.change.sort((a, b) => a - b); 16 | else if(0 < state.purse.length) { 17 | // Order matters, keep the larger coin on top 18 | if(0 < state.purse.length) { 19 | queue.push({ 20 | change: state.change, 21 | purse: state.purse.slice(1), 22 | remaining: state.remaining}); 23 | } 24 | if(state.purse[0] <= state.remaining) { 25 | queue.push({ 26 | change: [state.purse[0], ...state.change], 27 | purse: state.purse, 28 | remaining: state.remaining - state.purse[0]}); 29 | } 30 | } 31 | } 32 | if(min == undefined) 33 | throw new Error("The total " + value + " cannot be represented in the given currency."); 34 | 35 | return min; 36 | } 37 | } 38 | 39 | module.exports = Change; 40 | -------------------------------------------------------------------------------- /prime-factors/README.md: -------------------------------------------------------------------------------- 1 | # Prime Factors 2 | 3 | Compute the prime factors of a given natural number. 4 | 5 | A prime number is only evenly divisible by itself and 1. 6 | 7 | Note that 1 is not a prime number. 8 | 9 | ## Example 10 | 11 | What are the prime factors of 60? 12 | 13 | - Our first divisor is 2. 2 goes into 60, leaving 30. 14 | - 2 goes into 30, leaving 15. 15 | - 2 doesn't go cleanly into 15. So let's move on to our next divisor, 3. 16 | - 3 goes cleanly into 15, leaving 5. 17 | - 3 does not go cleanly into 5. The next possible factor is 4. 18 | - 4 does not go cleanly into 5. The next possible factor is 5. 19 | - 5 does go cleanly into 5. 20 | - We're left only with 1, so now, we're done. 21 | 22 | Our successful divisors in that computation represent the list of prime 23 | factors of 60: 2, 2, 3, and 5. 24 | 25 | You can check this yourself: 26 | 27 | - 2 * 2 * 3 * 5 28 | - = 4 * 15 29 | - = 60 30 | - Success! 31 | 32 | ## Setup 33 | 34 | Go through the setup instructions for JavaScript to 35 | install the necessary dependencies: 36 | 37 | http://help.exercism.io/getting-started-with-javascript.html 38 | 39 | ## Making the Test Suite Pass 40 | 41 | Execute the tests with: 42 | 43 | ```bash 44 | $ jasmine-node . 45 | ``` 46 | 47 | In many test suites all but the first test have been skipped. 48 | 49 | Once you get a test passing, you can unskip the next one by 50 | changing `xit` to `it`. 51 | 52 | 53 | ## Source 54 | 55 | The Prime Factors Kata by Uncle Bob [view source](http://butunclebob.com/ArticleS.UncleBob.ThePrimeFactorsKata) 56 | -------------------------------------------------------------------------------- /minesweeper/README.md: -------------------------------------------------------------------------------- 1 | # Minesweeper 2 | 3 | Add the numbers to a minesweeper board. 4 | 5 | Minesweeper is a popular game where the user has to find the mines using 6 | numeric hints that indicate how many mines are directly adjacent 7 | (horizontally, vertically, diagonally) to a square. 8 | 9 | In this exercise you have to create some code that counts the number of 10 | mines adjacent to a square and transforms boards like this (where `*` 11 | indicates a mine): 12 | 13 | +-----+ 14 | | * * | 15 | | * | 16 | | * | 17 | | | 18 | +-----+ 19 | 20 | into this: 21 | 22 | +-----+ 23 | |1*3*1| 24 | |13*31| 25 | | 2*2 | 26 | | 111 | 27 | +-----+ 28 | 29 | ## Setup 30 | 31 | Go through the setup instructions for JavaScript to install the 32 | necessary dependencies: 33 | 34 | http://exercism.io/languages/javascript/installation 35 | 36 | ## Running the test suite 37 | 38 | The provided test suite uses [Jasmine](https://jasmine.github.io/). 39 | You can install it by opening a terminal window and running the 40 | following command: 41 | 42 | ```sh 43 | npm install -g jasmine 44 | ``` 45 | 46 | Run the test suite from the exercise directory with: 47 | 48 | ```sh 49 | jasmine minesweeper.spec.js 50 | ``` 51 | 52 | In many test suites all but the first test have been marked "pending". 53 | Once you get a test passing, activate the next one by changing `xit` to `it`. 54 | 55 | ## Submitting Incomplete Solutions 56 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 57 | -------------------------------------------------------------------------------- /circular-buffer/circular-buffer.js: -------------------------------------------------------------------------------- 1 | var circularBuffer = function (size) { 2 | /* A circular buffer */ 3 | var buffer = []; 4 | 5 | return { 6 | read: function() { 7 | /* Reads data from the buffer */ 8 | if(buffer.length <= 0) 9 | throw new bufferEmptyException(); 10 | return buffer.shift(); 11 | }, 12 | 13 | write: function(data) { 14 | /* Writes data to the buffer */ 15 | if(buffer.length >= size) 16 | throw new bufferFullException(); 17 | if(data) 18 | buffer.push(data); 19 | }, 20 | 21 | /* Clears the buffer */ 22 | clear: function() { buffer = [] }, 23 | 24 | forceWrite: function(data) { 25 | /* Writes data to the buffer even if it will overwrite data */ 26 | if(buffer.length >= size) 27 | buffer.shift(); 28 | buffer.push(data); 29 | }, 30 | }; 31 | }; 32 | 33 | var bufferEmptyException = function() { 34 | /* Buffer is empty */ 35 | return { 36 | name: "Buffer Empty", 37 | message: "Buffer is empty there is no data to read.", 38 | toString: function(){return this.name + ": " + this.message;} 39 | }; 40 | }; 41 | 42 | var bufferFullException = function() { 43 | /* Buffer is full */ 44 | return { 45 | name: "Buffer Full", 46 | message: "Buffer is full there is no space to write.", 47 | toString: function(){return this.name + ": " + this.message;} 48 | }; 49 | }; 50 | 51 | module.exports = { 52 | circularBuffer: circularBuffer, 53 | bufferEmptyException: bufferEmptyException, 54 | bufferFullException: bufferFullException, 55 | }; 56 | -------------------------------------------------------------------------------- /react/README.md: -------------------------------------------------------------------------------- 1 | # React 2 | 3 | Implement a basic reactive system. 4 | 5 | Reactive programming is a programming paradigm that focuses on how values 6 | are computed in terms of each other to allow a change to one value to 7 | automatically propagate to other values, like in a spreadsheet. 8 | 9 | Implement a basic reactive system with cells with settable values ("input" 10 | cells) and cells with values computed in terms of other cells ("compute" 11 | cells). Implement updates so that when an input value is changed, values 12 | propagate to reach a new stable system state. 13 | 14 | In addition, compute cells should allow for registering change notification 15 | callbacks. Call a cell’s callbacks when the cell’s value in a new stable 16 | state has changed from the previous stable state. 17 | 18 | ## Setup 19 | 20 | Go through the setup instructions for Javascript to 21 | install the necessary dependencies: 22 | 23 | [https://exercism.io/tracks/javascript/installation](https://exercism.io/tracks/javascript/installation) 24 | 25 | ## Requirements 26 | 27 | Install assignment dependencies: 28 | 29 | ```bash 30 | $ npm install 31 | ``` 32 | 33 | ## Making the test suite pass 34 | 35 | Execute the tests with: 36 | 37 | ```bash 38 | $ npm test 39 | ``` 40 | 41 | In the test suites all tests but the first have been skipped. 42 | 43 | Once you get a test passing, you can enable the next one by 44 | changing `xtest` to `test`. 45 | 46 | 47 | ## Submitting Incomplete Solutions 48 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 49 | -------------------------------------------------------------------------------- /react/react.js: -------------------------------------------------------------------------------- 1 | let locked = false; 2 | let callbackQueue = {}; 3 | 4 | export class InputCell { 5 | constructor(value) { 6 | this.value = value; 7 | this.updateCells = []; 8 | this.callbacks = {}; 9 | } 10 | setValue(value) { 11 | if (this.value != value) { 12 | if (!locked) { 13 | this.locked = true; 14 | locked = true; 15 | } 16 | 17 | this.value = value; 18 | for (let cell of this.updateCells) { 19 | cell.setValue(cell.func(cell.cells)); 20 | } 21 | for (let key of Object.keys(this.callbacks)) { 22 | callbackQueue[key] = () => this.callbacks[key].values.push(value); 23 | } 24 | 25 | if (this.locked) { 26 | for (let key of Object.keys(callbackQueue)) { 27 | callbackQueue[key](); 28 | } 29 | callbackQueue = {}; 30 | this.locked = false; 31 | locked = false 32 | } 33 | } 34 | } 35 | addCallback(callback) { 36 | this.callbacks[callback.id] = callback; 37 | } 38 | removeCallback(callback) { 39 | delete this.callbacks[callback.id]; 40 | } 41 | } 42 | 43 | export class ComputeCell extends InputCell { 44 | constructor(cells, func) { 45 | super(func(cells)); 46 | this.func = func; 47 | this.cells = cells; 48 | for (let cell of cells) { 49 | cell.updateCells.push(this); 50 | } 51 | } 52 | } 53 | 54 | var callbackIDs = 0; 55 | 56 | export class CallbackCell { 57 | constructor(func) { 58 | this.values = []; 59 | this.id = callbackIDs; 60 | callbackIDs += 1; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /scrabble-score/README.md: -------------------------------------------------------------------------------- 1 | # Scrabble Score 2 | 3 | Write a program that, given a word, computes the scrabble score for that word. 4 | 5 | ## Letter Values 6 | 7 | You'll need these: 8 | 9 | ```plain 10 | Letter Value 11 | A, E, I, O, U, L, N, R, S, T 1 12 | D, G 2 13 | B, C, M, P 3 14 | F, H, V, W, Y 4 15 | K 5 16 | J, X 8 17 | Q, Z 10 18 | ``` 19 | 20 | ## Examples 21 | "cabbage" should be scored as worth 14 points: 22 | 23 | - 3 points for C 24 | - 1 point for A, twice 25 | - 3 points for B, twice 26 | - 2 points for G 27 | - 1 point for E 28 | 29 | And to total: 30 | 31 | - `3 + 2*1 + 2*3 + 2 + 1` 32 | - = `3 + 2 + 6 + 3` 33 | - = `5 + 9` 34 | - = 14 35 | 36 | ## Extensions 37 | - You can play a `:double` or a `:triple` letter. 38 | - You can play a `:double` or a `:triple` word. 39 | 40 | ## Setup 41 | 42 | Go through the setup instructions for JavaScript to 43 | install the necessary dependencies: 44 | 45 | http://help.exercism.io/getting-started-with-javascript.html 46 | 47 | ## Making the Test Suite Pass 48 | 49 | Execute the tests with: 50 | 51 | ```bash 52 | $ jasmine-node . 53 | ``` 54 | 55 | In many test suites all but the first test have been skipped. 56 | 57 | Once you get a test passing, you can unskip the next one by 58 | changing `xit` to `it`. 59 | 60 | 61 | ## Source 62 | 63 | Inspired by the Extreme Startup game [view source](https://github.com/rchatley/extreme_startup) 64 | -------------------------------------------------------------------------------- /forth/README.md: -------------------------------------------------------------------------------- 1 | # Forth 2 | 3 | Implement an evaluator for a very simple subset of Forth. 4 | 5 | [Forth](https://en.wikipedia.org/wiki/Forth_%28programming_language%29) 6 | is a stack-based programming language. Implement a very basic evaluator 7 | for a small subset of Forth. 8 | 9 | Your evaluator has to support the following words: 10 | 11 | - `+`, `-`, `*`, `/` (integer arithmetic) 12 | - `DUP`, `DROP`, `SWAP`, `OVER` (stack manipulation) 13 | 14 | Your evaluator also has to support defining new words using the 15 | customary syntax: `: word-name definition ;`. 16 | 17 | To keep things simple the only data type you need to support is signed 18 | integers of at least 16 bits size. 19 | 20 | You should use the following rules for the syntax: a number is a 21 | sequence of one or more (ASCII) digits, a word is a sequence of one or 22 | more letters, digits, symbols or punctuation that is not a number. 23 | (Forth probably uses slightly different rules, but this is close 24 | enough.) 25 | 26 | Words are case-insensitive. 27 | 28 | ## Setup 29 | 30 | Go through the setup instructions for ECMAScript to 31 | install the necessary dependencies: 32 | 33 | http://exercism.io/languages/ecmascript 34 | 35 | ## Requirements 36 | 37 | Install assignment dependencies: 38 | 39 | ```bash 40 | $ npm install 41 | ``` 42 | 43 | ## Making the test suite pass 44 | 45 | Execute the tests with: 46 | 47 | ```bash 48 | $ npm test 49 | ``` 50 | 51 | In the test suites all tests but the first have been skipped. 52 | 53 | Once you get a test passing, you can enable the next one by 54 | changing `xtest` to `test`. 55 | -------------------------------------------------------------------------------- /space-age/README.md: -------------------------------------------------------------------------------- 1 | # Space Age 2 | 3 | Write a program that, given an age in seconds, calculates how old someone is in terms of a given planet's solar years. 4 | 5 | Given an age in seconds, calculate how old someone would be on: 6 | 7 | - Earth: orbital period 365.25 Earth days, or 31557600 seconds 8 | - Mercury: orbital period 0.2408467 Earth years 9 | - Venus: orbital period 0.61519726 Earth years 10 | - Mars: orbital period 1.8808158 Earth years 11 | - Jupiter: orbital period 11.862615 Earth years 12 | - Saturn: orbital period 29.447498 Earth years 13 | - Uranus: orbital period 84.016846 Earth years 14 | - Neptune: orbital period 164.79132 Earth years 15 | 16 | So if you were told someone were 1,000,000,000 seconds old, you should 17 | be able to say that they're 31 Earth-years old. 18 | 19 | If you're wondering why Pluto didn't make the cut, go watch [this 20 | youtube video](http://www.youtube.com/watch?v=Z_2gbGXzFbs). 21 | 22 | ## Setup 23 | 24 | Go through the setup instructions for JavaScript to 25 | install the necessary dependencies: 26 | 27 | http://help.exercism.io/getting-started-with-javascript.html 28 | 29 | ## Making the Test Suite Pass 30 | 31 | Execute the tests with: 32 | 33 | ```bash 34 | $ jasmine-node . 35 | ``` 36 | 37 | In many test suites all but the first test have been skipped. 38 | 39 | Once you get a test passing, you can unskip the next one by 40 | changing `xit` to `it`. 41 | 42 | 43 | ## Source 44 | 45 | Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial. [view source](http://pine.fm/LearnToProgram/?Chapter=01) 46 | -------------------------------------------------------------------------------- /rectangles/README.md: -------------------------------------------------------------------------------- 1 | # Rectangles 2 | 3 | Count the rectangles in an ASCII diagram like the one below. 4 | 5 | ```text 6 | +--+ 7 | ++ | 8 | +-++--+ 9 | | | | 10 | +--+--+ 11 | ``` 12 | 13 | The above diagram contains 6 rectangles: 14 | 15 | ```text 16 | 17 | 18 | +-----+ 19 | | | 20 | +-----+ 21 | ``` 22 | 23 | ```text 24 | +--+ 25 | | | 26 | | | 27 | | | 28 | +--+ 29 | ``` 30 | 31 | ```text 32 | +--+ 33 | | | 34 | +--+ 35 | 36 | 37 | ``` 38 | 39 | ```text 40 | 41 | 42 | +--+ 43 | | | 44 | +--+ 45 | ``` 46 | 47 | ```text 48 | 49 | 50 | +--+ 51 | | | 52 | +--+ 53 | ``` 54 | 55 | ```text 56 | 57 | ++ 58 | ++ 59 | 60 | 61 | ``` 62 | 63 | You may assume that the input is always a proper rectangle (i.e. the length of 64 | every line equals the length of the first line). 65 | 66 | ## Setup 67 | 68 | Go through the setup instructions for JavaScript to 69 | install the necessary dependencies: 70 | 71 | http://exercism.io/languages/javascript 72 | 73 | ## Making the Test Suite Pass 74 | 75 | Execute the tests with: 76 | 77 | jasmine .spec.js 78 | 79 | Replace `` with the name of the current exercise. E.g., to 80 | test the Hello World exercise: 81 | 82 | jasmine hello-world.spec.js 83 | 84 | In many test suites all but the first test have been skipped. 85 | 86 | Once you get a test passing, you can unskip the next one by 87 | changing `xit` to `it`. 88 | 89 | 90 | ## Submitting Incomplete Solutions 91 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 92 | -------------------------------------------------------------------------------- /hexadecimal/hexadecimal.spec.js: -------------------------------------------------------------------------------- 1 | var Hexadecimal = require('./hexadecimal'); 2 | 3 | describe('Hexadecimal', function() { 4 | 5 | it('hex 1 is decimal 1', function() { 6 | var hex = new Hexadecimal('1'); 7 | expect(hex.toDecimal()).toEqual(1); 8 | }); 9 | 10 | it('hex c is decimal 12', function() { 11 | var hex = new Hexadecimal('c'); 12 | expect(hex.toDecimal()).toEqual(12); 13 | }); 14 | 15 | it('hex 10 is decimal 16', function() { 16 | var hex = new Hexadecimal('10'); 17 | expect(hex.toDecimal()).toEqual(16); 18 | }); 19 | 20 | it('hex af is decimal 175', function() { 21 | var hex = new Hexadecimal('af'); 22 | expect(hex.toDecimal()).toEqual(175); 23 | }); 24 | 25 | it('hex 100 is decimal 256', function() { 26 | var hex = new Hexadecimal('100'); 27 | expect(hex.toDecimal()).toEqual(256); 28 | }); 29 | 30 | it('hex 19ace is decimal 105166', function() { 31 | var hex = new Hexadecimal('19ace'); 32 | expect(hex.toDecimal()).toEqual(105166); 33 | }); 34 | 35 | it('invalid hex is decimal 0', function() { 36 | var hex = new Hexadecimal('carrot'); 37 | expect(hex.toDecimal()).toEqual(0); 38 | }); 39 | 40 | it('black', function() { 41 | var hex = new Hexadecimal('000000'); 42 | expect(hex.toDecimal()).toEqual(0); 43 | }); 44 | 45 | it('white', function() { 46 | var hex = new Hexadecimal('ffffff'); 47 | expect(hex.toDecimal()).toEqual(16777215); 48 | }); 49 | 50 | it('yellow', function() { 51 | var hex = new Hexadecimal('ffff00'); 52 | expect(hex.toDecimal()).toEqual(16776960); 53 | }); 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /say/say.js: -------------------------------------------------------------------------------- 1 | var ones = ",one,two,three,four,five,six,seven,eight,nine".split(','); 2 | var teens = ",eleven,twelve,thirteen,fourteen,fifteen,sixteen,seventeen,eighteen,nineteen".split(','); 3 | var tens = ",,twenty,thirty,forty,fifty,sixty,seventy,eighty,ninty".split(','); 4 | var powers = ", thousand, million, billion".split(','); 5 | 6 | function inEnglish(num) { 7 | /* A number in english */ 8 | if(0 > num || num >= 1000000000000) 9 | throw new Error('Number must be between 0 and 999,999,999,999.'); 10 | 11 | if(num == 0) 12 | return "zero"; 13 | 14 | var digit_groups = []; 15 | while(0 < num) { 16 | digit_groups.push(num % 1000); 17 | num = Math.floor(num / 1000); 18 | } 19 | 20 | return digit_groups.reduce(function(english, group, power) { 21 | if(group == 0) 22 | return english; 23 | return englishPower(group) + powers[power] + (english?" ":'') + english; 24 | }, ''); 25 | } 26 | 27 | function englishPower(digits) { 28 | /* Say numbers between 0 and 1000 in english (exclusive) */ 29 | digits = ("00" + digits).slice(-3).split('').map(function(e) {return parseInt(e)}); 30 | var english = ""; 31 | 32 | // hundereds 33 | if(digits[0] != 0) 34 | english += ones[digits[0]] + " hundred"; 35 | 36 | // tens, teens are specual cases 37 | if(digits[1] == 1) 38 | return english + (english?' ':'') + teens[digits[2]]; 39 | else if(digits[1] != 0) 40 | english += (english?' ':'') + tens[digits[1]]; 41 | 42 | // ones 43 | if(digits[2] != 0) 44 | english += (english?digits[1]?'-':' ':'') + ones[digits[2]]; 45 | 46 | return english; 47 | } 48 | 49 | module.exports = {inEnglish: inEnglish} 50 | -------------------------------------------------------------------------------- /armstrong-numbers/README.md: -------------------------------------------------------------------------------- 1 | # Armstrong Numbers 2 | 3 | An [Armstrong number](https://en.wikipedia.org/wiki/Narcissistic_number) is a number that is the sum of its own digits each raised to the power of the number of digits. 4 | 5 | For example: 6 | 7 | - 9 is an Armstrong number, because `9 = 9^1 = 9` 8 | - 10 is *not* an Armstrong number, because `10 != 1^2 + 0^2 = 2` 9 | - 153 is an Armstrong number, because: `153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153` 10 | - 154 is *not* an Armstrong number, because: `154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190` 11 | 12 | Write some code to determine whether a number is an Armstrong number. 13 | 14 | ## Setup 15 | 16 | Go through the setup instructions for JavaScript to install the 17 | necessary dependencies: 18 | 19 | http://exercism.io/languages/javascript/installation 20 | 21 | ## Running the test suite 22 | 23 | The provided test suite uses [Jasmine](https://jasmine.github.io/). 24 | You can install it by opening a terminal window and running the 25 | following command: 26 | 27 | ```sh 28 | npm install -g jasmine 29 | ``` 30 | 31 | Run the test suite from the exercise directory with: 32 | 33 | ```sh 34 | jasmine armstrong-numbers.spec.js 35 | ``` 36 | 37 | In many test suites all but the first test have been marked "pending". 38 | Once you get a test passing, activate the next one by changing `xit` to `it`. 39 | 40 | ## Source 41 | 42 | Wikipedia [Narcissistic number](https://en.wikipedia.org/wiki/Narcissistic_number) 43 | 44 | ## Submitting Incomplete Solutions 45 | 46 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 47 | -------------------------------------------------------------------------------- /luhn/luhn.spec.js: -------------------------------------------------------------------------------- 1 | var Luhn = require('./luhn'); 2 | 3 | describe('Luhn',function() { 4 | 5 | it('check digit',function() { 6 | var luhn = new Luhn(34567); 7 | expect(luhn.checkDigit).toEqual(7); 8 | }); 9 | 10 | it('check digit again',function() { 11 | var luhn = new Luhn(91370); 12 | expect(luhn.checkDigit).toEqual(0); 13 | }); 14 | 15 | it('addends',function() { 16 | var luhn = new Luhn(12121); 17 | expect(luhn.addends).toEqual([1, 4, 1, 4, 1]); 18 | }); 19 | 20 | it('too large added',function() { 21 | var luhn = new Luhn(8631); 22 | expect(luhn.addends).toEqual([7, 6, 6, 1]); 23 | }); 24 | 25 | it('checksum',function() { 26 | var luhn = new Luhn(4913); 27 | expect(luhn.checksum).toEqual(22); 28 | }); 29 | 30 | it('checksum again',function() { 31 | var luhn = new Luhn(201773); 32 | expect(luhn.checksum).toEqual(21); 33 | }); 34 | 35 | it('invalid number',function() { 36 | var luhn = new Luhn(738); 37 | expect(luhn.valid).toEqual(false); 38 | }); 39 | 40 | it('invalid number',function() { 41 | var luhn = new Luhn(8739567); 42 | expect(luhn.valid).toEqual(true); 43 | }); 44 | 45 | it('create valid number',function() { 46 | var number = Luhn.create(123); 47 | expect(number).toEqual(1230); 48 | }); 49 | 50 | it('create other valid number',function() { 51 | var number = Luhn.create(873956); 52 | expect(number).toEqual(8739567); 53 | }); 54 | 55 | it('create yet another valid number',function() { 56 | var number = Luhn.create(837263756); 57 | expect(number).toEqual(8372637564); 58 | }); 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /bob/README.md: -------------------------------------------------------------------------------- 1 | # Bob 2 | 3 | Bob is a lackadaisical teenager. In conversation, his responses are very limited. 4 | 5 | Bob answers 'Sure.' if you ask him a question. 6 | 7 | He answers 'Whoa, chill out!' if you yell at him. 8 | 9 | He says 'Fine. Be that way!' if you address him without actually saying 10 | anything. 11 | 12 | He answers 'Whatever.' to anything else. 13 | 14 | ## Instructions 15 | 16 | Run the test file, and fix each of the errors in turn. When you get the 17 | first test to pass, go to the first pending or skipped test, and make 18 | that pass as well. When all of the tests are passing, feel free to 19 | submit. 20 | 21 | Remember that passing code is just the first step. The goal is to work 22 | towards a solution that is as readable and expressive as you can make 23 | it. 24 | 25 | Please make your solution as general as possible. Good code doesn't just 26 | pass the test suite, it works with any input that fits the 27 | specification. 28 | 29 | Have fun! 30 | 31 | 32 | ## Setup 33 | 34 | Go through the setup instructions for JavaScript to 35 | install the necessary dependencies: 36 | 37 | http://help.exercism.io/getting-started-with-javascript.html 38 | 39 | ## Making the Test Suite Pass 40 | 41 | Execute the tests with: 42 | 43 | ```bash 44 | $ jasmine-node . 45 | ``` 46 | 47 | In many test suites all but the first test have been skipped. 48 | 49 | Once you get a test passing, you can unskip the next one by 50 | changing `xit` to `it`. 51 | 52 | 53 | ## Source 54 | 55 | Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. [view source](http://pine.fm/LearnToProgram/?Chapter=06) 56 | -------------------------------------------------------------------------------- /grains/README.md: -------------------------------------------------------------------------------- 1 | # Grains 2 | 3 | Write a program that calculates the number of grains of wheat on a chessboard given that the number on each square doubles. 4 | 5 | There once was a wise servant who saved the life of a prince. The king 6 | promised to pay whatever the servant could dream up. Knowing that the 7 | king loved chess, the servant told the king he would like to have grains 8 | of wheat. One grain on the first square of a chess board. Two grains on 9 | the next. Four on the third, and so on. 10 | 11 | There are 64 squares on a chessboard. 12 | 13 | Write a program that shows: 14 | - how many grains were on each square, and 15 | - the total number of grains 16 | 17 | 18 | ## For bonus points 19 | 20 | Did you get the tests passing and the code clean? If you want to, these 21 | are some additional things you could try: 22 | 23 | - Optimize for speed. 24 | - Optimize for readability. 25 | 26 | Then please share your thoughts in a comment on the submission. Did this 27 | experiment make the code better? Worse? Did you learn anything from it? 28 | 29 | ## Setup 30 | 31 | Go through the setup instructions for JavaScript to 32 | install the necessary dependencies: 33 | 34 | http://help.exercism.io/getting-started-with-javascript.html 35 | 36 | ## Making the Test Suite Pass 37 | 38 | Execute the tests with: 39 | 40 | ```bash 41 | $ jasmine-node . 42 | ``` 43 | 44 | In many test suites all but the first test have been skipped. 45 | 46 | Once you get a test passing, you can unskip the next one by 47 | changing `xit` to `it`. 48 | 49 | 50 | ## Source 51 | 52 | JavaRanch Cattle Drive, exercise 6 [view source](http://www.javaranch.com/grains.jsp) 53 | -------------------------------------------------------------------------------- /secret-handshake/secret-handshake.spec.js: -------------------------------------------------------------------------------- 1 | var SecretHandshake = require('./secret-handshake'); 2 | 3 | describe('Secret Handshake', function() { 4 | it('1 is a wink', function() { 5 | var handshake = new SecretHandshake(1); 6 | expect(handshake.commands()).toEqual(['wink']); 7 | }); 8 | 9 | it('10 is a double blink', function() { 10 | var handshake = new SecretHandshake(2); 11 | expect(handshake.commands()).toEqual(['double blink']); 12 | }); 13 | 14 | it('100 is close your eyes', function() { 15 | var handshake = new SecretHandshake(4); 16 | expect(handshake.commands()).toEqual(['close your eyes']); 17 | }); 18 | 19 | it('1000 is jump', function() { 20 | var handshake = new SecretHandshake(8); 21 | expect(handshake.commands()).toEqual(['jump']); 22 | }); 23 | 24 | it('11 is wink and double blink', function() { 25 | var handshake = new SecretHandshake(3); 26 | expect(handshake.commands()).toEqual(['wink','double blink']); 27 | }); 28 | 29 | it('10011 is double blink and wink', function() { 30 | var handshake = new SecretHandshake(19); 31 | expect(handshake.commands()).toEqual(['double blink','wink']); 32 | }); 33 | 34 | it('11111 is jump, close your eyes, double blink, and wink', function() { 35 | var handshake = new SecretHandshake(31); 36 | expect(handshake.commands()).toEqual(['jump','close your eyes','double blink','wink']); 37 | }); 38 | 39 | it('text is an invalid secret handshake', function() { 40 | expect( function () { 41 | var handshake = new SecretHandshake('piggies'); 42 | }).toThrow(new Error('Handshake must be a number')); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /spiral-matrix/spiral-matrix.spec.js: -------------------------------------------------------------------------------- 1 | import SpiralMatrix from './spiral-matrix'; 2 | 3 | describe('Spiral Matrix', () => { 4 | test('empty spiral', () => { 5 | const expected = []; 6 | const actual = SpiralMatrix.ofSize(0); 7 | 8 | expect(actual).toEqual(expected); 9 | }); 10 | 11 | test('trivial spiral', () => { 12 | const expected = [[1]]; 13 | const actual = SpiralMatrix.ofSize(1); 14 | 15 | expect(actual).toEqual(expected); 16 | }); 17 | 18 | test('spiral of size 2', () => { 19 | const expected = [[1, 2], 20 | [4, 3]]; 21 | const actual = SpiralMatrix.ofSize(2); 22 | 23 | expect(actual).toEqual(expected); 24 | }); 25 | 26 | test('spiral of size 3', () => { 27 | const expected = [[1, 2, 3], 28 | [8, 9, 4], 29 | [7, 6, 5]]; 30 | const actual = SpiralMatrix.ofSize(3); 31 | 32 | expect(actual).toEqual(expected); 33 | }); 34 | 35 | test('spiral of size 4', () => { 36 | const expected = [[1, 2, 3, 4], 37 | [12, 13, 14, 5], 38 | [11, 16, 15, 6], 39 | [10, 9, 8, 7]]; 40 | const actual = SpiralMatrix.ofSize(4); 41 | 42 | expect(actual).toEqual(expected); 43 | }); 44 | 45 | test('spiral of size 5', () => { 46 | const expected = [[1, 2, 3, 4, 5], 47 | [16, 17, 18, 19, 6], 48 | [15, 24, 25, 20, 7], 49 | [14, 23, 22, 21, 8], 50 | [13, 12, 11, 10, 9]]; 51 | const actual = SpiralMatrix.ofSize(5); 52 | 53 | expect(expected).toEqual(actual); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /rational-numbers/rational-numbers.js: -------------------------------------------------------------------------------- 1 | var gcd = function(num, denom) { 2 | var a = Math.max(Math.abs(num), Math.abs(denom)); 3 | var b = Math.min(Math.abs(num), Math.abs(denom)); 4 | var tmp; 5 | while(b != 0) { 6 | tmp = b; 7 | b = a % tmp; 8 | a = tmp; 9 | } 10 | return a; 11 | } 12 | 13 | class Rational { 14 | constructor(num, denom) { 15 | var divisor = gcd(num, denom); 16 | if(num < 0) 17 | divisor *= -1; 18 | this.num = num / divisor; 19 | this.denom = (num == 0 && denom < 0 ? -1 : 1) * denom / divisor; 20 | } 21 | 22 | add(rational) { 23 | return new Rational( 24 | this.num * rational.denom + this.denom * rational.num, 25 | this.denom * rational.denom); 26 | } 27 | 28 | sub(rational) { 29 | return new Rational( 30 | this.num * rational.denom - this.denom * rational.num, 31 | this.denom * rational.denom); 32 | } 33 | 34 | mul(rational) { 35 | return new Rational( this.num * rational.num, this.denom * rational.denom); 36 | } 37 | 38 | div(rational) { 39 | return new Rational(this.num * rational.denom, this.denom * rational.num); 40 | } 41 | 42 | abs() { 43 | return new Rational(Math.abs(this.num), Math.abs(this.denom)); 44 | } 45 | 46 | exprational(exp) { 47 | return new Rational(Math.pow(this.num, exp), Math.pow(this.denom, exp)); 48 | } 49 | 50 | expreal(base) { 51 | return Math.pow(base, this.num / this.denom); 52 | } 53 | 54 | reduce() { 55 | return this; 56 | } 57 | } 58 | 59 | module.exports = Rational; 60 | -------------------------------------------------------------------------------- /rectangles/rectangles.js: -------------------------------------------------------------------------------- 1 | function is_rectangle(diagram, top_left, bottom_right) { 2 | if(diagram[top_left.row][top_left.col] != '+' || 3 | diagram[bottom_right.row][bottom_right.col] != '+' || 4 | diagram[bottom_right.row][top_left.col] != '+' || 5 | diagram[top_left.row][bottom_right.col] != '+') 6 | return false; 7 | for(var row = top_left.row; row <= bottom_right.row; row++) { 8 | if(!(diagram[row][top_left.col] == '|' || 9 | diagram[row][top_left.col] == '+') || 10 | !(diagram[row][bottom_right.col] == '|' || 11 | diagram[row][bottom_right.col] == '+')) 12 | return false; 13 | } 14 | for(var col = top_left.col; col <= bottom_right.col; col++) { 15 | if(!(diagram[top_left.row][col] == '-' || 16 | diagram[top_left.row][col] == '+') || 17 | !(diagram[bottom_right.row][col] == '-' || 18 | diagram[bottom_right.row][col] == '+')) 19 | return false; 20 | } 21 | return true; 22 | } 23 | 24 | module.exports = function(diagram) { 25 | var count = 0; 26 | for(var r = 0; r < diagram.length; r++) { 27 | for(var c = 0; c < diagram[r].length; c++) { 28 | if(diagram[r][c] == '+') { 29 | for(var i = r + 1; i < diagram.length; i++) { 30 | for(var j = c + 1; j < diagram[r].length; j++) { 31 | if(is_rectangle(diagram, {row: r, col: c}, {row: i, col: j})) { 32 | count += 1 33 | } 34 | } 35 | } 36 | } 37 | } 38 | } 39 | return count; 40 | } 41 | -------------------------------------------------------------------------------- /alphametics/README.md: -------------------------------------------------------------------------------- 1 | # Alphametics 2 | 3 | Write a function to solve alphametics puzzles. 4 | 5 | [Alphametics](https://en.wikipedia.org/wiki/Alphametics) is a puzzle where 6 | letters in words are replaced with numbers. 7 | 8 | For example `SEND + MORE = MONEY`: 9 | 10 | ```text 11 | S E N D 12 | M O R E + 13 | ----------- 14 | M O N E Y 15 | ``` 16 | 17 | Replacing these with valid numbers gives: 18 | 19 | ```text 20 | 9 5 6 7 21 | 1 0 8 5 + 22 | ----------- 23 | 1 0 6 5 2 24 | ``` 25 | 26 | This is correct because every letter is replaced by a different number and the 27 | words, translated into numbers, then make a valid sum. 28 | 29 | Each letter must represent a different digit, and the leading digit of 30 | a multi-digit number must not be zero. 31 | 32 | Write a function to solve alphametics puzzles. 33 | 34 | ## Setup 35 | 36 | Go through the setup instructions for JavaScript to install the 37 | necessary dependencies: 38 | 39 | http://exercism.io/languages/javascript/installation 40 | 41 | ## Running the test suite 42 | 43 | The provided test suite uses [Jasmine](https://jasmine.github.io/). 44 | You can install it by opening a terminal window and running the 45 | following command: 46 | 47 | ```sh 48 | npm install -g jasmine 49 | ``` 50 | 51 | Run the test suite from the exercise directory with: 52 | 53 | ```sh 54 | jasmine alphametics.spec.js 55 | ``` 56 | 57 | In many test suites all but the first test have been marked "pending". 58 | Once you get a test passing, activate the next one by changing `xit` to `it`. 59 | 60 | ## Submitting Incomplete Solutions 61 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 62 | -------------------------------------------------------------------------------- /binary/README.md: -------------------------------------------------------------------------------- 1 | # Binary 2 | 3 | Write a program that will convert a binary number, represented as a string (e.g. '101010'), to its decimal equivalent using first principles 4 | 5 | Implement binary to decimal conversion. Given a binary input 6 | string, your program should produce a decimal output. 7 | 8 | ## Note 9 | - Implement the conversion yourself. 10 | Do not use something else to perform the conversion for you. 11 | - Treat invalid input as binary 0. 12 | 13 | ## About Binary (Base-2) 14 | Decimal is a base-10 system. 15 | 16 | A number 23 in base 10 notation can be understood 17 | as a linear combination of powers of 10: 18 | 19 | - The rightmost digit gets multiplied by 10^0 = 1 20 | - The next number gets multiplied by 10^1 = 10 21 | - ... 22 | - The *n*th number gets multiplied by 10^*(n-1)*. 23 | - All these values are summed. 24 | 25 | So: `23 => 2*10^1 + 3*10^0 => 2*10 + 3*1 = 23 base 10` 26 | 27 | Binary is similar, but uses powers of 2 rather than powers of 10. 28 | 29 | So: `101 => 1*2^2 + 0*2^1 + 1*2^0 => 1*4 + 0*2 + 1*1 => 4 + 1 => 5 base 10`. 30 | 31 | ## Setup 32 | 33 | Go through the setup instructions for JavaScript to 34 | install the necessary dependencies: 35 | 36 | http://help.exercism.io/getting-started-with-javascript.html 37 | 38 | ## Making the Test Suite Pass 39 | 40 | Execute the tests with: 41 | 42 | ```bash 43 | $ jasmine-node . 44 | ``` 45 | 46 | In many test suites all but the first test have been skipped. 47 | 48 | Once you get a test passing, you can unskip the next one by 49 | changing `xit` to `it`. 50 | 51 | 52 | ## Source 53 | 54 | All of Computer Science [view source](http://www.wolframalpha.com/input/?i=binary&a=*C.binary-_*MathWorld-) 55 | -------------------------------------------------------------------------------- /pig-latin/pig-latin.spec.js: -------------------------------------------------------------------------------- 1 | var pigLatin = require('./pig-latin'); 2 | 3 | describe('pigLatin', function () { 4 | 5 | it('translates a word beginning with a', function () { 6 | expect(pigLatin.translate('apple')).toEqual('appleay'); 7 | }); 8 | 9 | it('translates a word beginning with e', function () { 10 | expect(pigLatin.translate('ear')).toEqual('earay'); 11 | }); 12 | 13 | it('translates a word beginning with p', function () { 14 | expect(pigLatin.translate('pig')).toEqual('igpay'); 15 | }); 16 | 17 | it('translates a word beginning with k', function () { 18 | expect(pigLatin.translate('koala')).toEqual('oalakay'); 19 | }); 20 | 21 | it('translates a word beginning with ch', function () { 22 | expect(pigLatin.translate('chair')).toEqual('airchay'); 23 | }); 24 | 25 | it('translates a word beginning with qu', function () { 26 | expect(pigLatin.translate('queen')).toEqual('eenquay'); 27 | }); 28 | 29 | it('translates a word with a consonant preceding qu', function () { 30 | expect(pigLatin.translate('square')).toEqual('aresquay'); 31 | }); 32 | 33 | it('translates a word beginning with th', function () { 34 | expect(pigLatin.translate('therapy')).toEqual('erapythay'); 35 | }); 36 | 37 | it('translates a word beginning with thr', function () { 38 | expect(pigLatin.translate('thrush')).toEqual('ushthray'); 39 | }); 40 | 41 | it('translates a word beginning with sch', function () { 42 | expect(pigLatin.translate('school')).toEqual('oolschay'); 43 | }); 44 | 45 | it('translates a phrase', function () { 46 | expect(pigLatin.translate('quick fast run')) 47 | .toEqual('ickquay astfay unray'); 48 | }); 49 | 50 | }); 51 | -------------------------------------------------------------------------------- /food-chain/food-chain.js: -------------------------------------------------------------------------------- 1 | var FoodChain = function() {}; 2 | 3 | FoodChain.prototype.verse = function( verseNum ) { 4 | /* Sing a verse of a song */ 5 | var verse = lines.opening + lines.first[verseNum] 6 | // She's dead of course 7 | if (verseNum >= 8) { return verse } 8 | return verse + lines.chorus.slice(0, verseNum).reverse().join(''); 9 | }; 10 | 11 | FoodChain.prototype.verses = function(stop, start) { 12 | /* Sings a set of verses of a song */ 13 | var verses = ''; 14 | for(var i = stop; i <= start; i++) { 15 | verses += this.verse(i) + "\n"; 16 | } 17 | return verses; 18 | }; 19 | 20 | var lines = { 21 | /* The lines for the song "I know an old lady who swallowed a fly" */ 22 | opening : "I know an old lady who swallowed ", 23 | first : [ "", 24 | "a fly.\n", 25 | "a spider.\nIt wriggled and jiggled and tickled inside her.\n", 26 | "a bird.\nHow absurd to swallow a bird!\n", 27 | "a cat.\nImagine that, to swallow a cat!\n", 28 | "a dog.\nWhat a hog, to swallow a dog!\n", 29 | "a goat.\nJust opened her throat and swallowed a goat!\n", 30 | "a cow.\nI don't know how she swallowed a cow!\n", 31 | "a horse.\nShe's dead, of course!\n", 32 | ], 33 | chorus : ["I don't know why she swallowed the fly. Perhaps she'll die.\n", 34 | "She swallowed the spider to catch the fly.\n", 35 | "She swallowed the bird to catch the spider that wriggled and jiggled and tickled inside her.\n", 36 | "She swallowed the cat to catch the bird.\n", 37 | "She swallowed the dog to catch the cat.\n", 38 | "She swallowed the goat to catch the dog.\n", 39 | "She swallowed the cow to catch the goat.\n", 40 | ], 41 | }; 42 | 43 | module.exports = FoodChain; 44 | -------------------------------------------------------------------------------- /sublist/README.md: -------------------------------------------------------------------------------- 1 | # Sublist 2 | 3 | Given two lists determine if the first list is contained within the second 4 | list, if the second list is contained within the first list, if both lists are 5 | contained within each other or if none of these are true. 6 | 7 | Specifically, a list A is a sublist of list B if by dropping 0 or more elements 8 | from the front of B and 0 or more elements from the back of B you get a list 9 | that's completely equal to A. 10 | 11 | Examples: 12 | 13 | * A = [1, 2, 3], B = [1, 2, 3, 4, 5], A is a sublist of B 14 | * A = [3, 4, 5], B = [1, 2, 3, 4, 5], A is a sublist of B 15 | * A = [3, 4], B = [1, 2, 3, 4, 5], A is a sublist of B 16 | * A = [1, 2, 3], B = [1, 2, 3], A is equal to B 17 | * A = [1, 2, 3, 4, 5], B = [2, 3, 4], A is a superlist of B 18 | * A = [1, 2, 4], B = [1, 2, 3, 4, 5], A is not a superlist of, sublist of or equal to B 19 | 20 | ## Setup 21 | 22 | Go through the setup instructions for JavaScript to install the 23 | necessary dependencies: 24 | 25 | http://exercism.io/languages/javascript/installation 26 | 27 | ## Running the test suite 28 | 29 | The provided test suite uses [Jasmine](https://jasmine.github.io/). 30 | You can install it by opening a terminal window and running the 31 | following command: 32 | 33 | ```sh 34 | npm install -g jasmine 35 | ``` 36 | 37 | Run the test suite from the exercise directory with: 38 | 39 | ```sh 40 | jasmine sublist.spec.js 41 | ``` 42 | 43 | In many test suites all but the first test have been marked "pending". 44 | Once you get a test passing, activate the next one by changing `xit` to `it`. 45 | 46 | ## Submitting Incomplete Solutions 47 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 48 | -------------------------------------------------------------------------------- /forth/forth.js: -------------------------------------------------------------------------------- 1 | function n_args(n, func) { 2 | return function(stack) { 3 | if (stack.length < n) 4 | throw new Error("Stack empty"); 5 | let args = stack.splice(stack.length - n, stack.length); 6 | stack.push(...func(...args)); 7 | } 8 | } 9 | 10 | export default class Forth { 11 | constructor() { 12 | this.stack = []; 13 | this.env = {}; 14 | this.builtin = { 15 | "+": n_args(2, (a, b) => [a+b]), 16 | "-": n_args(2, (a, b) => [a-b]), 17 | "*": n_args(2, (a, b) => [a*b]), 18 | "/": n_args(2, (a, b) => { 19 | if(b == 0) 20 | throw new Error("Division by zero"); 21 | return [Math.floor(a/b)]; 22 | }), 23 | "dup": n_args(1, a => [a, a]), 24 | "drop": n_args(1, a => []), 25 | "swap": n_args(2, (a, b) => [b, a]), 26 | "over": n_args(2, (a, b) => [a, b, a]), 27 | } 28 | } 29 | evaluate(line) { 30 | if (line.startsWith(': ') && line.endsWith(' ;')){ 31 | var tokens = line.slice(2, line.length-2).split(" "); 32 | var head = tokens[0].toLowerCase(); 33 | if(String(Number(head)) === head) 34 | throw new Error("Invalid definition"); 35 | this.env[head] = tokens.slice(1); 36 | return; 37 | } 38 | 39 | var tokens = line.split(" "); 40 | while(0 < tokens.length) { 41 | let token = tokens.shift().toLowerCase(); 42 | if(String(Number(token)) === token) { 43 | this.stack.push(parseInt(token)); 44 | } else if(token in this.env) { 45 | tokens.push(...this.env[token]); 46 | } else if(token in this.builtin) { 47 | this.builtin[token](this.stack); 48 | } else { 49 | throw new Error('Unknown command'); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ocr-numbers/ocr-numbers.js: -------------------------------------------------------------------------------- 1 | function convert(OCR_string) { 2 | /* Convert a line representation of digits into a string of digits */ 3 | OCR_lines = OCR_string.split("\n"); 4 | OCR_characters = []; 5 | for(var line = 0; line < OCR_lines.length; line += 4) { 6 | OCR_characters.push(lines_to_characters(OCR_lines.slice(line, line + 4))); 7 | } 8 | 9 | return OCR_characters.map(function(row) { 10 | return row.map(function(character) { 11 | var digit = OCR_digits.indexOf(character); 12 | return digit != -1 ? digit.toString() : "?"; 13 | }).join(""); 14 | }).join(","); 15 | } 16 | 17 | function lines_to_characters(line) { 18 | /* Divides a set of lines into characters */ 19 | var characters = []; 20 | for(var c = 0; c < (line[0].length) / 3; c += 1) { 21 | characters.push( line.map(function(row) { 22 | return row.slice(c * 3, c * 3 + 3); 23 | }) ); 24 | } 25 | return characters.map(function(character) {return character.join("\n")}); 26 | } 27 | 28 | module.exports = {convert: convert}; 29 | 30 | var OCR_digits = [ 31 | " _ \n" + 32 | "| |\n" + 33 | "|_|\n" + 34 | " ", 35 | " \n" + 36 | " |\n" + 37 | " |\n" + 38 | " ", 39 | " _ \n" + 40 | " _|\n" + 41 | "|_ \n" + 42 | " ", 43 | " _ \n" + 44 | " _|\n" + 45 | " _|\n" + 46 | " ", 47 | " \n" + 48 | "|_|\n" + 49 | " |\n" + 50 | " ", 51 | " _ \n" + 52 | "|_ \n" + 53 | " _|\n" + 54 | " ", 55 | " _ \n" + 56 | "|_ \n" + 57 | "|_|\n" + 58 | " ", 59 | " _ \n" + 60 | " |\n" + 61 | " |\n" + 62 | " ", 63 | " _ \n" + 64 | "|_|\n" + 65 | "|_|\n" + 66 | " ", 67 | " _ \n" + 68 | "|_|\n" + 69 | " _|\n" + 70 | " ", 71 | ]; -------------------------------------------------------------------------------- /atbash-cipher/README.md: -------------------------------------------------------------------------------- 1 | # Atbash Cipher 2 | 3 | Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East. 4 | 5 | The Atbash cipher is a simple substitution cipher that relies on 6 | transposing all the letters in the alphabet such that the resulting 7 | alphabet is backwards. The first letter is replaced with the last 8 | letter, the second with the second-last, and so on. 9 | 10 | An Atbash cipher for the Latin alphabet would be as follows: 11 | 12 | ```plain 13 | Plain: abcdefghijklmnopqrstuvwxyz 14 | Cipher: zyxwvutsrqponmlkjihgfedcba 15 | ``` 16 | 17 | It is a very weak cipher because it only has one possible key, and it is 18 | a simple monoalphabetic substitution cipher. However, this may not have 19 | been an issue in the cipher's time. 20 | 21 | Ciphertext is written out in groups of fixed length, the traditional group size 22 | being 5 letters, and punctuation is excluded. This is to make it harder to guess 23 | things based on word boundaries. 24 | 25 | ## Examples 26 | - Encoding `test` gives `gvhg` 27 | - Decoding `gvhg` gives `test` 28 | - Decoding `gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt` gives `The quick brown fox jumps over the lazy dog.` 29 | 30 | ## Setup 31 | 32 | Go through the setup instructions for JavaScript to 33 | install the necessary dependencies: 34 | 35 | http://help.exercism.io/getting-started-with-javascript.html 36 | 37 | ## Making the Test Suite Pass 38 | 39 | Execute the tests with: 40 | 41 | ```bash 42 | $ jasmine-node . 43 | ``` 44 | 45 | In many test suites all but the first test have been skipped. 46 | 47 | Once you get a test passing, you can unskip the next one by 48 | changing `xit` to `it`. 49 | 50 | 51 | ## Source 52 | 53 | Wikipedia [view source](http://en.wikipedia.org/wiki/Atbash) 54 | -------------------------------------------------------------------------------- /space-age/space-age.spec.js: -------------------------------------------------------------------------------- 1 | var SpaceAge = require('./space-age'); 2 | 3 | describe('Space Age', function() { 4 | it('age in seconds', function() { 5 | var age = new SpaceAge(1000000); 6 | expect(age.seconds).toEqual(1000000); 7 | }); 8 | 9 | it('age in earth years', function() { 10 | var age = new SpaceAge(1000000000); 11 | expect(age.onEarth()).toEqual(31.69); 12 | }); 13 | 14 | it('age in mercury years', function() { 15 | var age = new SpaceAge(2134835688); 16 | expect(age.onEarth()).toEqual(67.65); 17 | expect(age.onMercury()).toEqual(280.88); 18 | }); 19 | 20 | it('age in venus years', function() { 21 | var age = new SpaceAge(189839836); 22 | expect(age.onEarth()).toEqual(6.02); 23 | expect(age.onVenus()).toEqual(9.78); 24 | }); 25 | 26 | it('age in mars years', function() { 27 | var age = new SpaceAge(2329871239); 28 | expect(age.onEarth()).toEqual(73.83); 29 | expect(age.onMars()).toEqual(39.25); 30 | }); 31 | 32 | it('age in jupiter years', function() { 33 | var age = new SpaceAge(901876382); 34 | expect(age.onEarth()).toEqual(28.58); 35 | expect(age.onJupiter()).toEqual(2.41); 36 | }); 37 | 38 | it('age in saturn years', function() { 39 | var age = new SpaceAge(3000000000); 40 | expect(age.onEarth()).toEqual(95.06); 41 | expect(age.onSaturn()).toEqual(3.23); 42 | }); 43 | 44 | it('age in uranus years', function() { 45 | var age = new SpaceAge(3210123456); 46 | expect(age.onEarth()).toEqual(101.72); 47 | expect(age.onUranus()).toEqual(1.21); 48 | }); 49 | 50 | it('age in neptune year', function() { 51 | var age = new SpaceAge(8210123456); 52 | expect(age.onEarth()).toEqual(260.16); 53 | expect(age.onNeptune()).toEqual(1.58); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /meetup/meetup.js: -------------------------------------------------------------------------------- 1 | var weekDays = "Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "); 2 | 3 | var Meetup = function(year, month, day, nth) { 4 | /* Finds a meetup date */ 5 | var intDay = weekDays.indexOf(day) 6 | 7 | switch (nth) { 8 | case "teenth": 9 | return find_day(year, month, intDay, 13, 19); 10 | case "1st": 11 | return find_day(year, month, intDay, 1, 7); 12 | case "2nd": 13 | return find_day(year, month, intDay, 8, 14); 14 | case "3rd": 15 | return find_day(year, month, intDay, 15, 21); 16 | case "4th": 17 | return find_day(year, month, intDay, 22, 28); 18 | case "5th": 19 | return find_day(year, month, intDay, 29, 31); 20 | case "last": 21 | return last(year, month, intDay); 22 | } 23 | } 24 | 25 | function find_day(year, month, day, start, stop) { 26 | /* Finds the first occurance of a day of the week between two dates */ 27 | for(var meetup = new Date(year, month, start); 28 | meetup.getDate() <= stop && meetup.getMonth() == month; 29 | meetup = meetup.addDays(1)) { 30 | 31 | if( meetup.getDay() == day ) 32 | return meetup; 33 | } 34 | throw "Date does not exist"; 35 | }; 36 | 37 | function last(year, month, day) { 38 | /* Last occurance of a day in a month */ 39 | for(var meetup = new Date(year, month + 1, 0); 40 | meetup.getMonth() == month; 41 | meetup = meetup.addDays(-1)) { 42 | 43 | if( meetup.getDay() == day ) 44 | return meetup; 45 | } 46 | throw "Date does not exist"; 47 | }; 48 | 49 | Date.prototype.addDays = function(days) { 50 | /* Adds a number of days to a date object */ 51 | var dat = new Date(this.valueOf()); 52 | dat.setDate(dat.getDate() + days); 53 | return dat; 54 | }; 55 | 56 | module.exports = Meetup; 57 | -------------------------------------------------------------------------------- /zipper/zipper.js: -------------------------------------------------------------------------------- 1 | class Zipper { 2 | constructor(tree, crumbs) { 3 | this.tree = tree; 4 | this.crumbs = crumbs || []; 5 | } 6 | toTree() { 7 | var val = this 8 | var next = this.up() 9 | while (next != null) { 10 | val = next; 11 | next = val.up(); 12 | } 13 | return val.tree; 14 | } 15 | left() { 16 | if (!this.tree['left']) 17 | return null; 18 | return new Zipper(this.tree['left'], 19 | [{ right: this.tree['right'], value: this.tree['value'] }, ...this.crumbs]); 20 | } 21 | right() { 22 | if (!this.tree['right']) 23 | return null; 24 | return new Zipper(this.tree['right'], 25 | [{ left: this.tree['left'], value: this.tree['value'] }, ...this.crumbs]); 26 | } 27 | value() { 28 | return this.tree['value']; 29 | } 30 | up() { 31 | if (this.crumbs.length <= 0) 32 | return null; 33 | let head = this.crumbs[0]; 34 | if (!('right' in head)) { 35 | head.right = this.tree; 36 | } 37 | if (!('left' in head)) { 38 | head.left = this.tree; 39 | } 40 | return new Zipper(head, this.crumbs.slice(1)); 41 | } 42 | setValue(val) { 43 | return new Zipper({ 44 | value: val, 45 | left: this.tree.left, 46 | right: this.tree.right 47 | }, this.crumbs); 48 | } 49 | setLeft(leaf) { 50 | return new Zipper({ 51 | value: this.tree.value, 52 | left: leaf, 53 | right: this.tree.right 54 | }, this.crumbs); 55 | } 56 | setRight(leaf) { 57 | return new Zipper({ 58 | value: this.tree.value, 59 | left: this.tree.left, 60 | right: leaf 61 | }, this.crumbs); 62 | } 63 | } 64 | 65 | module.exports = { 66 | fromTree(items) { 67 | return new Zipper(items); 68 | }, 69 | } 70 | -------------------------------------------------------------------------------- /forth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xecmascript", 3 | "version": "0.0.0", 4 | "description": "Exercism exercises in ECMAScript 6.", 5 | "author": "Katrina Owen", 6 | "private": true, 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/exercism/xecmascript" 10 | }, 11 | "devDependencies": { 12 | "babel-jest": "^21.2.0", 13 | "babel-plugin-transform-builtin-extend": "^1.1.2", 14 | "babel-preset-env": "^1.4.0", 15 | "eslint": "^3.19.0", 16 | "eslint-config-airbnb": "^15.0.1", 17 | "eslint-plugin-import": "^2.2.0", 18 | "eslint-plugin-jsx-a11y": "^5.0.1", 19 | "eslint-plugin-react": "^7.0.1", 20 | "jest": "^21.2.1" 21 | }, 22 | "jest": { 23 | "modulePathIgnorePatterns": [ 24 | "package.json" 25 | ] 26 | }, 27 | "babel": { 28 | "presets": [["env",{"targets":[{"node": "current"}]}] 29 | ], 30 | "plugins": [ 31 | [ 32 | "babel-plugin-transform-builtin-extend", 33 | { 34 | "globals": [ 35 | "Error" 36 | ] 37 | } 38 | ], 39 | [ 40 | "transform-regenerator" 41 | ] 42 | ] 43 | }, 44 | "scripts": { 45 | "test": "jest --no-cache ./*", 46 | "watch": "jest --no-cache --watch ./*", 47 | "lint": "eslint .", 48 | "lint-test": "eslint . && jest --no-cache ./* " 49 | }, 50 | "eslintConfig": { 51 | "parserOptions": { 52 | "ecmaVersion": 6, 53 | "sourceType": "module" 54 | }, 55 | "env": { 56 | "es6": true, 57 | "node": true, 58 | "jest": true 59 | }, 60 | "extends": "airbnb", 61 | "rules": { 62 | "import/no-unresolved": "off", 63 | "import/extensions": "off" 64 | } 65 | }, 66 | "licenses": [ 67 | "MIT" 68 | ], 69 | "dependencies": {} 70 | } 71 | -------------------------------------------------------------------------------- /house/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xecmascript", 3 | "version": "0.0.0", 4 | "description": "Exercism exercises in ECMAScript 6.", 5 | "author": "Katrina Owen", 6 | "private": true, 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/exercism/xecmascript" 10 | }, 11 | "devDependencies": { 12 | "babel-jest": "^21.2.0", 13 | "babel-plugin-transform-builtin-extend": "^1.1.2", 14 | "babel-preset-env": "^1.4.0", 15 | "eslint": "^3.19.0", 16 | "eslint-config-airbnb": "^15.0.1", 17 | "eslint-plugin-import": "^2.2.0", 18 | "eslint-plugin-jsx-a11y": "^5.0.1", 19 | "eslint-plugin-react": "^7.0.1", 20 | "jest": "^21.2.1" 21 | }, 22 | "jest": { 23 | "modulePathIgnorePatterns": [ 24 | "package.json" 25 | ] 26 | }, 27 | "babel": { 28 | "presets": [["env",{"targets":[{"node": "current"}]}] 29 | ], 30 | "plugins": [ 31 | [ 32 | "babel-plugin-transform-builtin-extend", 33 | { 34 | "globals": [ 35 | "Error" 36 | ] 37 | } 38 | ], 39 | [ 40 | "transform-regenerator" 41 | ] 42 | ] 43 | }, 44 | "scripts": { 45 | "test": "jest --no-cache ./*", 46 | "watch": "jest --no-cache --watch ./*", 47 | "lint": "eslint .", 48 | "lint-test": "eslint . && jest --no-cache ./* " 49 | }, 50 | "eslintConfig": { 51 | "parserOptions": { 52 | "ecmaVersion": 6, 53 | "sourceType": "module" 54 | }, 55 | "env": { 56 | "es6": true, 57 | "node": true, 58 | "jest": true 59 | }, 60 | "extends": "airbnb", 61 | "rules": { 62 | "import/no-unresolved": "off", 63 | "import/extensions": "off" 64 | } 65 | }, 66 | "licenses": [ 67 | "MIT" 68 | ], 69 | "dependencies": {} 70 | } 71 | --------------------------------------------------------------------------------