├── .eslintrc ├── .gitignore ├── Makefile ├── README.md ├── numbers ├── change-return │ ├── change.js │ └── index.js ├── decimal-binary-converter-proper │ └── index.js ├── decimal-binary-converter │ └── index.js └── test │ ├── change │ └── change.js │ ├── decimal-binairy-converter-proper │ └── decimal-binary-converter-proper.js │ └── decimal-binairy-converter │ └── decimal-binary-converter.js ├── package.json └── strings ├── count-vowels └── index.js ├── palindrome └── index.js ├── pig-latin └── index.js ├── reverse └── index.js └── test ├── count-vowels └── count-vowels-test.js ├── palindrome └── palindrome-test.js ├── pig-latin └── pig-latin-test.js └── reverse └── reverse-test.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-airbnb", 3 | "parserOptions": { 4 | "ecmaVersion": 2015, 5 | "sourceType": "script", 6 | }, 7 | "env": { 8 | "node": true, 9 | "es6": true, 10 | "mocha": true 11 | }, 12 | "rules": { 13 | "no-plusplus": 0, 14 | "strict": ["error", "safe"], 15 | "import/no-extraneous-dependencies": ["error", {devDependencies: ["**/test/**"]}], 16 | "no-restricted-properties": [0, {"object": "Math", "property": "pow"}], 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### NODE.JS ### 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | *.pid.lock 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional eslint cache 39 | .eslintcache 40 | 41 | # Optional REPL history 42 | .node_repl_history 43 | 44 | # Output of 'npm pack' 45 | *.tgz 46 | 47 | # Yarn Integrity file 48 | .yarn-integrity 49 | 50 | 51 | ### MACOS ### 52 | *.DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Icon must end with two \r 57 | Icon 58 | 59 | 60 | # Thumbnails 61 | ._* 62 | 63 | # Files that might appear in the root of a volume 64 | .DocumentRevisions-V100 65 | .fseventsd 66 | .Spotlight-V100 67 | .TemporaryItems 68 | .Trashes 69 | .VolumeIcon.icns 70 | .com.apple.timemachine.donotpresent 71 | 72 | # Directories potentially created on remote AFP share 73 | .AppleDB 74 | .AppleDesktop 75 | Network Trash Folder 76 | Temporary Items 77 | .apdisk 78 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REPORTER = spec 2 | MOCHA_BIN = ./node_modules/.bin/_mocha 3 | ISTANBUL_BIN = ./node_modules/.bin/istanbul 4 | GREP = '' 5 | 6 | test-strings : 7 | @NODE_ENV=test $(MOCHA_BIN) \ 8 | --reporter $(REPORTER) \ 9 | --recursive \ 10 | --grep $(GREP) \ 11 | */test 12 | 13 | test : test-strings 14 | 15 | coverage : 16 | @NODE_ENV=test $(ISTANBUL_BIN) \ 17 | cover ./node_modules/.bin/_mocha --\ 18 | --recursive \ 19 | --reporter dot \ 20 | */test 21 | 22 | test-all : test coverage 23 | 24 | .PHONY: test-strings test coverage test-all 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #THE PROGRAMMERS IDEA BOOK 2 | List of projects from [The Programmers Idea Book](http://www.coderslexicon.com/downloads/the-programmers-idea-book/) v1.05. 3 | 4 | ##Numbers 5 | - [ ] Add/Subtract/Multiply/DivideFractions 6 | - [ ] Alarm Clock 7 | - [x] Binary to Decimal and Back Converter 8 | - [ ] Calculator 9 | - [x] Change Return Program 10 | - [ ] Credit Card Validator 11 | - [ ] Decimal to Roman Numerals 12 | - [ ] Dijkstra’s Algorithm 13 | - [ ] Distance Between Two Cities 14 | - [ ] Fibonacci Sequence 15 | - [ ] Find Cost of Tile to Cover W x H Floor 16 | - [ ] Find PI to the Nth Digit 17 | - [ ] Household Budget Program 18 | - [ ] LCD/GCD Least/Great Common Denominator 19 | - [ ] Mortgage Calculator 20 | - [ ] Next Prime Number 21 | - [ ] Pascal’s Triangle 22 | - [ ] Prime Factorization 23 | - [ ] Sieve of Eratosthenes 24 | - [ ] Tax Calculator 25 | - [ ] Unit Converter (Temp, Currency, Volume, Mass and More) 26 | 27 | ##Text 28 | - [ ] CD Key Generator 29 | - [x] Check if Palindrome 30 | - [ ] Code Skeleton Creator 31 | - [x] Count Vowels 32 | - [ ] Count Words in a String 33 | - [ ] Font Viewer and Tester (Online or Not) 34 | - [ ] Fortune Teller (Horoscope) 35 | - [ ] Guestbook / Journal 36 | - [ ] Morse Code Maker 37 | - [ ] News Ticker and Game Scores 38 | - [x] Pig Latin 39 | - [ ] Post it Notes Program 40 | - [ ] Quote Tracker (Market Symbols etc) 41 | - [ ] Random Gift Suggestions 42 | - [ ] RSS Feed Creator 43 | - [ ] Recreate Grep (Pattern Matching) 44 | - [ ] Regex Query Tool 45 | - [x] Reverse a String 46 | - [ ] Text Editor 47 | - [ ] Text to HTML Generator 48 | - [ ] Vigenere / Vernam / Caesar Ciphers 49 | - [ ] Write Out Number 50 | 51 | ##Networking 52 | - [ ] Chat Application (IRC or MSN Style) 53 | - [ ] Country from IP Lookup 54 | - [ ] Email Server 55 | - [ ] Fetch Current Weather 56 | - [ ] FTP Program 57 | - [ ] Geolocation App 58 | - [ ] Get Atomic Time from Internet Clock 59 | - [ ] Mail Checker (POP / IMAP) 60 | - [ ] Network Mapper 61 | - [ ] P2P File Sharing App 62 | - [ ] Packet Sniffer 63 | - [ ] Port Scanner 64 | - [ ] Remote Login 65 | - [ ] Site Checker with Time Scheduling 66 | - [ ] Small Web Server 67 | - [ ] SMS Component 68 | - [ ] Social Network Manager 69 | - [ ] Video Conferencing 70 | - [ ] WebBot 71 | - [ ] Whois Search Tool 72 | - [ ] Zip / Postal Code Lookup 73 | 74 | ##Classes 75 | - [ ] Airline / Hotel Reservation System 76 | - [ ] Bank Account Manager 77 | - [ ] Chart Making Class / API 78 | - [ ] Class to Handle Large Numbers 79 | - [ ] Company dashboard 80 | - [ ] Customer Relationship Manager (CRM) 81 | - [ ] Employee Time Card program (track overtime, pay, tax deductions) 82 | - [ ] Family Tree Creator 83 | - [ ] Flower Shop Ordering To Go 84 | - [ ] Image Gallery 85 | - [ ] Josephus Problem 86 | - [ ] Library Catalog 87 | - [ ] Matrix Class 88 | - [ ] Movie Store 89 | - [ ] Patient / Doctor Scheduler 90 | - [ ] Product Inventory Project 91 | - [ ] Recipe Creator and Manager 92 | - [ ] Shape Area and Perimeter Classes 93 | - [ ] ShoppingCart 94 | - [ ] Software cataloger 95 | - [ ] Student Grade Book Application 96 | - [ ] Vending Machine 97 | 98 | ##Threading 99 | - [ ] Bulk Thumbnail Creator 100 | - [ ] Chat Application (remoting style) 101 | - [ ] Create Progress Bar of Download 102 | - [ ] Download Manager 103 | - [ ] News Aggregator 104 | - [ ] XML sitemap generator 105 | 106 | ##Web 107 | - [ ] Bandwidth Monitor 108 | - [ ] Bookmark Collector and Sorter 109 | - [ ] CAPTCHA Maker 110 | - [ ] Content Management System 111 | - [ ] Countdown Screen 112 | - [ ] E-Card Generator 113 | - [ ] File Downloader 114 | - [ ] Media Player Widget for iGoogle 115 | - [ ] Music player or manager 116 | - [ ] Online IDE 117 | - [ ] Online Whiteboard 118 | - [ ] Page Scraper 119 | - [ ] Password Safe 120 | - [ ] Scheduled Auto Login and Action 121 | - [ ] SEO optimizer (scan page, recommend keywords etc) 122 | - [ ] Shopping App 123 | - [ ] Telnet Application 124 | - [ ] TemplateMaker 125 | - [ ] Text Based Game Like Utopia 126 | - [ ] Web Browser with Tabs 127 | - [ ] WYSIWG (What you see is what you get) Editor 128 | 129 | ##Files 130 | - [ ] Add Transactions In File and Find Averages 131 | - [ ] Barcode reader and generator 132 | - [ ] Bulk Renamer and Organizer 133 | - [ ] CD Label Maker 134 | - [ ] Clipboard manager 135 | - [ ] Code Snippet Manager 136 | - [ ] Color Picker and Translator 137 | - [ ] Create Zip File Maker 138 | - [ ] Envelope/Label printer 139 | - [ ] Excel Spreadsheet Exporter 140 | - [ ] File Copy Utility 141 | - [ ] File Explorer 142 | - [ ] File splitter/joiner (split and join from multiple discs) 143 | - [ ] Font loader 144 | - [ ] Generate invoice/purchase order 145 | - [ ] Image Map Generator 146 | - [ ] Log File Maker 147 | - [ ] Mp3 Tagger 148 | - [ ] PDF Generator 149 | - [ ] Quick Launcher 150 | - [ ] Quiz Maker 151 | - [ ] Registry Cleaner 152 | - [ ] RPG Character Stat Creator 153 | - [ ] Song mixer / DJ 154 | - [ ] Sort File Records Utility 155 | - [ ] VersioningManager 156 | - [ ] Web document viewer 157 | 158 | ##Databases 159 | - [ ] Address Book 160 | - [ ] Baseball / Other Card Collector 161 | - [ ] Budget Tracker 162 | - [ ] Database Backup Script Maker 163 | - [ ] Database Translation (MySQL <-> SQL Server) 164 | - [ ] Entity Relationship Diagram (ERD) Creator 165 | - [ ] Error message db (file name, error, message, resolution) 166 | - [ ] Event Scheduler and Calendar 167 | - [ ] Meal / Food Journal (with calorie calculator) 168 | - [ ] Postal shipping program (cost to destination based on weight etc) 169 | - [ ] Remote SQL Tool 170 | - [ ] ReportGenerator 171 | - [ ] Repository App 172 | - [ ] SQL Query Analyzer 173 | - [ ] Survey system 174 | - [ ] TV Show Tracker 175 | - [ ] Travel Planner System 176 | - [ ] Web Board (Forum) 177 | 178 | ##Graphics and Multimedia 179 | - [ ] Bulk Picture Manipulator 180 | - [ ] CD Burning App 181 | - [ ] Image Browser 182 | - [ ] Import Picture and Save as Grayscale 183 | - [ ] MP 184 | - [ ] to Wav Converter 185 | - [ ] MindMapper 186 | - [ ] Mp 187 | - [ ] Player (and Other Formats) 188 | - [ ] Screen Capture Program 189 | - [ ] Screen Saver 190 | - [ ] Signature Maker 191 | - [ ] Slide Show 192 | - [ ] Stream Video from Online 193 | - [ ] Traffic Light Application 194 | - [ ] Turtle Graphics 195 | - [ ] Wallpaper Manager 196 | - [ ] Watermarking Application 197 | - [ ] YouTube Downloader 198 | 199 | ##Games 200 | - [ ] D RPG 201 | - [ ] Battleship 202 | - [ ] Black Jack 203 | - [ ] Breakout 204 | - [ ] Chess and Checkers 205 | - [ ] Connect four 206 | - [ ] Craps 207 | - [ ] Crossword Puzzle 208 | - [ ] Find Way Out of Maze 209 | - [ ] Frogger 210 | - [ ] Game of Memory 211 | - [ ] Guitar Hero Clone 212 | - [ ] Hangman 213 | - [ ] High / Low Number Guessing 214 | - [ ] Ice and Dice 215 | - [ ] Magic Eight Ball 216 | - [ ] Monopoly 217 | - [ ] Pac Man 218 | - [ ] Pin Ball 219 | - [ ] Poker (plus online component) 220 | - [ ] Scorched Earth 221 | - [ ] Slot Machine 222 | - [ ] Snake Game 223 | - [ ] Tic-Tac-Toe with Friend Online 224 | - [ ] Type that word (falls from sky) 225 | -------------------------------------------------------------------------------- /numbers/change-return/change.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const denominations = [100, 50, 20, 10, 5, 2, 1, 0.5, 0.25, 0.1, 0.05]; 4 | 5 | exports.calculate = (price, amount) => { 6 | if (price !== parseFloat(price) || amount !== parseFloat(amount)) { 7 | throw (new TypeError('Invalid params')); 8 | } 9 | if (amount < price) { 10 | throw (new TypeError('Not enough cash')); 11 | } 12 | 13 | let rest = amount - price; 14 | const change = {}; 15 | for (let i = 0; i < denominations.length; i++) { 16 | const multiplier = Math.floor(rest / denominations[i]); 17 | if (multiplier > 0) { 18 | change[denominations[i]] = multiplier; 19 | rest %= denominations[i]; 20 | } 21 | } 22 | if (rest > 0) { 23 | change['0.01'] = Math.round(rest / 0.01); 24 | } 25 | return change; 26 | }; 27 | -------------------------------------------------------------------------------- /numbers/change-return/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const prompt = require('prompt'); 4 | const Promise = require('bluebird'); 5 | const calculateChange = require('./change').calculate; 6 | const _ = require('lodash'); 7 | 8 | const prices = { 9 | 'Chocolate Bar': 1.23, 10 | Candy: 0.07, 11 | Drink: 1.99, 12 | }; 13 | const items = _.keys(prices); 14 | const width = 20; 15 | const itemProps = [ 16 | { 17 | name: 'item', 18 | validator: /^[0-9]+$/, 19 | warning: 'Input must be numeric', 20 | }, 21 | ]; 22 | const amountProps = [ 23 | { 24 | name: 'amount', 25 | validator: /^[0-9.]+$/, 26 | warning: 'Input must be a float', 27 | }, 28 | ]; 29 | 30 | for (let i = 0; i < items.length; i++) { 31 | console.log(`${i + 1}. ${items[i]}${(' '.repeat(width - items[i].length))}${prices[items[i]]}`); 32 | } 33 | 34 | const onError = function onError(err) { 35 | console.log(err.message); 36 | process.exit(); 37 | }; 38 | 39 | let price = -1; 40 | Promise.promisifyAll(prompt); 41 | prompt.start(); 42 | prompt.getAsync(itemProps).then((response) => { 43 | if (response.item > items.length || response.item < 1) { 44 | onError(new Error('Chosen item does not exist.')); 45 | } 46 | price = prices[items[response.item - 1]]; 47 | return prompt.getAsync(amountProps); 48 | }).then((response) => { 49 | const change = calculateChange(price, parseFloat(response.amount)); 50 | console.log(`Your change: ${JSON.stringify(change)}`); 51 | }); 52 | -------------------------------------------------------------------------------- /numbers/decimal-binary-converter-proper/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.dec2bin = (decimal) => { 4 | if (decimal !== parseInt(decimal, 10)) { 5 | return undefined; 6 | } 7 | // Coerse into unsigned int by logical shifting 8 | // http://stackoverflow.com/questions/9939760/how-do-i-convert-an-integer-to-binary-in-javascript 9 | /* eslint no-bitwise: ["error", { "allow": [">>>"] }] */ 10 | return (decimal >>> 0).toString(2); 11 | }; 12 | 13 | exports.bin2dec = (binary) => { 14 | if (typeof binary !== 'string' || binary.match(/[^10]/)) { 15 | return undefined; 16 | } 17 | // This will never produce unsigned integer results 18 | // I don't want to get into bit shifting magic here 19 | return parseInt(binary, 2); 20 | }; 21 | -------------------------------------------------------------------------------- /numbers/decimal-binary-converter/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.dec2bin = (decimal) => { 4 | if (decimal !== parseInt(decimal, 10) || decimal !== Math.abs(decimal)) { 5 | return undefined; 6 | } 7 | 8 | let rest = decimal; 9 | let result = ''; 10 | while (rest > 1) { 11 | result = (rest % 2).toString().concat(result); 12 | rest = Math.floor(rest / 2); 13 | } 14 | 15 | return rest.toString().concat(result); 16 | }; 17 | 18 | exports.bin2dec = (binary) => { 19 | if (typeof binary !== 'string' || binary.match(/[^10]/)) { 20 | return undefined; 21 | } 22 | 23 | const len = binary.length; 24 | return binary.split('').reduce((acc, val, idx) => { 25 | // left to right reduce, therefore value should be taken starting from right in binary string 26 | const calculated = (Math.pow(2, idx) * parseInt(binary[len - idx - 1], 10)); 27 | return acc + calculated; 28 | }, 0); 29 | }; 30 | -------------------------------------------------------------------------------- /numbers/test/change/change.js: -------------------------------------------------------------------------------- 1 | const expect = require('chai').expect; 2 | const calculateChange = require('../../change-return/change.js').calculate; 3 | 4 | describe('Change calculator', () => { 5 | it('handles invalid input', () => { 6 | expect(calculateChange.bind(calculateChange, 'abc', 'def')).to.throw(/Invalid params/); 7 | expect(calculateChange.bind(calculateChange, 0.1, 'def')).to.throw(/Invalid params/); 8 | expect(calculateChange.bind(calculateChange, 'abc', 0.1)).to.throw(/Invalid params/); 9 | }); 10 | it('handles empty input', () => { 11 | expect(calculateChange.bind(calculateChange, 10)).to.throw(/Invalid params/); 12 | expect(calculateChange.bind(calculateChange, null, 10)).to.throw(/Invalid params/); 13 | expect(calculateChange.bind(calculateChange)).to.throw(/Invalid params/); 14 | }); 15 | it('handles zero price', () => { 16 | expect(calculateChange(0, 0)).to.be.deep.equal({}); 17 | expect(calculateChange(0, 10)).to.be.deep.equal({ 10: 1 }); 18 | }); 19 | it('throws cash error when amount is insufficient', () => { 20 | expect(calculateChange.bind(calculateChange, 10, 0)).to.throw(/Not enough cash/); 21 | }); 22 | it('calculates small change', () => { 23 | expect(calculateChange(0.34, 0.5)).to.be.deep.equal({ 0.1: 1, 0.05: 1, 0.01: 1 }); 24 | expect(calculateChange(0.12, 0.5)).to.be.deep.equal({ 0.25: 1, 0.1: 1, 0.01: 3 }); 25 | }); 26 | it('calculates large change', () => { 27 | expect(calculateChange(0.34, 12)).to.be.deep.equal({ 28 | 10: 1, 1: 1, 0.5: 1, 0.1: 1, 0.05: 1, 0.01: 1, 29 | }); 30 | expect(calculateChange(12.54, 336)).to.be.deep.equal({ 31 | 100: 3, 20: 1, 2: 1, 1: 1, 0.25: 1, 0.1: 2, 0.01: 1, 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /numbers/test/decimal-binairy-converter-proper/decimal-binary-converter-proper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const toBinary = require('../../decimal-binary-converter-proper').dec2bin; 5 | const toDecimal = require('../../decimal-binary-converter-proper').bin2dec; 6 | 7 | describe('Proper Binary-Decimal Converter', () => { 8 | it('handles invalid input', () => { 9 | /* eslint-disable no-unused-expressions */ 10 | expect(toBinary()).to.not.exist; 11 | expect(toBinary('string')).to.not.exist; 12 | expect(toDecimal()).to.not.exist; 13 | expect(toDecimal('string')).to.not.exist; 14 | expect(toDecimal('112001')).to.not.exist; 15 | /* eslint-enable no-unused-expressions */ 16 | }); 17 | it('converts to binary', () => { 18 | expect(toBinary(-16)).to.be.equal('11111111111111111111111111110000'); 19 | expect(toBinary(4)).to.be.equal('100'); 20 | expect(toBinary(7)).to.be.equal('111'); 21 | expect(toBinary(131261)).to.be.equal('100000000010111101'); 22 | }); 23 | it('converts to decimal', () => { 24 | expect(toDecimal('100')).to.be.equal(4); 25 | expect(toDecimal('100000000010111101')).to.be.equal(131261); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /numbers/test/decimal-binairy-converter/decimal-binary-converter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const toBinary = require('../../decimal-binary-converter').dec2bin; 5 | const toDecimal = require('../../decimal-binary-converter').bin2dec; 6 | 7 | describe('Binary-Decimal Converter', () => { 8 | it('handles invalid input', () => { 9 | /* eslint-disable no-unused-expressions */ 10 | expect(toBinary()).to.not.exist; 11 | expect(toBinary('string')).to.not.exist; 12 | expect(toDecimal()).to.not.exist; 13 | expect(toDecimal(-2)).to.not.exist; 14 | expect(toDecimal('string')).to.not.exist; 15 | expect(toDecimal('112001')).to.not.exist; 16 | /* eslint-enable no-unused-expressions */ 17 | }); 18 | it('converts to binary', () => { 19 | expect(toBinary(4)).to.be.equal('100'); 20 | expect(toBinary(7)).to.be.equal('111'); 21 | expect(toBinary(131261)).to.be.equal('100000000010111101'); 22 | }); 23 | it('converts to decimal', () => { 24 | expect(toDecimal('100')).to.be.equal(4); 25 | expect(toDecimal('100000000010111101')).to.be.equal(131261); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Programmers Idea Book Solutions", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "make test", 8 | "coverage": "make coverage" 9 | }, 10 | "author": "alefi87@gmail.com", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "babel-eslint": "^7.1.1", 14 | "chai": "^3.5.0", 15 | "eslint": "^3.12.2", 16 | "eslint-config-airbnb": "^13.0.0", 17 | "eslint-plugin-import": "^2.2.0", 18 | "eslint-plugin-jsx-a11y": "^2.2.3", 19 | "eslint-plugin-react": "^6.8.0", 20 | "istanbul": "^0.4.5", 21 | "mocha": "^3.2.0" 22 | }, 23 | "dependencies": { 24 | "bluebird": "^3.4.7", 25 | "lodash": "^4.17.2", 26 | "prompt": "^1.0.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /strings/count-vowels/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.countVowels = (text) => { 4 | const result = text.toLowerCase().split(/[^aeoiu]/i).reduce((acc, curr) => { 5 | if (curr === '') { 6 | return acc; 7 | } 8 | const accumulated = acc; 9 | // handle multiple sequencial vowels 10 | curr.split('').map((vowel) => { 11 | if (!acc[vowel]) { 12 | accumulated[vowel] = 1; 13 | } else { 14 | accumulated[vowel] += 1; 15 | } 16 | return true; 17 | }); 18 | 19 | return accumulated; 20 | }, {}); 21 | 22 | return result; 23 | }; 24 | -------------------------------------------------------------------------------- /strings/palindrome/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.isPalindrome = (text) => { 4 | if (!text) { 5 | throw new TypeError('empty input'); 6 | } 7 | if (text.match(/[^a-z]/i)) { 8 | throw new TypeError('non-alphabetic input'); 9 | } 10 | 11 | return text === text.split('').reverse().join(''); 12 | }; 13 | -------------------------------------------------------------------------------- /strings/pig-latin/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function latinate(word) { 4 | // empty input 5 | if (word.length === 0) { return ''; } 6 | // does not contain any alphanumeric chars 7 | if (!word.match(/[a-z0-9]/i)) { return word; } 8 | // does not contain a letter (only numeric after previous check) 9 | if (!word.match(/[a-z]/i)) { return word; } 10 | // only consonants should be left unhandled 11 | if (!word.match(/[aeoiu]/i)) { return word; } 12 | 13 | const firstChar = word.substring(0, 1); 14 | const isCapitalized = firstChar && firstChar === firstChar.toUpperCase(); 15 | const consonants = word.split(/[aoeiu]/i)[0]; 16 | let result = ''; 17 | 18 | if (consonants.length > 0) { 19 | const wordWithoutLeadConsonants = word.substring(consonants.length, word.length); 20 | result = `${wordWithoutLeadConsonants}${consonants.toLowerCase()}ay`; 21 | } else { 22 | result = `${word}way`; 23 | } 24 | 25 | if (isCapitalized) { 26 | result = result.substr(0, 1).toUpperCase() + result.substr(1); 27 | } 28 | return result; 29 | } 30 | 31 | exports.translate = (text) => { 32 | const words = text.split(/\b/); 33 | const latinatedWords = words.map(word => latinate(word)); 34 | return latinatedWords.join(''); 35 | }; 36 | -------------------------------------------------------------------------------- /strings/reverse/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.reverse = str => str.split('').reverse().join(''); 4 | -------------------------------------------------------------------------------- /strings/test/count-vowels/count-vowels-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const countVowels = require('../../count-vowels').countVowels; 5 | 6 | describe('Count vowels', () => { 7 | it('handles empty input', () => { 8 | // eslint-disable-next-line no-unused-expressions 9 | expect(countVowels('')).to.be.empty; 10 | }); 11 | it('counts vowels in single word', () => { 12 | expect(countVowels('hello')).to.be.deep.equal({ e: 1, o: 1 }); 13 | }); 14 | it('handles multiple vowels in sequence', () => { 15 | expect(countVowels('sauce')).to.be.deep.equal({ a: 1, e: 1, u: 1 }); 16 | }); 17 | it('counts vowels in single alpha-numeric word', () => { 18 | expect(countVowels('AwesomeSauce12')).to.be.deep.equal({ e: 3, o: 1, a: 2, u: 1 }); 19 | }); 20 | it('counts vowels in a sentence with punctuation', () => { 21 | expect(countVowels('Hello, World!')).to.be.deep.equal({ e: 1, o: 2 }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /strings/test/palindrome/palindrome-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const isPalindrome = require('../../palindrome').isPalindrome; 5 | 6 | describe('Palindrome', () => { 7 | it('throws TypeError when input is empty', () => { 8 | expect(isPalindrome.bind(isPalindrome, '')).to.throw(/empty input/); 9 | }); 10 | it('throws TypeError when input contains anything but alphabetic characters', () => { 11 | expect(isPalindrome.bind(isPalindrome, 'r4c3c4r')).to.throw(/non-alphabetic/); 12 | expect(isPalindrome.bind(isPalindrome, 'racercar is awesome')).to.throw(/non-alphabetic/); 13 | }); 14 | it('identifies a palindrome', () => { 15 | // eslint-disable-next-line no-unused-expressions 16 | expect(isPalindrome('racecar')).to.be.true; 17 | }); 18 | it('identifies non-palindromes', () => { 19 | // eslint-disable-next-line no-unused-expressions 20 | expect(isPalindrome('racer')).to.be.false; 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /strings/test/pig-latin/pig-latin-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const translate = require('../../pig-latin').translate; 5 | 6 | describe('Pig Latin', () => { 7 | it('handles empty input', () => { 8 | expect(translate('')).to.equal(''); 9 | }); 10 | it('handles non-alphabetical words', () => { 11 | expect(translate('12#')).to.equal('12#'); 12 | }); 13 | it('handles words with only consonants (abbreviations?)', () => { 14 | expect(translate('CSR')).to.equal('CSR'); 15 | }); 16 | it('handles a word staring on one consonant', () => { 17 | expect(translate('hello')).to.equal('ellohay'); 18 | }); 19 | it('handles a word staring on multiple consonants', () => { 20 | expect(translate('trash')).to.equal('ashtray'); 21 | }); 22 | it('handles a word staring on a vocal', () => { 23 | expect(translate('apple')).to.equal('appleway'); 24 | }); 25 | it('handles words separated by space', () => { 26 | expect(translate('hello world')).to.equal('ellohay orldway'); 27 | }); 28 | it('handles a capitalized word', () => { 29 | expect(translate('Hello')).to.equal('Ellohay'); 30 | }); 31 | it('handles a word with capitals in the middle', () => { 32 | expect(translate('hElLo HeLLo')).to.equal('ElLohay ELLohay'); 33 | }); 34 | it('handles a word with punctuation', () => { 35 | expect(translate('hello,')).to.equal('ellohay,'); 36 | }); 37 | it('handles sentences with punctuation', () => { 38 | const sentence = 'Hello, World! This should work. Does it?' 39 | const expected = 'Ellohay, Orldway! Isthay ouldshay orkway. Oesday itway?' 40 | expect(translate(sentence)).to.equal(expected); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /strings/test/reverse/reverse-test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const expect = require('chai').expect; 4 | const reverse = require('../../reverse').reverse; 5 | 6 | describe('Reverse', () => { 7 | it('handles empty input', () => { 8 | expect(reverse('')).to.equal(''); 9 | }); 10 | it('reverses a string', () => { 11 | expect(reverse('hello')).to.equal('olleh'); 12 | }); 13 | it('handles multiple words and capital letters', () => { 14 | expect(reverse('Hello World')).to.equal('dlroW olleH'); 15 | }); 16 | it('reverses strange chars and numbers', () => { 17 | expect(reverse('hello $€#&%3')).to.equal('3%&#€$ olleh'); 18 | }); 19 | }); 20 | --------------------------------------------------------------------------------