├── '
├── .eslintrc.json
├── .gitignore
├── 700c20f2e4b3b5d9258b70cad5e5e9f5.json
├── README.md
├── _.js
├── _testing_in_action_.png
├── appa.html
├── build.json
├── ch01.html
├── ch02.html
├── ch03.html
├── ch04.html
├── ch05.html
├── ch06.html
├── ch07.html
├── ch08.html
├── ch09.html
├── ch10.html
├── ch11.html
├── ch12.html
├── code
├── ch01
│ └── 1_all.js
├── ch04
│ ├── 1_new_code_from_scratch
│ │ ├── 1_check_hand.js
│ │ └── 2_check_hand.js
│ ├── 2_new_code_tdd
│ │ ├── 1_new_code_tdd.js
│ │ └── 2_check_hand_with_mocha.js
│ ├── 3_untested_code
│ │ ├── 1_random_hand.js
│ │ └── 2_random_hand.js
│ └── 4_debugging_regression
│ │ └── 1_random_hand.js
├── ch05
│ ├── 1_general
│ │ └── general.js
│ ├── 2_context
│ │ ├── 1_background.js
│ │ ├── 2_another_object_variants.js
│ │ └── 3_fluent_and_composed.js
│ └── 3_context
│ │ ├── 1_diary.js
│ │ ├── 2_diary.js
│ │ ├── 3_diary.js
│ │ ├── 4_diary.js
│ │ ├── 5_diary.js
│ │ ├── 6_diary_module.js
│ │ ├── 7_diary_reader.js
│ │ ├── 8_possible_future_privacy.js
│ │ └── short-parts.js
├── ch06
│ ├── 1_initial.js
│ ├── 2_after_renaming.js
│ ├── 3_after_useless_code.js
│ ├── 4_after_variables.js
│ ├── 5_after_strings.js
│ └── 6_after_working_with_arrays.js
├── ch07
│ ├── 10_after_shorthand_syntax.js
│ ├── 11_after_getting_new_obj_constructor_func.js
│ ├── 12_after_constructor_vs_factory.js
│ ├── 13_after_class_for_classifier.js
│ ├── 14_after_choosing_api.js
│ ├── 15_after_privacy.js
│ ├── 15_after_privacy_test.js
│ ├── 16_after_new_domain.js
│ ├── 16_after_new_domain_test.js
│ ├── 1_initial.js
│ ├── 2_after_array_object_alternatives.js
│ ├── 3_after_testing_setup.js
│ ├── 4_after_extracting_functions.js
│ ├── 5_after_handling_remaining_globals.js
│ ├── 6_after_independent_data.js
│ ├── 7_after_bringing_classify_in.js
│ ├── 8_after_untangling_coupled_values.js
│ ├── 9_after_bringing_other_functions_and_vars.js
│ └── secrets.js
├── ch08
│ ├── 1_lets_build_a_hierarchy.js
│ ├── 2_lets_wreck_our_hierarchy.js
│ ├── 3_object_literals.js
│ ├── 4_factory_functions.js
│ ├── 4_factory_functions_with_prototype.js
│ ├── 5_multiple_inheritance.js
│ ├── 6_hyperextension.js
│ ├── 6_hyperextension_fixed.js
│ ├── 7_goat_cabbage_wolf.js
│ └── 7_goat_cabbage_wolf_fixed.js
├── ch09
│ ├── 1_template.js
│ ├── 1_template_final.js
│ ├── 1_template_functional.js
│ ├── 2_strategy.js
│ ├── 3_state.js
│ ├── 3_state_final.js
│ ├── 4_null_object.js
│ ├── 4_null_object_final.js
│ ├── 5_adapter.js
│ ├── 5_wrapper_dog.js
│ ├── 5_wrapper_null_object.js
│ └── 6_facade.js
├── ch10
│ ├── 1_why_async.js
│ ├── 2_fixing_pyramid_of_doom.js
│ ├── 3_callbacks_and_testing.js
│ └── 4_promises.js
└── ch11
│ ├── 10_functional_version_of_nbc.js
│ ├── 10_functional_version_of_nbc_test.js
│ ├── 1_restrictions_and_benefits.js
│ ├── 2_the_basics.js
│ ├── 3_currying.js
│ ├── 4_function_composition.js
│ ├── 5_types.js
│ ├── 6_introducing_sanctuary.js
│ ├── 7_null_object_again.js
│ ├── 8_functional_refactoring_with_maybe.js
│ └── 9_functional_refactoring_with_either.js
├── console_with_context.png
├── copyright.html
├── cover.html
├── cover.png
├── dedication.html
├── diagramming_language.afdesign
├── diagrams
├── diary_creating_function.png
├── function.png
├── function_anonymous.png
├── function_attached_to_anObject.png
├── function_diary_privateUnlock.png
├── function_diary_privateUnlock_2.png
├── function_diary_privateUnlock_3.png
├── function_diary_read.png
├── function_diary_tryLock.png
├── function_returning_number.png
├── function_setIt.png
├── function_with_bulk_1.png
├── function_with_bulk_2.png
├── function_with_complex_output.png
├── function_with_global_implicit.png
├── function_with_no_return.png
├── function_with_on_explicit_input_one_implicit_input.png
├── function_with_side_effect.png
├── function_with_two_explicit_inputs.png
├── function_with_two_non_local_inputs.png
└── function_with_undefined_implicit.png
├── diary.png
├── diary10.png
├── diary3.png
├── diary4.png
├── diary5.png
├── diary6.png
├── diary7.png
├── diary8.png
├── diary9.png
├── diary_2.png
├── dynamic_this.png
├── fantasy_land_diagram.png
├── function-circle.png
├── function.png
├── functions_with_bulk.png
├── haskell_diagram.png
├── images
├── __red_green_refactor.png
├── _red_green_refactor.png
├── _testing_in_action_.png
├── cover.png
├── red_green_refactor.afdesign
├── red_green_refactor_.png
├── tarsier.png
├── testing_in_action.afdesign
├── testing_in_action.png
└── testing_in_action_.png
├── ix.html
├── mocha_results.png
├── mocha_test_output.png
├── nbc.js
├── not_global_context.png
├── null.png
├── preface.html
├── private_function.png
├── red_green_refactor_.png
├── side_effect_to_other_this.png
├── testing_in_action.png
├── testing_in_action_.png
├── theme
├── epub
│ └── epub.css
├── html
│ └── html.css
├── mobi
│ └── mobi.css
└── pdf
│ └── pdf.css
├── timeline.png
├── titlepage.html
├── toc.html
├── todo
├── using_strict.png
├── with_inputs.png
├── with_outputs.png
└── with_side_effects.png
/':
--------------------------------------------------------------------------------
1 | function addOne(addend){
2 | new Error 'afdsf'
3 | return addend + 1;
4 | }
5 |
6 | function four(){
7 | return new Promise((resolve, _reject) => {
8 | setTimeout(() => resolve(4), 500);
9 | })
10 | }
11 |
12 | four()
13 | .then(addOne)
14 | .then((valueFromAddOne) => console.log(valueFromAddOne));
15 |
16 |
17 | //four()
18 | //.then(addOne)
19 | //.then(addOne)
20 | //.then(addOne)
21 | //.then(console.log);
22 |
23 | //.then(result => addOne(result))
24 | //.then(result => console.log(result));
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | //three.then((x) => addOne(x, console.log));
35 |
36 | //const test = require('tape');
37 | //const testdouble = require('testdouble');
38 |
39 | //test('our addOne function', (assert) => {
40 | //addOne(3, (result) => {
41 | //assert.equal(result, 4);
42 | //assert.end();
43 | //});
44 | //});
45 |
46 | //test('our three function', (assert) => {
47 | //three((result, callback) => {
48 | //assert.equal(result, 3);
49 | //assert.equal(callback, console.log);
50 | //assert.end();
51 | //});
52 | //});
53 | //test('our end-to-end test', (assert) => {
54 | //testdouble.replace(console, 'log')
55 | //three((result, callback) => {
56 | //addOne(result, callback)
57 | //testdouble.verify(console.log(4));
58 | //testdouble.reset();
59 | //assert.end();
60 | //});
61 | //});
62 |
63 |
64 | //function addOneSync(addend){
65 | //return addend + 1;
66 | //}
67 |
68 | //test('our addOneSync function', (assert) => {
69 | //assert.equal(addOneSync(3), 4);
70 | //assert.end();
71 | //});
72 |
73 | //function three(){
74 | //setTimeout(function(){
75 | //return 3
76 | //},
77 | //500);
78 | //}
79 | //function two(callback){
80 | //callback(2);
81 | //}
82 |
83 | //two(addOne);
84 |
85 | //addOne(three);
86 |
87 |
88 | //one((addend) => console.log(4 + addend));
89 |
90 | //x = [1, 2, 3, 4, 5];
91 |
92 | //function myMap(array, callback){
93 | //const newArray = [];
94 | //array.forEach(function(element){
95 | //newArray.push(callback(element));
96 | //})
97 | //return newArray;
98 | //}
99 |
100 | //function myMap(array, callback){
101 | //return array.reduce(function(acc, element){
102 | //acc.push(callback(element));
103 | //return acc;
104 | //}, [])
105 | //}
106 |
107 | //y = myMap(x, function(element){ return element * 2})
108 | //console.log(y);
109 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb-base",
3 | "plugins": [
4 | "import"
5 | ],
6 |
7 |
8 | "rules": {
9 | "space-before-blocks": 0,
10 | "no-bitwise": 1,
11 | "no-extra-semi": 0,
12 | "import/no-extraneous-dependencies": 0,
13 | "import/newline-after-import": 0,
14 | "semi": 2,
15 | "space-before-function-paren": 0,
16 | "brace-style": 0,
17 | "space-infix-ops": 0,
18 | "indent": 0,
19 | "keyword-spacing": 0,
20 | "no-var": 1,
21 | "no-else-return": 0,
22 | "prefer-arrow-callback": 1,
23 | "no-use-before-define": 1,
24 | "prefer-template": 1,
25 | "comma-dangle": 0,
26 | "quotes": 1,
27 | "no-underscore-dangle": 0,
28 | "max-len": ["error", 70],
29 | "operator-assignment": 1,
30 | "no-plusplus": 0,
31 | "no-mixed-operators": 0,
32 | "vars-on-top": 1,
33 | "object-curly-spacing": 0,
34 | "space-in-parens": 0,
35 | "no-param-reassign": 1 // this flags reduce calls
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_*
2 | ._.DS_*
3 |
--------------------------------------------------------------------------------
/700c20f2e4b3b5d9258b70cad5e5e9f5.json:
--------------------------------------------------------------------------------
1 | {"debug":[],"info":[],"warn":[],"error":[]}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # refactoringjavascript
2 | The code and text for the book, Refactoring JavaScript
3 |
4 |
5 | This repo is a mirror of the text, rather than a living document. If for no other reason, this is because the book, via the publisher, is the "upstream branch" of this.
6 |
7 | There is a way to submit errata to O'Reilly here: http://shop.oreilly.com/product/0636920053262.do
8 |
9 | If you feel like submitting an issue, although I'm happy that you're engaging with the text and appreciate your ideas, I'm unlikely to accept any PRs.
10 |
11 | But if there's anything in the text that gets in the way of your understanding, you can contact me through refactoringjs.com for questions. I always respond.
12 |
--------------------------------------------------------------------------------
/_.js:
--------------------------------------------------------------------------------
1 | //const http = require('http');
2 | //const getBody = {
3 | //bodyArray: [],
4 | //saveBody: function(chunk){
5 | //this.bodyArray.push(chunk);
6 | //},
7 | //getResult: function(result){
8 | //result.on('data', this.saveBody.bind(this));
9 | //result.on('end', this.printBody.bind(this));
10 | //},
11 | //printBody: function(){
12 | //console.log(this.bodyArray.join(''))
13 | //this.allDone();
14 | //},
15 | //allDone: function(){}
16 |
17 | //};
18 | //const test = require('tape');
19 | //test('our async routine', function (assert) {
20 | //getBody.allDone = function(){
21 | //assert.equal(getBody.bodyArray.length, 2);
22 | //assert.end();
23 | //}
24 | //http.get('http://refactoringjs.com', getBody.getResult.bind(getBody));
25 | //});
26 |
27 | //test('our async routine two', function (assert) {
28 | //getBody.bodyArray = [];
29 | //getBody.allDone = function(){ };
30 | //http.get('http://refactoringjs.com', getBody.getResult.bind(getBody));
31 | //assert.equal(getBody.bodyArray.length, 0);
32 | //assert.end();
33 | //});
34 |
35 |
36 | //addOne(console.log, four());
37 |
38 |
39 | //function three(callback, innerCallback){
40 | //setTimeout(() => {
41 | //callback(3, innerCallback);
42 | //},
43 | //500);
44 | //}
45 | //three(addOne(console.log));
46 | //three(addOne);
47 |
48 | four(){j
49 | function addOne(addend, callback){
50 | callback(addend + 1);
51 | };
52 |
53 | function three(callback, innerCallback){
54 | callback(3, innerCallback);
55 | };
56 | three(addOne, console.log);
57 |
58 |
59 |
--------------------------------------------------------------------------------
/_testing_in_action_.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/_testing_in_action_.png
--------------------------------------------------------------------------------
/appa.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Refactoring JavaScript
6 |
7 |
8 |
9 |
10 |
11 | Further Reading and Resources
12 |
13 | This is a collection of books, tools, videos, and other resources for additional background on the topics covered in Refactoring JavaScript.
14 |
15 | If any of the links go dead, try them on the wayback machine .
16 |
17 | For sources without links, try searching the words given (with and without surrounding quotes) and you should be able to find them.
18 |
19 | Most resources are free, but some are only available for purchase (or with clever searching). In the cases of non-free resources, I have not provided a link and have included the author name for easier searching.
20 |
21 | Origins of Refactoring
22 |
23 |
24 | Refactoring Object-Oriented Frameworks by William F. Opdyke
25 | Refactoring: Improving the Design of Existing Code by Martin Fowler, Kent Beck, John Brant and William Opdyke
26 | Design Patterns: Elements of Reusable Object-Oriented Software(GoF book) by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
27 |
28 |
29 | Baseline JavaScript(s)
30 |
31 |
37 |
38 | Keeping Up With JavaScript
39 |
40 |
46 |
47 | JavaScript Reference
48 |
49 |
57 |
58 | Object-Oriented Programs/Patterns (Including Anti-Class stances)
59 |
60 |
73 |
74 | Async
75 |
76 |
83 |
84 | Functional
85 |
86 |
100 |
101 | Tools
102 |
103 |
104 | node (JavaScript outside of the browser)
105 | git (source/version control management)
106 | npm (node package manager)
107 | yarn (npm alternative)
108 | node assert and browser console.assert
109 | wish (assert alternative)
110 | mocha (big testing library)
111 | tape (smaller testing library)
112 | testdouble (mocking/stubbing framework)
113 | Underscore (functional library)
114 | Lodash (functional library)
115 | Ramda (better functional library)
116 | Sanctuary (FP with objects too)
117 | jQuery (JavaScript Library)
118 | Trellus (function diagramming)
119 |
120 |
121 | Other Non-JS specific, but Relevant Sources
122 |
123 |
124 | Refactoring To Patterns by Joshua Kerievsky
125 | Design Patterns in Ruby by Russ Olsen
126 | Refactoring: Ruby Edition by Jay Fields, Shane Harvie, Martin Fowler, Kent Beck
127 | "Therapeutic Refactoring" Talk by Katrina Owen
128 |
129 |
130 | Me
131 |
132 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/ch12.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Refactoring JavaScript
6 |
7 |
8 |
9 |
10 |
11 | Conclusion
12 |
13 | The techniques we covered ranged from things as small as renaming variables, to as large as applying paradigms like FP and OOP. Between paradigms, architectures, libraries, frameworks, and even individual style your "JavaScript" might be wildly different than someone else's. You might even find yourself in two very different modes from one project to another. If you write a simple 3 line shell script in JavaScript, you might not even want tests or write any functions. You might completely leave off scoping declarations, even a simple var
. You might be working with a team that thinks well in OOP. In that case, ES2015 classes and applying appropriate design patterns might be the way to go. For more functional (or just function-curious) teams, FP is a great approach. It doesn't have to be all-or-nothing, and it doesn't have to be scary. Turn your linter off every once in a while.
14 |
15 | Whether you're making decisions about small stylistic changes or large architectural changes, ideally this book has provided you with a good basis for making choices. The alternatives of simply using a framework, rewriting into another framework, or putting up with JavaScript Jenga are comparatively expensive and demoralizing.
16 |
17 | "JavaScript" is basically a mythological animal. "Your JavaScript" starts with whatever styles, tools, platforms, paradigms, ideologies, and purposes you decide to pursue. Having a well developed "your JavaScript" opens up a tremendous amount of possibilities.
18 |
19 | Hopefully, this book has helped you craft "your JavaScript," leading to creating more maintainable, confidence-inspiring code bases.
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/code/ch01/1_all.js:
--------------------------------------------------------------------------------
1 | function byTwo(number){
2 | return number * 2;
3 | }
4 |
5 | function byTwo(number){
6 | return number << 1;
7 | }
8 |
9 | assert = require('assert');
10 | assert(2 + 2 === 4);
11 |
12 | _ = require('underscore');
13 | assert(_.first([3, 2] === 3));
14 |
15 | function doesThings(args, callback){
16 | doesOtherThings(args);
17 | doesOtherOtherThings(args, callback);
18 | return 5;
19 | };
20 |
--------------------------------------------------------------------------------
/code/ch04/1_new_code_from_scratch/1_check_hand.js:
--------------------------------------------------------------------------------
1 | var checkHand = function(hand){
2 | if (checkStraightFlush(hand)){
3 | return 'straight flush';
4 | }
5 | else if (checkFourOfKind(hand)){
6 | return 'four of a kind';
7 | }
8 | else if (checkFullHouse(hand)){
9 | return 'full house';
10 | }
11 | else if (checkFlush(hand)){
12 | return 'flush';
13 | }
14 | else if (checkStraight(hand)){
15 | return 'straight';
16 | }
17 | else if (checkThreeOfKind(hand)){
18 | return 'three of a kind';
19 | }
20 | else if (checkTwoPair(hand)){
21 | return 'two pair';
22 | }
23 | else if (checkPair(hand)){
24 | return 'pair';
25 | }
26 | else {
27 | return 'high card';
28 | }
29 | };
30 |
31 | console.log(checkHand(['2-H', '3-C', '4-D', '5-H', '2-C']));
32 |
--------------------------------------------------------------------------------
/code/ch04/1_new_code_from_scratch/2_check_hand.js:
--------------------------------------------------------------------------------
1 | // not just multiples
2 | checkStraightFlush = function(){
3 | return false;
4 | };
5 | checkFullHouse = function(){
6 | return false;
7 | };
8 | checkFlush = function(){
9 | return false;
10 | };
11 | checkStraight = function(){
12 | return false;
13 | };
14 | checkStraightFlush = function(){
15 | return false;
16 | };
17 | checkTwoPair = function(){
18 | return false;
19 | };
20 |
21 | // just multiples
22 | checkFourOfKind = function(){
23 | return false;
24 | };
25 | checkThreeOfKind = function(){
26 | return false;
27 | };
28 | checkPair = function(){
29 | return false;
30 | };
31 |
32 | // get just the values
33 | var getValues = function(hand){
34 | console.log(hand);
35 | var values = [];
36 | for(var i=0;i numberOfDuplicates){
68 | numberOfDuplicates = duplicatesOfThisCard;
69 | }
70 | }
71 | return numberOfDuplicates;
72 | };
73 |
74 | var checkHand = function(hand){
75 | var values = getValues(hand);
76 | var number = countDuplicates(values);
77 | console.log(number);
78 |
79 | if (checkStraightFlush(hand)){
80 | return 'straight flush';
81 | }
82 | else if (number==4){
83 | return 'four of a kind';
84 | }
85 | else if (checkFullHouse(hand)){
86 | return 'full house';
87 | }
88 | else if (checkFlush(hand)){
89 | return 'flush';
90 | }
91 | else if (checkStraight(hand)){
92 | return 'straight';
93 | }
94 | else if (number==3){
95 | return 'three of a kind';
96 | }
97 | else if (checkTwoPair(hand)){
98 | return 'two pair';
99 | }
100 | else if (number==2){
101 | return 'pair';
102 | }
103 | else {
104 | return 'high card';
105 | }
106 | };
107 | // debugger;
108 | var assert = require('assert');
109 | assert(checkHand(['2-H', '3-C', '4-D', '5-H', '2-C'])==='pair');
110 | assert(checkHand(['3-H', '3-C', '3-D',
111 | '5-H', '2-H'])==='three of a kind');
112 |
--------------------------------------------------------------------------------
/code/ch04/2_new_code_tdd/1_new_code_tdd.js:
--------------------------------------------------------------------------------
1 | var checkHand = function(hand){
2 | if(hand[0]==='2-H' && hand[1]==='3-C'
3 | && hand[2]==='4-D' && hand[3]==='5-H'
4 | && hand[4]==='2-C'){
5 | return 'pair';
6 | }else{
7 | return 'three of a kind';
8 | }
9 | };
10 | var wish = require('wish');
11 | wish(checkHand(['2-H', '3-C', '4-D', '5-H', '2-C'])==='pair');
12 | wish(checkHand(['3-H', '3-C', '3-D', '5-H', '2-H'])==='three of a kind');
13 |
--------------------------------------------------------------------------------
/code/ch04/2_new_code_tdd/2_check_hand_with_mocha.js:
--------------------------------------------------------------------------------
1 | var wish = require('wish');
2 | var deepEqual = require('deep-equal');
3 |
4 | function checkHand(hand) {
5 | if(isTwoPair(hand)){
6 | return 'two pair';
7 | } else if(isPair(hand)){
8 | return 'pair';
9 | }else if(isFullHouse(hand)){
10 | return 'full house';
11 | }else if(isTriple(hand)){
12 | return 'three of a kind';
13 | }else if(isQuadruple(hand)){
14 | return 'four of a kind';
15 | }else if(isStraightFlush(hand)){
16 | return 'straight flush';
17 | }else if(isFlush(hand)){
18 | return 'flush';
19 | }else if(isStraight(hand)){
20 | return 'straight';
21 | }else{
22 | return 'high card';
23 | }
24 | };
25 |
26 | function isTwoPair(hand){
27 | var theCounts = allCounts(valuesFromHand(hand));
28 | return(theCounts[0]===2 && theCounts[1]===2);
29 | };
30 |
31 | function allCounts(values){
32 | var counts = {};
33 | values.forEach(function(value, index){
34 | counts[value]= 0;
35 | if(value == values[0]){
36 | counts[value] = counts[value] + 1;
37 | };
38 | if(value == values[1]){
39 | counts[value] = counts[value] + 1;
40 | };
41 | if(value == values[2]){
42 | counts[value] = counts[value] + 1;
43 | };
44 | if(value == values[3]){
45 | counts[value] = counts[value] + 1;
46 | };
47 | if(value == values[4]){
48 | counts[value] = counts[value] + 1;
49 | };
50 | });
51 | var totalCounts = Object.keys(counts).map(function(key){
52 | return counts[key];
53 | });
54 | return totalCounts.sort(function(a,b){return b-a});
55 | };
56 |
57 | function isFullHouse(hand){
58 | var theCounts = allCounts(valuesFromHand(hand));
59 | return(theCounts[0]===3 && theCounts[1]===2);
60 | };
61 |
62 | function isStraightFlush(hand){
63 | return isStraight(hand) && isFlush(hand);
64 | }
65 |
66 | function cardsInSequence(values){
67 | var sortedValues = values.sort();
68 | return fourAway(sortedValues) && noMultiples(values);
69 | };
70 |
71 | function isStraight(hand){
72 | return cardsInSequence(valuesFromHand(hand));
73 | };
74 |
75 | function isFlush(hand){
76 | return allTheSameSuit(suitsFor(hand));
77 | };
78 |
79 | function isQuadruple(hand){
80 | return multiplesIn(hand) === 4;
81 | };
82 |
83 | function isPair(hand){
84 | return multiplesIn(hand) === 2;
85 | };
86 |
87 | function isTriple(hand){
88 | return multiplesIn(hand) === 3;
89 | };
90 |
91 | function multiplesIn(hand){
92 | return highestCount(valuesFromHand(hand));
93 | };
94 |
95 | function highestCount(values){
96 | var counts = {};
97 | values.forEach(function(value, index){
98 | counts[value]= 0;
99 | if(value == values[0]){
100 | counts[value] = counts[value] + 1;
101 | };
102 | if(value == values[1]){
103 | counts[value] = counts[value] + 1;
104 | };
105 | if(value == values[2]){
106 | counts[value] = counts[value] + 1;
107 | };
108 | if(value == values[3]){
109 | counts[value] = counts[value] + 1;
110 | };
111 | if(value == values[4]){
112 | counts[value] = counts[value] + 1;
113 | };
114 | });
115 | var totalCounts = Object.keys(counts).map(function(key){
116 | return counts[key];
117 | });
118 | return totalCounts.sort(function(a,b){return b-a})[0];
119 | };
120 |
121 | function valuesFromHand(hand){
122 | return hand.map(function(card){
123 | return card.split('-')[0];
124 | })
125 | };
126 |
127 | function suitsFor(hand){
128 | return hand.map(function(card){
129 | return card.split('-')[1];
130 | })
131 | };
132 |
133 | function allTheSameSuit(suits){
134 | var toReturn = true;
135 | suits.forEach(function(suit){
136 | if(suit !== suits[0]){
137 | toReturn = false;
138 | }
139 | })
140 | return toReturn;
141 | }
142 | function fourAway(values){
143 | return ((+values[values.length-1] - 4 - +values[0])===0);
144 | };
145 | function noMultiples(values){
146 | return highestCount(values)==1;
147 | };
148 |
149 | describe('noMultiples()', function() {
150 | it('reports true when all elements are different', function() {
151 | var result = noMultiples(['2', '6']);
152 | wish(result);
153 | });
154 | it('reports false when two elements are the same', function() {
155 | var result = noMultiples(['2', '2']);
156 | wish(!result);
157 | });
158 | });
159 | describe('fourAway()', function() {
160 | it('reports true if first and last are 4 away', function() {
161 | var result = fourAway(['2', '6']);
162 | wish(result);
163 | });
164 | });
165 | describe('allTheSameSuit()', function() {
166 | it('reports true if elements are the same', function() {
167 | var result = allTheSameSuit(['D', 'D', 'D', 'D', 'D']);
168 | wish(result);
169 | });
170 | it('reports false if elements are not the same', function() {
171 | var result = allTheSameSuit(['D', 'H', 'D', 'D', 'D']);
172 | wish(!result);
173 | });
174 |
175 | });
176 |
177 | describe('valuesFromHand()', function() {
178 | it('returns just the values from a hand', function() {
179 | var result = valuesFromHand(['2-H', '3-C', '4-D', '5-H', '2-C']);
180 | wish(deepEqual(result, ['2', '3', '4', '5', '2']));
181 | });
182 | });
183 |
184 | describe('highestCount()', function() {
185 | it('returns count of the most common card from array',
186 | function() {
187 | var result = highestCount(['2', '4', '4', '4', '2']);
188 | wish(result === 3);
189 | }
190 | );
191 | });
192 |
193 | describe('multiplesIn()', function() {
194 | it('finds a duplicate', function() {
195 | var result = multiplesIn(['2-H', '3-C', '4-D', '5-H', '2-C']);
196 | wish(result === 2);
197 | });
198 | });
199 | describe('isPair()', function() {
200 | it('finds a pair', function() {
201 | var result = isPair(['2-H', '3-C', '4-D', '5-H', '2-C']);
202 | wish(result);
203 | });
204 | });
205 |
206 |
207 | describe('checkHand()', function() {
208 | it('handles pairs', function() {
209 | var result = checkHand(['2-H', '3-C', '4-D', '5-H', '2-C']);
210 | wish(result === 'pair');
211 |
212 | var anotherResult = checkHand(['3-H', '3-C',
213 | '4-D', '5-H', '2-C']);
214 | wish(anotherResult === 'pair');
215 | });
216 | it('handles three of a kind', function() {
217 | var result = checkHand(['3-H', '3-C', '3-D', '5-H', '2-H']);
218 | wish(result === 'three of a kind');
219 | });
220 | it('handles four of a kind', function() {
221 | var result = checkHand(['3-H', '3-C', '3-D', '3-S', '2-H']);
222 | wish(result === 'four of a kind');
223 | });
224 | it('handles high card', function() {
225 | var result = checkHand(['2-H', '5-C', '9-D', '7-S', '3-H']);
226 | wish(result === 'high card');
227 | });
228 | it('handles flush', function() {
229 | var result = checkHand(['2-H', '5-H', '9-H', '7-H', '3-H']);
230 | wish(result === 'flush');
231 | });
232 | it('handles straight', function() {
233 | var result = checkHand(['1-H', '2-H', '3-H', '4-H', '5-D']);
234 | wish(result === 'straight');
235 | });
236 | it('handles straight flush', function() {
237 | var result = checkHand(['1-H', '2-H', '3-H', '4-H', '5-H']);
238 | wish(result === 'straight flush');
239 | });
240 | it('handles full house', function() {
241 | var result = checkHand(['2-D', '2-H', '3-H', '3-D', '3-C']);
242 | wish(result === 'full house');
243 | });
244 | it('handles two pair', function() {
245 | var result = checkHand(['2-D', '2-H', '3-H', '3-D', '8-D']);
246 | wish(result === 'two pair');
247 | });
248 | });
249 |
--------------------------------------------------------------------------------
/code/ch04/3_untested_code/1_random_hand.js:
--------------------------------------------------------------------------------
1 | var s = ['H', 'D', 'S', 'C'];
2 | var v = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
3 | var c = [];
4 | var rS = function(){
5 | return s[Math.floor(Math.random()*(s.length))];
6 | };
7 | var rV = function(){
8 | return v[Math.floor(Math.random()*(v.length))];
9 | };
10 | var rC = function(){
11 | return rV() + '-' + rS();
12 | };
13 |
14 | var doIt = function(){
15 | c.push(rC());
16 | c.push(rC());
17 | c.push(rC());
18 | c.push(rC());
19 | c.push(rC());
20 | };
21 | doIt();
22 | console.log(c);
23 |
--------------------------------------------------------------------------------
/code/ch04/3_untested_code/2_random_hand.js:
--------------------------------------------------------------------------------
1 | var s = ['H', 'D', 'S', 'C'];
2 | var v = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
3 | var c = [];
4 | var rS = function(){
5 | return s[Math.floor(Math.random()*(s.length))];
6 | };
7 | var rV = function(){
8 | return v[Math.floor(Math.random()*(v.length))];
9 | };
10 | var rC = function(){
11 | return rV() + '-' + rS();
12 | };
13 |
14 | var doIt = function(){
15 | var c = [];
16 | c.push(rC());
17 | c.push(rC());
18 | c.push(rC());
19 | c.push(rC());
20 | c.push(rC());
21 | return c;
22 | };
23 | console.log(doIt());
24 |
25 | const wish = require('wish');
26 | describe('doIt()', function() {
27 | it('returns something with a length of 5', function() {
28 | wish(doIt().length === 5);
29 | });
30 | });
31 | describe('rC()', function() {
32 | it('returns match for card', function() {
33 | wish(rC().match(/\w{1,2}-[HDSC]/));
34 | });
35 | });
36 | describe('rV()', function() {
37 | it('returns match for card value', function() {
38 | wish(rV().match(/\w{1,2}/));
39 | });
40 | });
41 | describe('rS()', function() {
42 | it('returns match for suit', function() {
43 | wish(rS().match(/[HDSC]/));
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/code/ch04/4_debugging_regression/1_random_hand.js:
--------------------------------------------------------------------------------
1 | var suits = ['H', 'D', 'S', 'C'];
2 | var values = ['1', '2', '3', '4', '5', '6',
3 | '7', '8', '9', '10', 'J', 'Q', 'K'];
4 | var randomSuit = function(){
5 | return suits[Math.floor(Math.random()*(suits.length))];
6 | };
7 | var randomValue = function(){
8 | return values[Math.floor(Math.random()*(values.length))];
9 | };
10 | var randomCard = function(){
11 | return randomValue() + '-' + randomSuit();
12 | };
13 |
14 | var randomHand = function(){
15 | var cards = [];
16 | var cardArray = buildCardArray();
17 | [cards[0], cardArray] = spliceCard(cardArray);
18 | [cards[1], cardArray] = spliceCard(cardArray);
19 | [cards[2], cardArray] = spliceCard(cardArray);
20 | [cards[3], cardArray] = spliceCard(cardArray);
21 | [cards[4], cardArray] = spliceCard(cardArray);
22 | return cards;
23 | };
24 |
25 | var buildCardArray = function(){
26 | var tempArray = [];
27 | for(var i=0; i < values.length; i++){
28 | for(var j=0; j < suits.length; j++){
29 | tempArray.push(values[i]+'-'+suits[j])
30 | }
31 | };
32 | return tempArray;
33 | };
34 | var spliceCard = function(cardArray){
35 | var takeAway = cardArray.splice(
36 | Math.floor(Math.random()*cardArray.length), 1)[0];
37 | return [takeAway, cardArray];
38 | };
39 | console.log(randomHand());
40 |
41 | var wish = require('wish');
42 | var deepEqual = require('deep-equal');
43 |
44 | describe('randomHand()', function() {
45 | it('returns 5 randomCards', function() {
46 | wish(randomHand().length === 5);
47 | });
48 | for(var i=0; i<100; i++){
49 | it('should not have the first two cards be the same', function() {
50 | var result = randomHand();
51 | wish(result[0] !== result[1]);
52 | });
53 | };
54 | });
55 | describe('randomCard()', function() {
56 | it('returns nothing', function() {
57 | wish(randomCard().match(/\w{1,2}-[HDSC]/));
58 | });
59 | });
60 | describe('randomValue()', function() {
61 | it('returns nothing', function() {
62 | wish(randomValue().match(/\w{1,2}/));
63 | });
64 | });
65 | describe('randomSuit()', function() {
66 | it('returns nothing', function() {
67 | wish(randomSuit().match(/[HDSC]/));
68 | });
69 | });
70 | describe('buildCardArray()', function() {
71 | it('gives a card array', function() {
72 | wish(deepEqual(buildCardArray(), [ '1-H', '1-D', '1-S', '1-C',
73 | '2-H', '2-D', '2-S', '2-C',
74 | '3-H', '3-D', '3-S', '3-C', '4-H', '4-D', '4-S', '4-C',
75 | '5-H', '5-D', '5-S', '5-C', '6-H', '6-D', '6-S', '6-C',
76 | '7-H', '7-D', '7-S', '7-C', '8-H', '8-D', '8-S', '8-C',
77 | '9-H', '9-D', '9-S', '9-C', '10-H', '10-D', '10-S', '10-C',
78 | 'J-H', 'J-D', 'J-S', 'J-C', 'Q-H', 'Q-D', 'Q-S', 'Q-C',
79 | 'K-H', 'K-D', 'K-S', 'K-C' ]));
80 | });
81 | it('returns a full deck', function() {
82 | wish(buildCardArray().length === 52);
83 | });
84 | });
85 | describe('spliceCard()', function() {
86 | it('returns two things', function() {
87 | wish(spliceCard(buildCardArray()).length === 2);
88 | });
89 | it('returns the selected card', function() {
90 | wish(spliceCard(buildCardArray())[0].match(/\w{1,2}-[HDSC]/));
91 | });
92 | it('returns an array with one card gone', function() {
93 | wish(spliceCard(buildCardArray())[1].length ===
94 | buildCardArray().length - 1);
95 | });
96 | });
97 |
--------------------------------------------------------------------------------
/code/ch05/1_general/general.js:
--------------------------------------------------------------------------------
1 | var calculator = {
2 | add: function(a, b){
3 | return a + b;
4 | },
5 | addTwo: function(a){
6 | return this.add(a, this.two);
7 | },
8 | two: 2
9 | };
10 |
11 | var name = "Max";
12 | var punctuation = "!";
13 | function sayHi(){
14 | return "Hi " + name + punctuation;
15 | };
16 |
17 | function trueForTruthyThings(sometimesTruthyThing){
18 | return !!(sometimesTruthyThing);
19 | };
20 |
21 | function add(a, b){
22 | return a + b;
23 | };
24 |
25 | function doesSomething(options){
26 | if(options.a){
27 | var a = options.a;
28 | }
29 | if(options.b){
30 | var b = options.b;
31 | }
32 | // ...
33 | }
34 |
35 | search('something', 20);
36 | //vs.
37 | search({query: 'something', pageSize: 20});
38 |
39 | function search({query: query, pageSize: pageSize}){
40 | console.log(query, pageSize);
41 | };
42 | search({query: 'something', pageSize: 20});
43 |
44 | function add(a, b){
45 | a + b;
46 | };
47 |
48 | function moreThanThree(number){
49 | if(number > 3){
50 | return true;
51 | } else {
52 | return "No. The number was only " + number + ".";
53 | }
54 | };
55 |
56 | function printValue(value){
57 | console.log(value);
58 | };
59 |
--------------------------------------------------------------------------------
/code/ch05/2_context/1_background.js:
--------------------------------------------------------------------------------
1 | console.log('blah');
2 | this.console.log('blah');
3 | window.console.log('blah'); // in a browser
4 | global.console.log('blah'); // in a node shell
5 |
6 | var x = function(){
7 | console.log(this);
8 | };
9 | x(); // here, we'll see our global object, even in node files
10 |
11 | var x = function(){
12 | 'use strict'
13 | console.log(this);
14 | }
15 | x();
16 | 'use strict'
17 | var x = function(){
18 | console.log(this);
19 | }
20 | x();
21 |
--------------------------------------------------------------------------------
/code/ch05/2_context/2_another_object_variants.js:
--------------------------------------------------------------------------------
1 |
2 | var anObject = {
3 | number: 5,
4 | getNumber: function(){ return this.number }
5 | }
6 |
7 | console.log(anObject.getNumber());
8 |
9 |
10 | var anObject =
11 | Object.create(null, {"number": {value: 5},
12 | "getNumber": {value: function(){return this.number}}})
13 | console.log(anObject.getNumber());
14 |
15 |
16 | class AnObject{
17 | constructor(){
18 | this.number = 5;
19 | this.getNumber = function(){return this.number}
20 | }
21 | }
22 | anObject = new AnObject;
23 | console.log(anObject.getNumber());
24 |
25 | var anObject = {
26 | number: 5
27 | }
28 | var anotherObject = {
29 | getNumber: function(){ return this.number }
30 | }
31 | console.log(anotherObject.getNumber.call(anObject));
32 | console.log(anotherObject.getNumber.apply(anObject));
33 | var callForTheFirstObject = anotherObject.getNumber.bind(anObject);
34 | console.log(callForTheFirstObject());
35 |
36 | anotherObject.getNumber.call(anObject);
37 |
38 | anotherObject.getNumber();
39 |
40 | var anObject = {
41 | number: 5
42 | }
43 | var anotherObject = {
44 | getIt: function(){ return this.number },
45 | setIt: function(value){ this.number = value; return this; }
46 | }
47 | console.log(anotherObject.setIt.call(anObject, 3));
48 |
--------------------------------------------------------------------------------
/code/ch05/2_context/3_fluent_and_composed.js:
--------------------------------------------------------------------------------
1 | // fluent
2 | object.setIt(3).setIt(4).setIt(5);
3 |
4 | // composed
5 | f(g(h()));
6 |
--------------------------------------------------------------------------------
/code/ch05/3_context/1_diary.js:
--------------------------------------------------------------------------------
1 | var diary = (function(){
2 | var key = 12345;
3 | var secrets = 'rosebud';
4 |
5 | function privateUnlock(keyAttempt){
6 | if(key===keyAttempt){
7 | console.log('unlocked');
8 | diary.open = true;
9 | }else{
10 | console.log('no');
11 | }
12 | };
13 |
14 | function privateTryLock(keyAttempt){
15 | privateUnlock(keyAttempt);
16 | };
17 |
18 | function privateRead(){
19 | if(this.open){
20 | console.log(secrets);
21 | }else{
22 | console.log('no');
23 | }
24 | };
25 |
26 | return {
27 | open: false,
28 | read: privateRead,
29 | tryLock: privateTryLock
30 | }
31 |
32 | })();
33 |
34 | // run with
35 | diary.tryLock(12345);
36 | diary.read();
37 |
--------------------------------------------------------------------------------
/code/ch05/3_context/2_diary.js:
--------------------------------------------------------------------------------
1 | var diary = (function(){
2 | var key = 12345;
3 | var secrets = 'programming is just syntactic sugar for labor';
4 |
5 | function privateUnlock(keyAttempt, that){
6 | if(key===keyAttempt){
7 | console.log('unlocked');
8 | that.open = true;
9 | }else{
10 | console.log('no');
11 | }
12 | };
13 |
14 | function privateTryLock(keyAttempt){
15 | privateUnlock(keyAttempt, this);
16 | };
17 |
18 | function privateRead(){
19 | if(this.open){
20 | console.log(secrets);
21 | }else{
22 | console.log('no');
23 | }
24 | }
25 |
26 | return {
27 | open: false,
28 | read: privateRead,
29 | tryLock: privateTryLock
30 | }
31 |
32 | })();
33 | diary.tryLock(12345);
34 | diary.read();
35 |
--------------------------------------------------------------------------------
/code/ch05/3_context/3_diary.js:
--------------------------------------------------------------------------------
1 | var diary = (function(){
2 | var key = 12345;
3 | var secrets = 'sitting for 8 hrs/day straight considered harmful';
4 |
5 | function privateUnlock(keyAttempt){
6 | if(key===keyAttempt){
7 | console.log('unlocked');
8 | this.open = true;
9 | }else{
10 | console.log('no');
11 | }
12 | };
13 |
14 | function privateTryLock(keyAttempt){
15 | privateUnlock.call(this, keyAttempt);
16 | };
17 |
18 | function privateRead(){
19 | if(this.open){
20 | console.log(secrets);
21 | }else{
22 | console.log('no');
23 | }
24 | };
25 |
26 | return {
27 | open: false,
28 | read: privateRead,
29 | tryLock: privateTryLock
30 | };
31 |
32 | })();
33 | diary.tryLock(12345);
34 | diary.read();
35 |
36 | // bind variants
37 | // function privateTryLock(keyAttempt){
38 | // var boundUnlock = privateUnlock.bind(this);
39 | // boundUnlock(keyAttempt);
40 | // };
41 | //
42 | // function privateTryLock(keyAttempt){
43 | // privateUnlock.bind(this)(keyAttempt);
44 | // };
45 |
--------------------------------------------------------------------------------
/code/ch05/3_context/4_diary.js:
--------------------------------------------------------------------------------
1 | class Diary {
2 | constructor(){
3 | this.open = false;
4 | this._key = 12345;
5 | this._secrets = 'the average human lives around 1000 months';
6 | };
7 |
8 | _unlock(keyAttempt){
9 | if(this._key===keyAttempt){
10 | console.log('unlocked');
11 | this.open = true;
12 | }else{
13 | console.log('no');
14 | }
15 | };
16 | tryLock(keyAttempt){
17 | this._unlock(keyAttempt);
18 | };
19 |
20 | read(){
21 | if(this.open){
22 | console.log(this._secrets);
23 | }else{
24 | console.log('no')
25 | }
26 | }
27 | }
28 | d = new Diary();
29 | d.tryLock(12345);
30 | d.read();
31 |
--------------------------------------------------------------------------------
/code/ch05/3_context/5_diary.js:
--------------------------------------------------------------------------------
1 | var key = 12345;
2 | var secrets = 'rectangles are popular with people, but not nature';
3 | function globalUnlock(keyAttempt){
4 | if(key===keyAttempt){
5 | console.log('unlocked');
6 | this.open = true;
7 | }else{
8 | console.log('no');
9 | }
10 | };
11 |
12 | class Diary {
13 | constructor(){
14 | this.open = false;
15 | };
16 | tryLock(keyAttempt){
17 | globalUnlock.bind(this)(keyAttempt);
18 | };
19 | read(){
20 | if(this.open){
21 | console.log(secrets);
22 | }else{
23 | console.log('no');
24 | }
25 | }
26 | };
27 | d = new Diary();
28 | d.tryLock(12345);
29 | d.read();
30 |
--------------------------------------------------------------------------------
/code/ch05/3_context/6_diary_module.js:
--------------------------------------------------------------------------------
1 | var key = 12345;
2 | var secrets='how to win friends/influence people is for psychopaths';
3 | function globalUnlock(keyAttempt){
4 | if(key===keyAttempt){
5 | console.log('unlocked');
6 | this.open = true;
7 | }else{
8 | console.log('no');
9 | }
10 | };
11 |
12 | module.exports = class Diary {
13 | constructor(){
14 | this.open = false;
15 | };
16 |
17 | tryLock(keyAttempt){
18 | globalUnlock.bind(this)(keyAttempt);
19 | };
20 |
21 | read(){
22 | if(this.open){
23 | console.log(secrets);
24 | }else{
25 | console.log('no')
26 | }
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/code/ch05/3_context/7_diary_reader.js:
--------------------------------------------------------------------------------
1 | const Diary = require('./diary_module.js');
2 | let d = new Diary();
3 | d.tryLock(12345);
4 | d.read();
5 |
--------------------------------------------------------------------------------
/code/ch05/3_context/8_possible_future_privacy.js:
--------------------------------------------------------------------------------
1 | class Foo {
2 | #a;
3 | #b;
4 | #sum() { return #a + #b; }
5 | printSum() { console.log(#sum()); }
6 | constructor(a, b) { #a = a; #b = b; }
7 | };
8 |
--------------------------------------------------------------------------------
/code/ch05/3_context/short-parts.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | console.log('hi');
3 | })();
4 |
5 | (function(){
6 | (function(){
7 | console.log('hi');
8 | })();
9 | })();
10 |
11 | function hi(){
12 | console.log(hello);
13 | };
14 | hi();
15 |
16 | // ReferenceError: hello is not defined
17 |
18 | var hello = "hi";
19 | hi();
20 | // logs "hi"
21 |
--------------------------------------------------------------------------------
/code/ch06/1_initial.js:
--------------------------------------------------------------------------------
1 | fs = require('fs');
2 | // songs
3 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
4 | somewhere_over_the_rainbow = ['c', 'em', 'f', 'g', 'am'];
5 | tooManyCooks = ['c', 'g', 'f'];
6 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
7 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
8 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
9 | army = ['ab', 'ebm7', 'dbadd9', 'fm7', 'bbm', 'abmaj7', 'ebm'];
10 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7',
11 | 'em7', 'a7', 'f7', 'b'];
12 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7',
13 | 'g7'];
14 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
15 | song_11 = [];
16 |
17 | var songs = [];
18 | var labels = [];
19 | var allChords = [];
20 | var labelCounts = [];
21 | var labelProbabilities = [];
22 | var chordCountsInLabels = {};
23 | var probabilityOfChordsInLabels = {};
24 |
25 | function train(chords, label){
26 | songs.push([label, chords]);
27 | labels.push(label);
28 | for (var i = 0; i < chords.length; i++){
29 | if(!allChords.includes(chords[i])){
30 | allChords.push(chords[i]);
31 | }
32 | }
33 | if(!!(Object.keys(labelCounts).includes(label))){
34 | labelCounts[label] = labelCounts[label] + 1;
35 | } else {
36 | labelCounts[label] = 1;
37 | }
38 | };
39 |
40 | function getNumberOfSongs(){
41 | return songs.length;
42 | };
43 |
44 | function setLabelProbabilities(){
45 | Object.keys(labelCounts).forEach(function(label){
46 | var numberOfSongs = getNumberOfSongs();
47 | labelProbabilities[label] = labelCounts[label] / numberOfSongs;
48 | });
49 | };
50 |
51 | function setChordCountsInLabels(){
52 | songs.forEach(function(i){
53 | if(chordCountsInLabels[i[0]] === undefined){
54 | chordCountsInLabels[i[0]] = {};
55 | }
56 | i[1].forEach(function(j){
57 | if(chordCountsInLabels[i[0]][j] > 0){
58 | chordCountsInLabels[i[0]][j] =
59 | chordCountsInLabels[i[0]][j] + 1;
60 | } else {
61 | chordCountsInLabels[i[0]][j] = 1;
62 | }
63 | });
64 | });
65 | }
66 |
67 | function setProbabilityOfChordsInLabels(){
68 | probabilityOfChordsInLabels = chordCountsInLabels;
69 | Object.keys(probabilityOfChordsInLabels).forEach(function(i){
70 | Object.keys(probabilityOfChordsInLabels[i]).forEach(function(j){
71 | probabilityOfChordsInLabels[i][j] =
72 | probabilityOfChordsInLabels[i][j] * 1.0 / songs.length;
73 | });
74 | });
75 | }
76 |
77 | train(imagine, 'easy');
78 | train(somewhere_over_the_rainbow, 'easy');
79 | train(tooManyCooks, 'easy');
80 | train(iWillFollowYouIntoTheDark, 'medium');
81 | train(babyOneMoreTime, 'medium');
82 | train(creep, 'medium');
83 | train(paperBag, 'hard');
84 | train(toxic, 'hard');
85 | train(bulletproof, 'hard');
86 |
87 | setLabelProbabilities();
88 | setChordCountsInLabels();
89 | setProbabilityOfChordsInLabels();
90 |
91 |
92 |
93 | function classify(chords){
94 | var ttal = labelProbabilities;
95 | console.log(ttal);
96 | var classified = {};
97 | Object.keys(ttal).forEach(function(obj){
98 | var first = labelProbabilities[obj] + 1.01;
99 | chords.forEach(function(chord){
100 | var probabilityOfChordInLabel =
101 | probabilityOfChordsInLabels[obj][chord];
102 | if(probabilityOfChordInLabel === undefined){
103 | first + 1.01;
104 | } else {
105 | first = first * (probabilityOfChordInLabel + 1.01);
106 | }
107 | });
108 | classified[obj] = first;
109 | });
110 | console.log(classified);
111 | };
112 |
113 | classify(['d', 'g', 'e', 'dm']);
114 | classify(['f#m7', 'a', 'dadd9', 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
115 |
--------------------------------------------------------------------------------
/code/ch06/2_after_renaming.js:
--------------------------------------------------------------------------------
1 | fs = require('fs');
2 | // songs
3 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
4 | somewhereOverTheRainbow = ['c', 'em', 'f', 'g', 'am'];
5 | tooManyCooks = ['c', 'g', 'f'];
6 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
7 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
8 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
9 | army = ['ab', 'ebm7', 'dbadd9', 'fm7', 'bbm', 'abmaj7', 'ebm'];
10 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7',
11 | 'em7', 'a7', 'f7', 'b'];
12 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7',
13 | 'g7'];
14 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
15 | blankSong = [];
16 |
17 | var songs = [];
18 | var labels = [];
19 | var allChords = [];
20 | var labelCounts = [];
21 | var labelProbabilities = [];
22 | var chordCountsInLabels = {};
23 | var probabilityOfChordsInLabels = {};
24 |
25 | function train(chords, label){
26 | songs.push([label, chords]);
27 | labels.push(label);
28 | for (var index = 0; index < chords.length; index++){
29 | if(!allChords.includes(chords[index])){
30 | allChords.push(chords[index]);
31 | }
32 | }
33 | if(!!(Object.keys(labelCounts).includes(label))){
34 | labelCounts[label] = labelCounts[label] + 1;
35 | } else {
36 | labelCounts[label] = 1;
37 | }
38 | };
39 |
40 | function getNumberOfSongs(){
41 | return songs.length;
42 | };
43 |
44 | function setLabelProbabilities(){
45 | Object.keys(labelCounts).forEach(function(label){
46 | var numberOfSongs = getNumberOfSongs();
47 | labelProbabilities[label] = labelCounts[label] / numberOfSongs;
48 | });
49 | };
50 |
51 | function setChordCountsInLabels(){
52 | songs.forEach(function(song){
53 | if(chordCountsInLabels[song[0]] === undefined){
54 | chordCountsInLabels[song[0]] = {};
55 | }
56 | song[1].forEach(function(chord){
57 | if(chordCountsInLabels[song[0]][chord] > 0){
58 | chordCountsInLabels[song[0]][chord] =
59 | chordCountsInLabels[song[0]][chord] + 1;
60 | } else {
61 | chordCountsInLabels[song[0]][chord] = 1;
62 | }
63 | });
64 | });
65 | }
66 |
67 | function setProbabilityOfChordsInLabels(){
68 | probabilityOfChordsInLabels = chordCountsInLabels;
69 | Object.keys(probabilityOfChordsInLabels).forEach(function(difficulty){
70 | Object.keys(probabilityOfChordsInLabels[difficulty]).forEach(function(chord){
71 | probabilityOfChordsInLabels[difficulty][chord] =
72 | probabilityOfChordsInLabels[difficulty][chord] * 1.0 / songs.length;
73 | });
74 | });
75 | }
76 |
77 | train(imagine, 'easy');
78 | train(somewhereOverTheRainbow, 'easy');
79 | train(tooManyCooks, 'easy');
80 | train(iWillFollowYouIntoTheDark, 'medium');
81 | train(babyOneMoreTime, 'medium');
82 | train(creep, 'medium');
83 | train(paperBag, 'hard');
84 | train(toxic, 'hard');
85 | train(bulletproof, 'hard');
86 |
87 | setLabelProbabilities();
88 | setChordCountsInLabels();
89 | setProbabilityOfChordsInLabels();
90 |
91 |
92 |
93 | function classify(chords){
94 | var total = labelProbabilities;
95 | console.log(total);
96 | var classified = {};
97 | Object.keys(total).forEach(function(difficulty){
98 | var first = labelProbabilities[difficulty] + 1.01;
99 | chords.forEach(function(chord){
100 | var probabilityOfChordInLabel =
101 | probabilityOfChordsInLabels[difficulty][chord];
102 | if(probabilityOfChordInLabel === undefined){
103 | first + 1.01;
104 | } else {
105 | first = first * (probabilityOfChordInLabel + 1.01);
106 | }
107 | });
108 | classified[difficulty] = first;
109 | });
110 | console.log(classified);
111 | };
112 |
113 | classify(['d', 'g', 'e', 'dm']);
114 | classify(['f#m7', 'a', 'dadd9', 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
115 |
--------------------------------------------------------------------------------
/code/ch06/3_after_useless_code.js:
--------------------------------------------------------------------------------
1 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
2 | somewhereOverTheRainbow = ['c', 'em', 'f', 'g', 'am'];
3 | tooManyCooks = ['c', 'g', 'f'];
4 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
5 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
6 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
7 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7',
8 | 'em7', 'a7', 'f7', 'b'];
9 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7',
10 | 'g7'];
11 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
12 |
13 | var songs = [];
14 | var labels = [];
15 | var allChords = [];
16 | var labelCounts = [];
17 | var labelProbabilities = [];
18 | var chordCountsInLabels = {};
19 | var probabilityOfChordsInLabels = {};
20 |
21 | function train(chords, label){
22 | songs.push([label, chords]);
23 | labels.push(label);
24 | for (var index = 0; index < chords.length; index++){
25 | if(!allChords.includes(chords[index])){
26 | allChords.push(chords[index]);
27 | }
28 | }
29 | if(Object.keys(labelCounts).includes(label)){
30 | labelCounts[label] = labelCounts[label] + 1;
31 | } else {
32 | labelCounts[label] = 1;
33 | }
34 | };
35 |
36 | function getNumberOfSongs(){
37 | return songs.length;
38 | };
39 |
40 | function setLabelProbabilities(){
41 | Object.keys(labelCounts).forEach(function(label){
42 | var numberOfSongs = getNumberOfSongs();
43 | labelProbabilities[label] = labelCounts[label] / numberOfSongs;
44 | });
45 | };
46 |
47 | function setChordCountsInLabels(){
48 | songs.forEach(function(song){
49 | if(chordCountsInLabels[song[0]] === undefined){
50 | chordCountsInLabels[song[0]] = {};
51 | }
52 | song[1].forEach(function(chord){
53 | if(chordCountsInLabels[song[0]][chord] > 0){
54 | chordCountsInLabels[song[0]][chord] =
55 | chordCountsInLabels[song[0]][chord] + 1;
56 | } else {
57 | chordCountsInLabels[song[0]][chord] = 1;
58 | }
59 | });
60 | });
61 | }
62 |
63 | function setProbabilityOfChordsInLabels(){
64 | probabilityOfChordsInLabels = chordCountsInLabels;
65 | Object.keys(probabilityOfChordsInLabels).forEach(function(difficulty){
66 | Object.keys(probabilityOfChordsInLabels[difficulty]).forEach(function(chord){
67 | probabilityOfChordsInLabels[difficulty][chord] =
68 | probabilityOfChordsInLabels[difficulty][chord] / songs.length;
69 | });
70 | });
71 | }
72 |
73 | train(imagine, 'easy');
74 | train(somewhereOverTheRainbow, 'easy');
75 | train(tooManyCooks, 'easy');
76 | train(iWillFollowYouIntoTheDark, 'medium');
77 | train(babyOneMoreTime, 'medium');
78 | train(creep, 'medium');
79 | train(paperBag, 'hard');
80 | train(toxic, 'hard');
81 | train(bulletproof, 'hard');
82 |
83 | setLabelProbabilities();
84 | setChordCountsInLabels();
85 | setProbabilityOfChordsInLabels();
86 |
87 | function classify(chords){
88 | console.log(labelProbabilities);
89 | var classified = {};
90 | Object.keys(labelProbabilities).forEach(function(difficulty){
91 | var first = labelProbabilities[difficulty] + 1.01;
92 | chords.forEach(function(chord){
93 | var probabilityOfChordInLabel =
94 | probabilityOfChordsInLabels[difficulty][chord];
95 | if(probabilityOfChordInLabel){
96 | first = first * (probabilityOfChordInLabel + 1.01);
97 | }
98 | });
99 | classified[difficulty] = first;
100 | });
101 | console.log(classified);
102 | };
103 |
104 | classify(['d', 'g', 'e', 'dm']);
105 | classify(['f#m7', 'a', 'dadd9', 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
106 |
--------------------------------------------------------------------------------
/code/ch06/4_after_variables.js:
--------------------------------------------------------------------------------
1 | var easy = 'easy';
2 | var medium = 'medium';
3 | var hard = 'hard';
4 |
5 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
6 | somewhereOverTheRainbow = ['c', 'em', 'f', 'g', 'am'];
7 | tooManyCooks = ['c', 'g', 'f'];
8 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
9 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
10 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
11 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7',
12 | 'em7', 'a7', 'f7', 'b'];
13 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7',
14 | 'g7'];
15 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
16 |
17 | var songs = [];
18 | var labels = [];
19 | var allChords = [];
20 | var labelCounts = [];
21 | var labelProbabilities = [];
22 | var chordCountsInLabels = {};
23 | var probabilityOfChordsInLabels = {};
24 |
25 | function train(chords, label){
26 | var index;
27 | songs.push([label, chords]);
28 | labels.push(label);
29 | for (index = 0; index < chords.length; index++){
30 | if(!allChords.includes(chords[index])){
31 | allChords.push(chords[index]);
32 | }
33 | }
34 | if(Object.keys(labelCounts).includes(label)){
35 | labelCounts[label] = labelCounts[label] + 1;
36 | } else {
37 | labelCounts[label] = 1;
38 | }
39 | };
40 |
41 | function setLabelProbabilities(){
42 | Object.keys(labelCounts).forEach(function(label){
43 | labelProbabilities[label] = labelCounts[label] / songs.length;
44 | });
45 | };
46 |
47 | function setChordCountsInLabels(){
48 | songs.forEach(function(song){
49 | if(chordCountsInLabels[song[0]] === undefined){
50 | chordCountsInLabels[song[0]] = {};
51 | }
52 | song[1].forEach(function(chord){
53 | if(chordCountsInLabels[song[0]][chord] > 0){
54 | chordCountsInLabels[song[0]][chord] += 1;
55 | } else {
56 | chordCountsInLabels[song[0]][chord] = 1;
57 | }
58 | });
59 | });
60 | }
61 |
62 | function setProbabilityOfChordsInLabels(){
63 | probabilityOfChordsInLabels = chordCountsInLabels;
64 | Object.keys(probabilityOfChordsInLabels).forEach(function(difficulty){
65 | Object.keys(probabilityOfChordsInLabels[difficulty]).forEach(function(chord){
66 | probabilityOfChordsInLabels[difficulty][chord] /= songs.length;
67 | });
68 | });
69 | }
70 |
71 | train(imagine, easy);
72 | train(somewhereOverTheRainbow, easy);
73 | train(tooManyCooks, easy);
74 | train(iWillFollowYouIntoTheDark, medium);
75 | train(babyOneMoreTime, medium);
76 | train(creep, medium);
77 | train(paperBag, hard);
78 | train(toxic, hard);
79 | train(bulletproof, hard);
80 |
81 | setLabelProbabilities();
82 | setChordCountsInLabels();
83 | setProbabilityOfChordsInLabels();
84 |
85 | function classify(chords){
86 | var smoothing = 1.01;
87 | console.log(labelProbabilities);
88 | var classified = {};
89 | Object.keys(labelProbabilities).forEach(function(difficulty){
90 | var first = labelProbabilities[difficulty] + smoothing;
91 | chords.forEach(function(chord){
92 | var probabilityOfChordInLabel =
93 | probabilityOfChordsInLabels[difficulty][chord]
94 | if(probabilityOfChordInLabel){
95 | first = first * (probabilityOfChordInLabel + smoothing)
96 | }
97 | })
98 | classified[difficulty] = first
99 | });
100 | console.log(classified);
101 | };
102 |
103 | classify(['d', 'g', 'e', 'dm']);
104 | classify(['f#m7', 'a', 'dadd9', 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
105 |
--------------------------------------------------------------------------------
/code/ch06/5_after_strings.js:
--------------------------------------------------------------------------------
1 | function fileName(){
2 | var theError = new Error("here I am");
3 | return theError.stack.match(/\/(\w+\.js)\:/)[1];
4 | };
5 | console.log(`Welcome to ${fileName()}!`);
6 | var easy = 'easy';
7 | var medium = 'medium';
8 | var hard = 'hard';
9 |
10 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
11 | somewhereOverTheRainbow = ['c', 'em', 'f', 'g', 'am'];
12 | tooManyCooks = ['c', 'g', 'f'];
13 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
14 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
15 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
16 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7',
17 | 'em7', 'a7', 'f7', 'b'];
18 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7',
19 | 'g7'];
20 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
21 |
22 | var songs = [];
23 | var labels = [];
24 | var allChords = [];
25 | var labelCounts = [];
26 | var labelProbabilities = [];
27 | var chordCountsInLabels = {};
28 | var probabilityOfChordsInLabels = {};
29 |
30 | function train(chords, label){
31 | var index;
32 | songs.push([label, chords]);
33 | labels.push(label);
34 | for (index = 0; index < chords.length; index++){
35 | if(!allChords.includes(chords[index])){
36 | allChords.push(chords[index]);
37 | }
38 | }
39 | if(Object.keys(labelCounts).includes(label)){
40 | labelCounts[label] = labelCounts[label] + 1;
41 | } else {
42 | labelCounts[label] = 1;
43 | }
44 | };
45 |
46 | function setLabelProbabilities(){
47 | Object.keys(labelCounts).forEach(function(label){
48 | labelProbabilities[label] = labelCounts[label] / songs.length;
49 | });
50 | };
51 |
52 | function setChordCountsInLabels(){
53 | songs.forEach(function(song){
54 | if(chordCountsInLabels[song[0]] === undefined){
55 | chordCountsInLabels[song[0]] = {};
56 | }
57 | song[1].forEach(function(chord){
58 | if(chordCountsInLabels[song[0]][chord] > 0){
59 | chordCountsInLabels[song[0]][chord] += 1;
60 | } else {
61 | chordCountsInLabels[song[0]][chord] = 1;
62 | }
63 | });
64 | });
65 | }
66 |
67 | function setProbabilityOfChordsInLabels(){
68 | probabilityOfChordsInLabels = chordCountsInLabels;
69 | Object.keys(probabilityOfChordsInLabels).forEach(function(difficulty){
70 | Object.keys(probabilityOfChordsInLabels[difficulty]).forEach(function(chord){
71 | probabilityOfChordsInLabels[difficulty][chord] /= songs.length;
72 | });
73 | });
74 | }
75 |
76 | train(imagine, easy);
77 | train(somewhereOverTheRainbow, easy);
78 | train(tooManyCooks, easy);
79 | train(iWillFollowYouIntoTheDark, medium);
80 | train(babyOneMoreTime, medium);
81 | train(creep, medium);
82 | train(paperBag, hard);
83 | train(toxic, hard);
84 | train(bulletproof, hard);
85 |
86 | setLabelProbabilities();
87 | setChordCountsInLabels();
88 | setProbabilityOfChordsInLabels();
89 |
90 | function classify(chords){
91 | var smoothing = 1.01;
92 | console.log(labelProbabilities);
93 | var classified = {};
94 | Object.keys(labelProbabilities).forEach(function(difficulty){
95 | var first = labelProbabilities[difficulty] + smoothing;
96 | chords.forEach(function(chord){
97 | var probabilityOfChordInLabel =
98 | probabilityOfChordsInLabels[difficulty][chord]
99 | if(probabilityOfChordInLabel){
100 | first = first * (probabilityOfChordInLabel + smoothing)
101 | }
102 | })
103 | classified[difficulty] = first
104 | });
105 | console.log(classified);
106 | };
107 |
108 | classify(['d', 'g', 'e', 'dm']);
109 | classify(['f#m7', 'a', 'dadd9', 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
110 |
--------------------------------------------------------------------------------
/code/ch06/6_after_working_with_arrays.js:
--------------------------------------------------------------------------------
1 | function fileName(){
2 | var theError = new Error("here I am");
3 | return theError.stack.match(/\/(\w+\.js)\:/)[1];
4 | };
5 | console.log(`Welcome to ${fileName()}!`);
6 | var easy = 'easy';
7 | var medium = 'medium';
8 | var hard = 'hard';
9 |
10 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
11 | somewhereOverTheRainbow = ['c', 'em', 'f', 'g', 'am'];
12 | tooManyCooks = ['c', 'g', 'f'];
13 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
14 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
15 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
16 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7',
17 | 'em7', 'a7', 'f7', 'b'];
18 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7',
19 | 'g7'];
20 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
21 |
22 | var songs = [];
23 | var labels = [];
24 | var allChords = [];
25 | var labelCounts = [];
26 | var labelProbabilities = [];
27 | var chordCountsInLabels = {};
28 | var probabilityOfChordsInLabels = {};
29 |
30 | function train(chords, label){
31 | songs.push([label, chords]);
32 | labels.push(label);
33 | chords.forEach(chord => {
34 | if(!allChords.includes(chord)){
35 | allChords.push(chord);
36 | }
37 | });
38 | if(Object.keys(labelCounts).includes(label)){
39 | labelCounts[label] = labelCounts[label] + 1;
40 | } else {
41 | labelCounts[label] = 1;
42 | }
43 | };
44 |
45 | function setLabelProbabilities(){
46 | Object.keys(labelCounts).forEach(function(label){
47 | labelProbabilities[label] = labelCounts[label] / songs.length;
48 | });
49 | };
50 |
51 | function setChordCountsInLabels(){
52 | songs.forEach(function(song){
53 | if(chordCountsInLabels[song[0]] === undefined){
54 | chordCountsInLabels[song[0]] = {};
55 | }
56 | song[1].forEach(function(chord){
57 | if(chordCountsInLabels[song[0]][chord] > 0){
58 | chordCountsInLabels[song[0]][chord] += 1;
59 | } else {
60 | chordCountsInLabels[song[0]][chord] = 1;
61 | }
62 | });
63 | });
64 | }
65 |
66 | function setProbabilityOfChordsInLabels(){
67 | probabilityOfChordsInLabels = chordCountsInLabels;
68 | Object.keys(probabilityOfChordsInLabels).forEach(function(difficulty){
69 | Object.keys(probabilityOfChordsInLabels[difficulty]).forEach(function(chord){
70 | probabilityOfChordsInLabels[difficulty][chord] /= songs.length;
71 | });
72 | });
73 | }
74 |
75 | train(imagine, easy);
76 | train(somewhereOverTheRainbow, easy);
77 | train(tooManyCooks, easy);
78 | train(iWillFollowYouIntoTheDark, medium);
79 | train(babyOneMoreTime, medium);
80 | train(creep, medium);
81 | train(paperBag, hard);
82 | train(toxic, hard);
83 | train(bulletproof, hard);
84 |
85 | setLabelProbabilities();
86 | setChordCountsInLabels();
87 | setProbabilityOfChordsInLabels();
88 |
89 | function classify(chords){
90 | var smoothing = 1.01;
91 | console.log(labelProbabilities);
92 | var classified = {};
93 | Object.keys(labelProbabilities).forEach(function(difficulty){
94 | var first = labelProbabilities[difficulty] + smoothing;
95 | chords.forEach(function(chord){
96 | var probabilityOfChordInLabel =
97 | probabilityOfChordsInLabels[difficulty][chord]
98 | if(probabilityOfChordInLabel){
99 | first = first * (probabilityOfChordInLabel + smoothing)
100 | }
101 | })
102 | classified[difficulty] = first
103 | });
104 | console.log(classified);
105 | };
106 |
107 | classify(['d', 'g', 'e', 'dm']);
108 | classify(['f#m7', 'a', 'dadd9', 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
109 |
--------------------------------------------------------------------------------
/code/ch07/10_after_shorthand_syntax.js:
--------------------------------------------------------------------------------
1 | const classifier = {
2 | labelCounts: new Map(),
3 | labelProbabilities: new Map(),
4 | chordCountsInLabels: new Map(),
5 | smoothing: 1.01,
6 | songList: {
7 | allChords: new Set(),
8 | difficulties: ['easy', 'medium', 'hard'],
9 | songs: [],
10 | addSong(name, chords, difficulty){
11 | this.songs.push({name, chords,
12 | difficulty: this.difficulties[difficulty]});
13 | }
14 | },
15 | chordCountForDifficulty(difficulty, testChord){
16 | return this.songList.songs.reduce((counter, song) => {
17 | if(song.difficulty === difficulty){
18 | counter += song.chords.filter(chord => chord === testChord).length;
19 | }
20 | return counter;
21 | }, 0);
22 | },
23 |
24 | likelihoodFromChord(difficulty, chord){
25 | return this.chordCountForDifficulty(difficulty, chord) /
26 | this.songList.songs.length;
27 | },
28 | valueForChordDifficulty(difficulty, chord){
29 | const value = this.likelihoodFromChord(difficulty, chord);
30 | return value ? value + this.smoothing : 1;
31 | },
32 | trainAll(){
33 | this.songList.songs.forEach((song) => {
34 | this.train(song.chords, song.difficulty);
35 | });
36 | this.setLabelProbabilities();
37 | },
38 |
39 | train(chords, label){
40 | chords.forEach(chord => this.songList.allChords.add(chord) );
41 | if(Array.from(this.labelCounts.keys()).includes(label)){
42 | this.labelCounts.set(label, this.labelCounts.get(label) + 1);
43 | } else {
44 | this.labelCounts.set(label, 1);
45 | }
46 | },
47 |
48 | setLabelProbabilities(){
49 | this.labelCounts.forEach((_count, label) => {
50 | this.labelProbabilities.set(label, this.labelCounts.get(label) /
51 | this.songList.songs.length);
52 | });
53 | },
54 |
55 | classify(chords){
56 | return new Map(Array.from(
57 | this.labelProbabilities.entries()).map((labelWithProbability) => {
58 | const difficulty = labelWithProbability[0];
59 | return [difficulty, chords.reduce((total, chord) => {
60 | return total * this.valueForChordDifficulty(difficulty, chord);
61 | }, this.labelProbabilities.get(difficulty) + this.smoothing)];
62 | }));
63 | }
64 | };
65 |
66 | const wish = require('wish');
67 | describe('the file', () => {
68 | classifier.songList.addSong('imagine',
69 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
70 | classifier.songList.addSong('somewhereOverTheRainbow',
71 | ['c', 'em', 'f', 'g', 'am'], 0);
72 | classifier.songList.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
73 | classifier.songList.addSong('iWillFollowYouIntoTheDark',
74 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
75 | classifier.songList.addSong('babyOneMoreTime',
76 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
77 | classifier.songList.addSong('creep',
78 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
79 | classifier.songList.addSong('paperBag',
80 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
81 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
82 | 'b'], 2);
83 | classifier.songList.addSong('toxic',
84 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
85 | classifier.songList.addSong('bulletproof',
86 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
87 | classifier.trainAll();
88 | it('classifies', () => {
89 | const classified = classifier.classify(['f#m7', 'a', 'dadd9',
90 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
91 | wish(classified.get('easy') === 1.3433333333333333);
92 | wish(classified.get('medium') === 1.5060259259259259);
93 | wish(classified.get('hard') === 1.6884223991769547);
94 | });
95 | it('classifies again', () => {
96 | const classified = classifier.classify(['d', 'g', 'e', 'dm']);
97 | wish(classified.get('easy') === 2.023094827160494);
98 | wish(classified.get('medium') === 1.855758613168724);
99 | wish(classified.get('hard') === 1.855758613168724);
100 | });
101 | it('label probabilities', () => {
102 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
103 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
104 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
105 | });
106 | });
107 |
--------------------------------------------------------------------------------
/code/ch07/11_after_getting_new_obj_constructor_func.js:
--------------------------------------------------------------------------------
1 | const Classifier = function(){
2 | const SongList = function() {
3 | this.allChords = new Set();
4 | this.difficulties = ['easy', 'medium', 'hard'];
5 | this.songs = [];
6 | this.addSong = function(name, chords, difficulty){
7 | this.songs.push({name,
8 | chords,
9 | difficulty: this.difficulties[difficulty]});
10 | };
11 | };
12 | this.songList = new SongList();
13 | this.labelCounts = new Map();
14 | this.labelProbabilities = new Map();
15 | this.chordCountsInLabels = new Map();
16 | this.smoothing = 1.01;
17 | this.chordCountForDifficulty = function(difficulty, testChord){
18 | return this.songList.songs.reduce((counter, song) => {
19 | if(song.difficulty === difficulty){
20 | counter += song.chords.filter(chord => chord === testChord).length;
21 | }
22 | return counter;
23 | }, 0);
24 | };
25 |
26 | this.likelihoodFromChord = function(difficulty, chord){
27 | return this.chordCountForDifficulty(difficulty, chord) /
28 | this.songList.songs.length;
29 | };
30 | this.valueForChordDifficulty = function(difficulty, chord){
31 | const value = this.likelihoodFromChord(difficulty, chord);
32 | return value ? value + this.smoothing : 1;
33 | };
34 | this.trainAll = function(){
35 | this.songList.songs.forEach((song) => {
36 | this.train(song.chords, song.difficulty);
37 | });
38 | this.setLabelProbabilities();
39 | };
40 |
41 | this.train = function(chords, label){
42 | chords.forEach(chord => this.songList.allChords.add(chord) );
43 | if(Array.from(this.labelCounts.keys()).includes(label)){
44 | this.labelCounts.set(label, this.labelCounts.get(label) + 1);
45 | } else {
46 | this.labelCounts.set(label, 1);
47 | }
48 | };
49 |
50 | this.setLabelProbabilities = function(){
51 | this.labelCounts.forEach((_count, label) => {
52 | this.labelProbabilities.set(label, this.labelCounts.get(label) /
53 | this.songList.songs.length);
54 | });
55 | };
56 |
57 | this.classify = function(chords){
58 | return new Map(Array.from(
59 | this.labelProbabilities.entries()).map((labelWithProbability) => {
60 | const difficulty = labelWithProbability[0];
61 | return [difficulty, chords.reduce((total, chord) => {
62 | return total * this.valueForChordDifficulty(difficulty, chord);
63 | }, this.labelProbabilities.get(difficulty) + this.smoothing)];
64 | }));
65 | };
66 | };
67 |
68 | const wish = require('wish');
69 | describe('the file', () => {
70 | const classifier = new Classifier();
71 | classifier.songList.addSong('imagine',
72 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
73 | classifier.songList.addSong('somewhereOverTheRainbow',
74 | ['c', 'em', 'f', 'g', 'am'], 0);
75 | classifier.songList.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
76 | classifier.songList.addSong('iWillFollowYouIntoTheDark',
77 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
78 | classifier.songList.addSong('babyOneMoreTime',
79 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
80 | classifier.songList.addSong('creep',
81 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
82 | classifier.songList.addSong('paperBag',
83 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
84 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
85 | 'b'], 2);
86 | classifier.songList.addSong('toxic',
87 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
88 | classifier.songList.addSong('bulletproof',
89 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
90 | classifier.trainAll();
91 | it('classifies', () => {
92 | const classified = classifier.classify(['f#m7', 'a', 'dadd9',
93 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
94 | wish(classified.get('easy') === 1.3433333333333333);
95 | wish(classified.get('medium') === 1.5060259259259259);
96 | wish(classified.get('hard') === 1.6884223991769547);
97 | });
98 | it('classifies again', () => {
99 | const classified = classifier.classify(['d', 'g', 'e', 'dm']);
100 | wish(classified.get('easy') === 2.023094827160494);
101 | wish(classified.get('medium') === 1.855758613168724);
102 | wish(classified.get('hard') === 1.855758613168724);
103 | });
104 | it('label probabilities', () => {
105 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
106 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
107 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
108 | });
109 | });
110 |
--------------------------------------------------------------------------------
/code/ch07/12_after_constructor_vs_factory.js:
--------------------------------------------------------------------------------
1 | const classifierTemplate = {
2 | labelCounts: new Map(),
3 | labelProbabilities: new Map(),
4 | chordCountsInLabels: new Map(),
5 | smoothing: 1.01,
6 | songList: {
7 | allChords: new Set(),
8 | difficulties: ['easy', 'medium', 'hard'],
9 | songs: [],
10 | addSong(name, chords, difficulty){
11 | this.songs.push({name, chords,
12 | difficulty: this.difficulties[difficulty]});
13 | }
14 | },
15 | chordCountForDifficulty(difficulty, testChord){
16 | return this.songList.songs.reduce((counter, song) => {
17 | if(song.difficulty === difficulty){
18 | counter += song.chords.filter(chord => chord === testChord).length;
19 | }
20 | return counter;
21 | }, 0);
22 | },
23 |
24 | likelihoodFromChord(difficulty, chord){
25 | return this.chordCountForDifficulty(difficulty, chord) /
26 | this.songList.songs.length;
27 | },
28 | valueForChordDifficulty(difficulty, chord){
29 | const value = this.likelihoodFromChord(difficulty, chord);
30 | return value ? value + this.smoothing : 1;
31 | },
32 | trainAll(){
33 | this.songList.songs.forEach((song) => {
34 | this.train(song.chords, song.difficulty);
35 | });
36 | this.setLabelProbabilities();
37 | },
38 |
39 | train(chords, label){
40 | chords.forEach(chord => this.songList.allChords.add(chord) );
41 | if(Array.from(this.labelCounts.keys()).includes(label)){
42 | this.labelCounts.set(label, this.labelCounts.get(label) + 1);
43 | } else {
44 | this.labelCounts.set(label, 1);
45 | }
46 | },
47 |
48 | setLabelProbabilities(){
49 | this.labelCounts.forEach((_count, label) => {
50 | this.labelProbabilities.set(label, this.labelCounts.get(label) /
51 | this.songList.songs.length);
52 | });
53 | },
54 |
55 | classify(chords){
56 | return new Map(Array.from(
57 | this.labelProbabilities.entries()).map((labelWithProbability) => {
58 | const difficulty = labelWithProbability[0];
59 | return [difficulty, chords.reduce((total, chord) => {
60 | return total * this.valueForChordDifficulty(difficulty, chord);
61 | }, this.labelProbabilities.get(difficulty) + this.smoothing)];
62 | }));
63 | }
64 | };
65 |
66 | const wish = require('wish');
67 | describe('the file', () => {
68 | const classifier = Object.create(classifierTemplate);
69 | classifier.songList.addSong('imagine',
70 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
71 | classifier.songList.addSong('somewhereOverTheRainbow',
72 | ['c', 'em', 'f', 'g', 'am'], 0);
73 | classifier.songList.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
74 | classifier.songList.addSong('iWillFollowYouIntoTheDark',
75 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
76 | classifier.songList.addSong('babyOneMoreTime',
77 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
78 | classifier.songList.addSong('creep',
79 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
80 | classifier.songList.addSong('paperBag',
81 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
82 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
83 | 'b'], 2);
84 | classifier.songList.addSong('toxic',
85 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
86 | classifier.songList.addSong('bulletproof',
87 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
88 | classifier.trainAll();
89 | it('classifies', () => {
90 | const classified = classifier.classify(['f#m7', 'a', 'dadd9',
91 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
92 | wish(classified.get('easy') === 1.3433333333333333);
93 | wish(classified.get('medium') === 1.5060259259259259);
94 | wish(classified.get('hard') === 1.6884223991769547);
95 | });
96 | it('classifies again', () => {
97 | const classified = classifier.classify(['d', 'g', 'e', 'dm']);
98 | wish(classified.get('easy') === 2.023094827160494);
99 | wish(classified.get('medium') === 1.855758613168724);
100 | wish(classified.get('hard') === 1.855758613168724);
101 | });
102 | it('label probabilities', () => {
103 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
104 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
105 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
106 | });
107 | });
108 |
--------------------------------------------------------------------------------
/code/ch07/13_after_class_for_classifier.js:
--------------------------------------------------------------------------------
1 | class Classifier {
2 | constructor(){
3 | this.songList = {
4 | allChords: new Set(),
5 | difficulties: ['easy', 'medium', 'hard'],
6 | songs: [],
7 | addSong(name, chords, difficulty){
8 | this.songs.push({name,
9 | chords,
10 | difficulty: this.difficulties[difficulty]});
11 | }
12 | };
13 | this.labelCounts = new Map();
14 | this.labelProbabilities = new Map();
15 | this.smoothing = 1.01;
16 | };
17 | chordCountForDifficulty(difficulty, testChord){
18 | return this.songList.songs.reduce((counter, song) => {
19 | if(song.difficulty === difficulty){
20 | counter += song.chords.filter(
21 | chord => chord === testChord
22 | ).length;
23 | }
24 | return counter;
25 | }, 0);
26 | };
27 |
28 | likelihoodFromChord(difficulty, chord){
29 | return this.chordCountForDifficulty(difficulty, chord) /
30 | this.songList.songs.length;
31 | };
32 | valueForChordDifficulty(difficulty, chord){
33 | const value = this.likelihoodFromChord(difficulty, chord);
34 | return value ? value + this.smoothing : 1;
35 | };
36 | trainAll(){
37 | this.songList.songs.forEach((song) => {
38 | this.train(song.chords, song.difficulty);
39 | });
40 | this.setLabelProbabilities();
41 | };
42 |
43 | train(chords, label){
44 | chords.forEach(chord => this.songList.allChords.add(chord) );
45 | if(Array.from(this.labelCounts.keys()).includes(label)){
46 | this.labelCounts.set(label, this.labelCounts.get(label) + 1);
47 | } else {
48 | this.labelCounts.set(label, 1);
49 | }
50 | };
51 |
52 | setLabelProbabilities(){
53 | this.labelCounts.forEach((_count, label) => {
54 | this.labelProbabilities.set(label, this.labelCounts.get(label) /
55 | this.songList.songs.length);
56 | });
57 | };
58 |
59 | classify(chords){
60 | return new Map(Array.from(
61 | this.labelProbabilities.entries()).map((labelWithProbability) => {
62 | const difficulty = labelWithProbability[0];
63 | return [difficulty, chords.reduce((total, chord) => {
64 | return total * this.valueForChordDifficulty(difficulty, chord);
65 | }, this.labelProbabilities.get(difficulty) + this.smoothing)];
66 | }));
67 | }
68 | };
69 |
70 | const wish = require('wish');
71 | describe('the file', () => {
72 | const classifier = new Classifier;
73 | classifier.songList.addSong('imagine',
74 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
75 | classifier.songList.addSong('somewhereOverTheRainbow',
76 | ['c', 'em', 'f', 'g', 'am'], 0);
77 | classifier.songList.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
78 | classifier.songList.addSong('iWillFollowYouIntoTheDark',
79 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
80 | classifier.songList.addSong('babyOneMoreTime',
81 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
82 | classifier.songList.addSong('creep',
83 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
84 | classifier.songList.addSong('paperBag',
85 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
86 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
87 | 'b'], 2);
88 | classifier.songList.addSong('toxic',
89 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
90 | classifier.songList.addSong('bulletproof',
91 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
92 | classifier.trainAll();
93 | it('classifies', () => {
94 | const classified = classifier.classify(['f#m7', 'a', 'dadd9',
95 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
96 | wish(classified.get('easy') === 1.3433333333333333);
97 | wish(classified.get('medium') === 1.5060259259259259);
98 | wish(classified.get('hard') === 1.6884223991769547);
99 | });
100 | it('classifies again', () => {
101 | const classified = classifier.classify(['d', 'g', 'e', 'dm']);
102 | wish(classified.get('easy') === 2.023094827160494);
103 | wish(classified.get('medium') === 1.855758613168724);
104 | wish(classified.get('hard') === 1.855758613168724);
105 | });
106 | it('label probabilities', () => {
107 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
108 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
109 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
110 | });
111 | });
112 |
--------------------------------------------------------------------------------
/code/ch07/14_after_choosing_api.js:
--------------------------------------------------------------------------------
1 | class Classifier {
2 | constructor(){
3 | this.songList = {
4 | allChords: new Set(),
5 | difficulties: ['easy', 'medium', 'hard'],
6 | songs: [],
7 | addSong(name, chords, difficulty){
8 | this.songs.push({name,
9 | chords,
10 | difficulty: this.difficulties[difficulty]});
11 | }
12 | };
13 | this.labelCounts = new Map();
14 | this.labelProbabilities = new Map();
15 | this.smoothing = 1.01;
16 | };
17 | addSong(...songParams){
18 | this.songList.addSong(...songParams);
19 | };
20 | chordCountForDifficulty(difficulty, testChord){
21 | return this.songList.songs.reduce((counter, song) => {
22 | if(song.difficulty === difficulty){
23 | counter += song.chords.filter(
24 | chord => chord === testChord
25 | ).length;
26 | }
27 | return counter;
28 | }, 0);
29 | };
30 |
31 | likelihoodFromChord(difficulty, chord){
32 | return this.chordCountForDifficulty(difficulty, chord) /
33 | this.songList.songs.length;
34 | };
35 | valueForChordDifficulty(difficulty, chord){
36 | const value = this.likelihoodFromChord(difficulty, chord);
37 | return value ? value + this.smoothing : 1;
38 | };
39 | trainAll(){
40 | this.songList.songs.forEach((song) => {
41 | this.train(song.chords, song.difficulty);
42 | });
43 | this.setLabelProbabilities();
44 | };
45 |
46 | train(chords, label){
47 | chords.forEach(chord => this.songList.allChords.add(chord) );
48 | if(Array.from(this.labelCounts.keys()).includes(label)){
49 | this.labelCounts.set(label, this.labelCounts.get(label) + 1);
50 | } else {
51 | this.labelCounts.set(label, 1);
52 | }
53 | };
54 |
55 | setLabelProbabilities(){
56 | this.labelCounts.forEach((_count, label) => {
57 | this.labelProbabilities.set(label, this.labelCounts.get(label) /
58 | this.songList.songs.length);
59 | });
60 | };
61 |
62 | classify(chords){
63 | return new Map(Array.from(
64 | this.labelProbabilities.entries()).map((labelWithProbability) => {
65 | const difficulty = labelWithProbability[0];
66 | return [difficulty, chords.reduce((total, chord) => {
67 | return total * this.valueForChordDifficulty(difficulty, chord);
68 | }, this.labelProbabilities.get(difficulty) + this.smoothing)];
69 | }));
70 | }
71 | };
72 |
73 | const wish = require('wish');
74 | describe('the file', () => {
75 | const classifier = new Classifier;
76 | classifier.addSong('imagine',
77 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
78 | classifier.addSong('somewhereOverTheRainbow',
79 | ['c', 'em', 'f', 'g', 'am'], 0);
80 | classifier.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
81 | classifier.addSong('iWillFollowYouIntoTheDark',
82 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
83 | classifier.addSong('babyOneMoreTime',
84 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
85 | classifier.addSong('creep',
86 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
87 | classifier.addSong('paperBag',
88 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
89 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
90 | 'b'], 2);
91 | classifier.addSong('toxic',
92 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
93 | classifier.addSong('bulletproof',
94 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
95 | classifier.trainAll();
96 | it('classifies', () => {
97 | const classified = classifier.classify(['f#m7', 'a', 'dadd9',
98 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
99 | wish(classified.get('easy') === 1.3433333333333333);
100 | wish(classified.get('medium') === 1.5060259259259259);
101 | wish(classified.get('hard') === 1.6884223991769547);
102 | });
103 | it('classifies again', () => {
104 | const classified = classifier.classify(['d', 'g', 'e', 'dm']);
105 | wish(classified.get('easy') === 2.023094827160494);
106 | wish(classified.get('medium') === 1.855758613168724);
107 | wish(classified.get('hard') === 1.855758613168724);
108 | });
109 | it('label probabilities', () => {
110 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
111 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
112 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
113 | });
114 | });
115 |
--------------------------------------------------------------------------------
/code/ch07/15_after_privacy.js:
--------------------------------------------------------------------------------
1 | module.exports = class Classifier {
2 | constructor(){
3 | this._songList = {
4 | allChords: new Set(),
5 | difficulties: ['easy', 'medium', 'hard'],
6 | songs: [],
7 | addSong(name, chords, difficulty){
8 | this.songs.push({name,
9 | chords,
10 | difficulty: this.difficulties[difficulty]});
11 | }
12 | };
13 | this._labelCounts = new Map();
14 | this._labelProbabilities = new Map();
15 | this._smoothing = 1.01;
16 | };
17 | addSong(...songParams){
18 | this._songList.addSong(...songParams);
19 | };
20 | _chordCountForDifficulty(difficulty, testChord){
21 | return this._songList.songs.reduce((counter, song) => {
22 | if(song.difficulty === difficulty){
23 | counter += song.chords.filter(
24 | chord => chord === testChord
25 | ).length;
26 | }
27 | return counter;
28 | }, 0);
29 | };
30 |
31 | _likelihoodFromChord(difficulty, chord){
32 | return this._chordCountForDifficulty(difficulty, chord) /
33 | this._songList.songs.length;
34 | };
35 | _valueForChordDifficulty(difficulty, chord){
36 | const value = this._likelihoodFromChord(difficulty, chord);
37 | return value ? value + this._smoothing : 1;
38 | };
39 | trainAll(){
40 | this._songList.songs.forEach((song) => {
41 | this._train(song.chords, song.difficulty);
42 | });
43 | this._setLabelProbabilities();
44 | };
45 |
46 | _train(chords, label){
47 | chords.forEach(chord => this._songList.allChords.add(chord) );
48 | if(Array.from(this._labelCounts.keys()).includes(label)){
49 | this._labelCounts.set(label, this._labelCounts.get(label) + 1);
50 | } else {
51 | this._labelCounts.set(label, 1);
52 | }
53 | };
54 |
55 | _setLabelProbabilities(){
56 | this._labelCounts.forEach((_count, label) => {
57 | this._labelProbabilities.set(label, this._labelCounts.get(label) /
58 | this._songList.songs.length);
59 | });
60 | };
61 |
62 | classify(chords){
63 | return new Map(Array.from(
64 | this._labelProbabilities.entries()).map(
65 | (labelWithProbability) => {
66 | const difficulty = labelWithProbability[0];
67 | return [difficulty, chords.reduce((total, chord) => {
68 | return total * this._valueForChordDifficulty(difficulty,
69 | chord);
70 | }, this._labelProbabilities.get(difficulty) + this._smoothing)];
71 | }));
72 | }
73 | };
74 |
--------------------------------------------------------------------------------
/code/ch07/15_after_privacy_test.js:
--------------------------------------------------------------------------------
1 | const Classifier = require('./15_after_privacy.js');
2 | const wish = require('wish');
3 | describe('the file', () => {
4 | const classifier = new Classifier;
5 | classifier.addSong('imagine',
6 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
7 | classifier.addSong('somewhereOverTheRainbow',
8 | ['c', 'em', 'f', 'g', 'am'], 0);
9 | classifier.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
10 | classifier.addSong('iWillFollowYouIntoTheDark',
11 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
12 | classifier.addSong('babyOneMoreTime',
13 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
14 | classifier.addSong('creep',
15 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
16 | classifier.addSong('paperBag',
17 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
18 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
19 | 'b'], 2);
20 | classifier.addSong('toxic',
21 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
22 | classifier.addSong('bulletproof',
23 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
24 | classifier.trainAll();
25 | it('classifies', () => {
26 | const classified = classifier.classify(['f#m7', 'a', 'dadd9',
27 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
28 | wish(classified.get('easy') === 1.3433333333333333);
29 | wish(classified.get('medium') === 1.5060259259259259);
30 | wish(classified.get('hard') === 1.6884223991769547);
31 | });
32 | it('classifies again', () => {
33 | const classified = classifier.classify(['d', 'g', 'e', 'dm']);
34 | wish(classified.get('easy') === 2.023094827160494);
35 | wish(classified.get('medium') === 1.855758613168724);
36 | wish(classified.get('hard') === 1.855758613168724);
37 | });
38 | it('label probabilities', () => {
39 | wish(classifier._labelProbabilities.get('easy') === 0.3333333333333333);
40 | wish(classifier._labelProbabilities.get('medium') === 0.3333333333333333);
41 | wish(classifier._labelProbabilities.get('hard') === 0.3333333333333333);
42 | });
43 | });
44 |
--------------------------------------------------------------------------------
/code/ch07/16_after_new_domain.js:
--------------------------------------------------------------------------------
1 | module.exports = class Classifier {
2 | constructor(){
3 | this._textList = {
4 | allWords: new Set(),
5 | understood: ['yes', 'no'],
6 | texts: [],
7 | addText(name, words, comprehension){
8 | this.texts.push({name,
9 | words,
10 | comprehension: this.understood[comprehension]});
11 | }
12 | };
13 | this._labelCounts = new Map();
14 | this._labelProbabilities = new Map();
15 | this._smoothing = 1.01;
16 | };
17 | addText(...textParams){
18 | this._textList.addText(...textParams);
19 | };
20 | _wordCountForComprehension(comprehension, testWord){
21 | return this._textList.texts.reduce((counter, text) => {
22 | if(text.comprehension === comprehension){
23 | counter += text.words.filter(
24 | word => word === testWord
25 | ).length;
26 | }
27 | return counter;
28 | }, 0);
29 | };
30 |
31 | _likelihoodFromWord(comprehension, word){
32 | return this._wordCountForComprehension(comprehension, word) /
33 | this._textList.texts.length;
34 | };
35 | _valueForWordComprehension(comprehension, word){
36 | const value = this._likelihoodFromWord(comprehension, word);
37 | return value ? value + this._smoothing : 1;
38 | };
39 | trainAll(){
40 | this._textList.texts.forEach((text) => {
41 | this._train(text.words, text.comprehension);
42 | });
43 | this._setLabelProbabilities();
44 | };
45 |
46 | _train(words, label){
47 | words.forEach(word => this._textList.allWords.add(word) );
48 | if(Array.from(this._labelCounts.keys()).includes(label)){
49 | this._labelCounts.set(label, this._labelCounts.get(label) + 1);
50 | } else {
51 | this._labelCounts.set(label, 1);
52 | }
53 | };
54 |
55 | _setLabelProbabilities(){
56 | this._labelCounts.forEach((_count, label) => {
57 | this._labelProbabilities.set(label, this._labelCounts.get(label) /
58 | this._textList.texts.length);
59 | });
60 | };
61 |
62 | classify(words){
63 | return new Map(Array.from(
64 | this._labelProbabilities.entries()).map(
65 | (labelWithProbability) => {
66 | const comprehension = labelWithProbability[0];
67 | return [comprehension, words.reduce((total, word) => {
68 | return total * this._valueForWordComprehension(comprehension,
69 | word);
70 | }, this._labelProbabilities.get(comprehension) + this._smoothing)];
71 | }));
72 | }
73 | };
74 |
--------------------------------------------------------------------------------
/code/ch07/16_after_new_domain_test.js:
--------------------------------------------------------------------------------
1 | Classifier = require('./16_after_new_domain.js');
2 | const wish = require('wish');
3 | describe('the file', () => {
4 | const classifier = new Classifier;
5 | classifier.addText('english text',
6 | ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
7 | 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q'],
8 | 0);
9 | classifier.addText('japanese text',
10 | ['あ', 'い', 'う', 'え', 'お',
11 | 'か', 'き', 'く', 'け', 'こ'],
12 | 1);
13 |
14 | classifier.trainAll();
15 | it('classifies', () =>{
16 | const classified = classifier.classify(['お', 'は', 'よ', 'う', 'ご', 'ざ', 'い', 'ま', 'す']);
17 | wish(classified.get('yes') === 1.51);
18 | wish(classified.get('no') === 5.19885601);
19 | });
20 | it('number of words', ()=>{
21 | wish(classifier._textList.allWords.size === 27);
22 | });
23 |
24 | it('label probabilities', ()=>{
25 | wish(classifier._labelProbabilities.get('yes') === 0.5);
26 | wish(classifier._labelProbabilities.get('no') === 0.5);
27 | });
28 | });
29 |
--------------------------------------------------------------------------------
/code/ch07/1_initial.js:
--------------------------------------------------------------------------------
1 | function fileName(){
2 | var theError = new Error("here I am");
3 | return theError.stack.match(/\/(\w+\.js)\:/)[1];
4 | };
5 | console.log(`Welcome to ${fileName()}!`);
6 | var easy = 'easy';
7 | var medium = 'medium';
8 | var hard = 'hard';
9 |
10 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
11 | somewhereOverTheRainbow = ['c', 'em', 'f', 'g', 'am'];
12 | tooManyCooks = ['c', 'g', 'f'];
13 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
14 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
15 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
16 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7',
17 | 'em7', 'a7', 'f7', 'b'];
18 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7',
19 | 'g7'];
20 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
21 |
22 | var songs = [];
23 | var labels = [];
24 | var allChords = [];
25 | var labelCounts = [];
26 | var labelProbabilities = [];
27 | var chordCountsInLabels = {};
28 | var probabilityOfChordsInLabels = {};
29 |
30 | function train(chords, label){
31 | songs.push([label, chords]);
32 | labels.push(label);
33 | chords.forEach(chord => {
34 | if(!allChords.includes(chord)){
35 | allChords.push(chord);
36 | }
37 | });
38 | if(Object.keys(labelCounts).includes(label)){
39 | labelCounts[label] = labelCounts[label] + 1;
40 | } else {
41 | labelCounts[label] = 1;
42 | }
43 | };
44 |
45 | function setLabelProbabilities(){
46 | Object.keys(labelCounts).forEach(function(label){
47 | labelProbabilities[label] = labelCounts[label] / songs.length;
48 | });
49 | };
50 |
51 | function setChordCountsInLabels(){
52 | songs.forEach(function(song){
53 | if(chordCountsInLabels[song[0]] === undefined){
54 | chordCountsInLabels[song[0]] = {};
55 | }
56 | song[1].forEach(function(chord){
57 | if(chordCountsInLabels[song[0]][chord] > 0){
58 | chordCountsInLabels[song[0]][chord] += 1;
59 | } else {
60 | chordCountsInLabels[song[0]][chord] = 1;
61 | }
62 | });
63 | });
64 | }
65 |
66 | function setProbabilityOfChordsInLabels(){
67 | probabilityOfChordsInLabels = chordCountsInLabels;
68 | Object.keys(probabilityOfChordsInLabels).forEach(
69 | function(difficulty){
70 | Object.keys(probabilityOfChordsInLabels[difficulty]).forEach(
71 | function(chord){
72 | probabilityOfChordsInLabels[difficulty][chord] /= songs.length;
73 | });
74 | });
75 | }
76 |
77 | train(imagine, easy);
78 | train(somewhereOverTheRainbow, easy);
79 | train(tooManyCooks, easy);
80 | train(iWillFollowYouIntoTheDark, medium);
81 | train(babyOneMoreTime, medium);
82 | train(creep, medium);
83 | train(paperBag, hard);
84 | train(toxic, hard);
85 | train(bulletproof, hard);
86 |
87 | setLabelProbabilities();
88 | setChordCountsInLabels();
89 | setProbabilityOfChordsInLabels();
90 |
91 | function classify(chords){
92 | var smoothing = 1.01;
93 | console.log(labelProbabilities);
94 | var classified = {};
95 | Object.keys(labelProbabilities).forEach(function(difficulty){
96 | var first = labelProbabilities[difficulty] + smoothing;
97 | chords.forEach(function(chord){
98 | var probabilityOfChordInLabel =
99 | probabilityOfChordsInLabels[difficulty][chord];
100 | if(probabilityOfChordInLabel){
101 | first = first * (probabilityOfChordInLabel + smoothing);
102 | }
103 | });
104 | classified[difficulty] = first;
105 | });
106 | console.log(classified);
107 | };
108 |
109 | classify(['d', 'g', 'e', 'dm']);
110 | classify(['f#m7', 'a', 'dadd9', 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
111 |
--------------------------------------------------------------------------------
/code/ch07/2_after_array_object_alternatives.js:
--------------------------------------------------------------------------------
1 | function fileName(){
2 | var theError = new Error("here I am");
3 | return theError.stack.match(/\/(\w+\.js)\:/)[1];
4 | };
5 | console.log(`Welcome to ${fileName()}!`);
6 | var easy = 'easy';
7 | var medium = 'medium';
8 | var hard = 'hard';
9 |
10 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
11 | somewhereOverTheRainbow = ['c', 'em', 'f', 'g', 'am'];
12 | tooManyCooks = ['c', 'g', 'f'];
13 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
14 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
15 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
16 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7',
17 | 'em7', 'a7', 'f7', 'b'];
18 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7',
19 | 'g7'];
20 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
21 |
22 | var songs = [];
23 | var allChords = new Set();
24 | var labelCounts = new Map();
25 | var labelProbabilities = new Map();
26 | var chordCountsInLabels = new Map();
27 | var probabilityOfChordsInLabels = new Map();
28 |
29 | function train(chords, label){
30 | songs.push({label, chords});
31 | chords.forEach(chord => allChords.add(chord));
32 | if(Array.from(labelCounts.keys()).includes(label)){
33 | labelCounts.set(label, labelCounts.get(label) + 1);
34 | } else {
35 | labelCounts.set(label, 1);
36 | }
37 | };
38 |
39 | function setLabelProbabilities(){
40 | labelCounts.forEach(function(_count, label){
41 | labelProbabilities.set(label, labelCounts.get(label) / songs.length);
42 | });
43 | };
44 |
45 | function setChordCountsInLabels(){
46 | songs.forEach(function(song){
47 | if(chordCountsInLabels.get(song.label) === undefined){
48 | chordCountsInLabels.set(song.label, {});
49 | }
50 | song.chords.forEach(function(chord){
51 | if(chordCountsInLabels.get(song.label)[chord] > 0){
52 | chordCountsInLabels.get(song.label)[chord] += 1;
53 | } else {
54 | chordCountsInLabels.get(song.label)[chord] = 1;
55 | }
56 | });
57 | });
58 | }
59 |
60 | function setProbabilityOfChordsInLabels(){
61 | probabilityOfChordsInLabels = chordCountsInLabels;
62 | probabilityOfChordsInLabels.forEach(function(_chords, difficulty){
63 | Object.keys(probabilityOfChordsInLabels.get(difficulty)).forEach(
64 | function(chord){
65 | probabilityOfChordsInLabels.get(difficulty)[chord] /= songs.length;
66 | });
67 | });
68 | }
69 |
70 | train(imagine, easy);
71 | train(somewhereOverTheRainbow, easy);
72 | train(tooManyCooks, easy);
73 | train(iWillFollowYouIntoTheDark, medium);
74 | train(babyOneMoreTime, medium);
75 | train(creep, medium);
76 | train(paperBag, hard);
77 | train(toxic, hard);
78 | train(bulletproof, hard);
79 |
80 | setLabelProbabilities();
81 | setChordCountsInLabels();
82 | setProbabilityOfChordsInLabels();
83 |
84 | function classify(chords){
85 | var smoothing = 1.01;
86 | console.log(labelProbabilities);
87 | var classified = new Map();
88 | labelProbabilities.forEach(function(_probabilities, difficulty){
89 | var first = labelProbabilities.get(difficulty) + smoothing;
90 | chords.forEach(function(chord){
91 | var probabilityOfChordInLabel =
92 | probabilityOfChordsInLabels.get(difficulty)[chord];
93 | if(probabilityOfChordInLabel){
94 | first = first * (probabilityOfChordInLabel + smoothing);
95 | }
96 | });
97 | classified.set(difficulty, first);
98 | });
99 | console.log(classified);
100 | };
101 |
102 | classify(['d', 'g', 'e', 'dm']);
103 | classify(['f#m7', 'a', 'dadd9', 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
104 |
--------------------------------------------------------------------------------
/code/ch07/3_after_testing_setup.js:
--------------------------------------------------------------------------------
1 | function welcomeMessage(){
2 | return `Welcome to ${fileName()}!`;
3 | };
4 | function fileName(){
5 | var theError = new Error("here I am");
6 | return theError.stack.match(/\/(\w+\.js)\:/)[1];
7 | };
8 | var easy = 'easy';
9 | var medium = 'medium';
10 | var hard = 'hard';
11 |
12 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
13 | somewhereOverTheRainbow = ['c', 'em', 'f', 'g', 'am'];
14 | tooManyCooks = ['c', 'g', 'f'];
15 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
16 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
17 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
18 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7',
19 | 'em7', 'a7', 'f7', 'b'];
20 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7',
21 | 'g7'];
22 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
23 |
24 | var songs = [];
25 | var allChords = new Set();
26 | var labelCounts = new Map();
27 | var labelProbabilities = new Map();
28 | var chordCountsInLabels = new Map();
29 | var probabilityOfChordsInLabels = new Map();
30 |
31 | function train(chords, label){
32 | songs.push({label, chords});
33 | chords.forEach(chord => allChords.add(chord));
34 | if(Array.from(labelCounts.keys()).includes(label)){
35 | labelCounts.set(label, labelCounts.get(label) + 1);
36 | } else {
37 | labelCounts.set(label, 1);
38 | }
39 | };
40 |
41 | function setLabelProbabilities(){
42 | labelCounts.forEach(function(_count, label){
43 | labelProbabilities.set(label, labelCounts.get(label) / songs.length);
44 | });
45 | };
46 |
47 | function setChordCountsInLabels(){
48 | songs.forEach(function(song){
49 | if(chordCountsInLabels.get(song.label) === undefined){
50 | chordCountsInLabels.set(song.label, {});
51 | }
52 | song.chords.forEach(function(chord){
53 | if(chordCountsInLabels.get(song.label)[chord] > 0){
54 | chordCountsInLabels.get(song.label)[chord] += 1;
55 | } else {
56 | chordCountsInLabels.get(song.label)[chord] = 1;
57 | }
58 | });
59 | });
60 | }
61 |
62 | function setProbabilityOfChordsInLabels(){
63 | probabilityOfChordsInLabels = chordCountsInLabels;
64 | probabilityOfChordsInLabels.forEach(function(_chords, difficulty){
65 | Object.keys(probabilityOfChordsInLabels.get(difficulty)).forEach(
66 | function(chord){
67 | probabilityOfChordsInLabels.get(difficulty)[chord] /= songs.length;
68 | });
69 | });
70 | }
71 |
72 | train(imagine, easy);
73 | train(somewhereOverTheRainbow, easy);
74 | train(tooManyCooks, easy);
75 | train(iWillFollowYouIntoTheDark, medium);
76 | train(babyOneMoreTime, medium);
77 | train(creep, medium);
78 | train(paperBag, hard);
79 | train(toxic, hard);
80 | train(bulletproof, hard);
81 |
82 | setLabelProbabilities();
83 | setChordCountsInLabels();
84 | setProbabilityOfChordsInLabels();
85 |
86 | function classify(chords){
87 | var smoothing = 1.01;
88 | var classified = new Map();
89 | labelProbabilities.forEach(function(_probabilities, difficulty){
90 | var first = labelProbabilities.get(difficulty) + smoothing;
91 | chords.forEach(function(chord){
92 | var probabilityOfChordInLabel =
93 | probabilityOfChordsInLabels.get(difficulty)[chord];
94 | if(probabilityOfChordInLabel){
95 | first = first * (probabilityOfChordInLabel + smoothing);
96 | }
97 | });
98 | classified.set(difficulty, first);
99 | });
100 | return classified;
101 | };
102 |
103 | var wish = require('wish');
104 | describe('the file', function() {
105 | it('classifies', function(){
106 | var classified = classify(['f#m7', 'a', 'dadd9',
107 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
108 | wish(classified.get('easy') === 1.3433333333333333);
109 | wish(classified.get('medium') === 1.5060259259259259);
110 | wish(classified.get('hard') === 1.6884223991769547);
111 | });
112 | it('classifies again', function(){
113 | var classified = classify(['d', 'g', 'e', 'dm']);
114 | wish(classified.get('easy') === 2.023094827160494);
115 | wish(classified.get('medium') === 1.855758613168724);
116 | wish(classified.get('hard') === 1.855758613168724);
117 | });
118 | it('sets welcome message', function(){
119 | wish(welcomeMessage() === 'Welcome to nb.js!')
120 | });
121 | it('label probabilities', function(){
122 | wish(labelProbabilities.get('easy') === 0.3333333333333333);
123 | wish(labelProbabilities.get('medium') === 0.3333333333333333);
124 | wish(labelProbabilities.get('hard') === 0.3333333333333333);
125 | });
126 | });
127 |
--------------------------------------------------------------------------------
/code/ch07/4_after_extracting_functions.js:
--------------------------------------------------------------------------------
1 | function welcomeMessage(){
2 | return `Welcome to ${fileName()}!`;
3 | };
4 | function fileName(){
5 | var theError = new Error("here I am");
6 | return theError.stack.match(/\/(\w+\.js)\:/)[1];
7 | };
8 | function setDifficulties(){
9 | easy = 'easy';
10 | medium = 'medium';
11 | hard = 'hard';
12 | };
13 |
14 |
15 | function setSongs(){
16 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
17 | somewhereOverTheRainbow = ['c', 'em', 'f', 'g', 'am'];
18 | tooManyCooks = ['c', 'g', 'f'];
19 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
20 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
21 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
22 | paperBag = ['bm7', 'e', 'c', 'g',
23 | 'b7', 'f', 'em', 'a',
24 | 'cmaj7', 'em7', 'a7', 'f7',
25 | 'b'];
26 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab',
27 | 'gmaj7', 'g7'];
28 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
29 | };
30 |
31 | function setup(){
32 | songs = [];
33 | allChords = new Set();
34 | labelCounts = new Map();
35 | labelProbabilities = new Map();
36 | chordCountsInLabels = new Map();
37 | probabilityOfChordsInLabels = new Map();
38 | };
39 |
40 |
41 | function train(chords, label){
42 | songs.push({label, chords});
43 | chords.forEach(chord => allChords.add(chord));
44 | if(Array.from(labelCounts.keys()).includes(label)){
45 | labelCounts.set(label, labelCounts.get(label) + 1);
46 | } else {
47 | labelCounts.set(label, 1);
48 | }
49 | };
50 |
51 | function setLabelProbabilities(){
52 | labelCounts.forEach(function(_count, label){
53 | labelProbabilities.set(label, labelCounts.get(label) / songs.length);
54 | });
55 | };
56 |
57 | function setChordCountsInLabels(){
58 | songs.forEach(function(song){
59 | if(chordCountsInLabels.get(song.label) === undefined){
60 | chordCountsInLabels.set(song.label, {});
61 | }
62 | song.chords.forEach(function(chord){
63 | if(chordCountsInLabels.get(song.label)[chord] > 0){
64 | chordCountsInLabels.get(song.label)[chord] += 1;
65 | } else {
66 | chordCountsInLabels.get(song.label)[chord] = 1;
67 | }
68 | });
69 | });
70 | }
71 |
72 | function setProbabilityOfChordsInLabels(){
73 | probabilityOfChordsInLabels = chordCountsInLabels;
74 | probabilityOfChordsInLabels.forEach(function(_chords, difficulty){
75 | Object.keys(probabilityOfChordsInLabels.get(difficulty)).forEach(
76 | function(chord){
77 | probabilityOfChordsInLabels.get(difficulty)[chord] /= songs.length;
78 | });
79 | });
80 | }
81 |
82 | function trainAll(){
83 | setDifficulties();
84 | setup();
85 | setSongs();
86 | train(imagine, easy);
87 | train(somewhereOverTheRainbow, easy);
88 | train(tooManyCooks, easy);
89 | train(iWillFollowYouIntoTheDark, medium);
90 | train(babyOneMoreTime, medium);
91 | train(creep, medium);
92 | train(paperBag, hard);
93 | train(toxic, hard);
94 | train(bulletproof, hard);
95 | setLabelsAndProbabilities();
96 | };
97 |
98 | function setLabelsAndProbabilities(){
99 | setLabelProbabilities();
100 | setChordCountsInLabels();
101 | setProbabilityOfChordsInLabels();
102 | };
103 |
104 | function classify(chords){
105 | var smoothing = 1.01;
106 | var classified = new Map();
107 | labelProbabilities.forEach(function(_probabilities, difficulty){
108 | var first = labelProbabilities.get(difficulty) + smoothing;
109 | chords.forEach(function(chord){
110 | var probabilityOfChordInLabel =
111 | probabilityOfChordsInLabels.get(difficulty)[chord];
112 | if(probabilityOfChordInLabel){
113 | first = first * (probabilityOfChordInLabel + smoothing);
114 | }
115 | });
116 | classified.set(difficulty, first);
117 | });
118 | return classified;
119 | };
120 |
121 | var wish = require('wish');
122 | describe('the file', function() {
123 | trainAll();
124 | it('classifies', function(){
125 | var classified = classify(['f#m7', 'a', 'dadd9',
126 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
127 | wish(classified.get('easy') === 1.3433333333333333);
128 | wish(classified.get('medium') === 1.5060259259259259);
129 | wish(classified.get('hard') === 1.6884223991769547);
130 | });
131 | it('classifies again', function(){
132 | var classified = classify(['d', 'g', 'e', 'dm']);
133 | wish(classified.get('easy') === 2.023094827160494);
134 | wish(classified.get('medium') === 1.855758613168724);
135 | wish(classified.get('hard') === 1.855758613168724);
136 | });
137 | it('sets welcome message', function(){
138 | wish(welcomeMessage() === 'Welcome to nb.js!')
139 | });
140 | it('label probabilities', function(){
141 | wish(labelProbabilities.get('easy') === 0.3333333333333333);
142 | wish(labelProbabilities.get('medium') === 0.3333333333333333);
143 | wish(labelProbabilities.get('hard') === 0.3333333333333333);
144 | });
145 | });
146 |
--------------------------------------------------------------------------------
/code/ch07/5_after_handling_remaining_globals.js:
--------------------------------------------------------------------------------
1 | var songList = {
2 | difficulties: ['easy', 'medium', 'hard'],
3 | songs: [],
4 | addSong: function(name, chords, difficulty){
5 | this.songs.push({name: name,
6 | chords: chords,
7 | difficulty: this.difficulties[difficulty]})
8 | }
9 | };
10 |
11 | var classifier = {
12 | songs: [],
13 | allChords: new Set(),
14 | labelCounts: new Map(),
15 | labelProbabilities: new Map(),
16 | chordCountsInLabels: new Map(),
17 | probabilityOfChordsInLabels: new Map()
18 | };
19 |
20 | function setSongs(){
21 | songList.addSong('imagine',
22 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
23 | songList.addSong('somewhereOverTheRainbow',
24 | ['c', 'em', 'f', 'g', 'am'], 0);
25 | songList.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
26 | songList.addSong('iWillFollowYouIntoTheDark',
27 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
28 | songList.addSong('babyOneMoreTime',
29 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
30 | songList.addSong('creep',
31 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
32 | songList.addSong('paperBag',
33 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
34 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
35 | 'b'], 2);
36 | songList.addSong('toxic',
37 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
38 | songList.addSong('bulletproof',
39 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
40 | };
41 |
42 | function train(chords, label){
43 | classifier.songs.push({label, chords});
44 | chords.forEach(chord => classifier.allChords.add(chord));
45 | if(Array.from(classifier.labelCounts.keys()).includes(label)){
46 | classifier.labelCounts.set(label, classifier.labelCounts.get(label) + 1);
47 | } else {
48 | classifier.labelCounts.set(label, 1);
49 | }
50 | };
51 |
52 | function setLabelProbabilities(){
53 | classifier.labelCounts.forEach(function(_count, label){
54 | classifier.labelProbabilities.set(label,
55 | classifier.labelCounts.get(label) / classifier.songs.length);
56 | });
57 | };
58 |
59 | function setChordCountsInLabels(){
60 | classifier.songs.forEach(function(song){
61 | if(classifier.chordCountsInLabels.get(song.label) === undefined){
62 | classifier.chordCountsInLabels.set(song.label, {});
63 | }
64 | song.chords.forEach(function(chord){
65 | if(classifier.chordCountsInLabels.get(song.label)[chord] > 0){
66 | classifier.chordCountsInLabels.get(song.label)[chord] += 1;
67 | } else {
68 | classifier.chordCountsInLabels.get(song.label)[chord] = 1;
69 | }
70 | });
71 | });
72 | }
73 |
74 | function setProbabilityOfChordsInLabels(){
75 | classifier.probabilityOfChordsInLabels = classifier.chordCountsInLabels;
76 | classifier.probabilityOfChordsInLabels.forEach(function(_chords, difficulty){
77 | Object.keys(classifier.probabilityOfChordsInLabels.get(difficulty)).forEach(
78 | function(chord){
79 | classifier.probabilityOfChordsInLabels.get(difficulty)[chord]
80 | /= classifier.songs.length;
81 | });
82 | });
83 | }
84 |
85 | function trainAll(){
86 | setSongs();
87 | songList.songs.forEach(function(song){
88 | train(song.chords, song.difficulty);
89 | });
90 | setLabelsAndProbabilities();
91 | };
92 |
93 | function setLabelsAndProbabilities(){
94 | setLabelProbabilities();
95 | setChordCountsInLabels();
96 | setProbabilityOfChordsInLabels();
97 | };
98 |
99 | function classify(chords){
100 | var smoothing = 1.01;
101 | var classified = new Map();
102 | classifier.labelProbabilities.forEach(function(_probabilities, difficulty){
103 | var first = classifier.labelProbabilities.get(difficulty) + smoothing;
104 | chords.forEach(function(chord){
105 | var probabilityOfChordInLabel =
106 | classifier.probabilityOfChordsInLabels.get(difficulty)[chord];
107 | if(probabilityOfChordInLabel){
108 | first = first * (probabilityOfChordInLabel + smoothing);
109 | }
110 | });
111 | classified.set(difficulty, first);
112 | });
113 | return classified;
114 | };
115 |
116 | var wish = require('wish');
117 | describe('the file', function() {
118 | trainAll();
119 | it('classifies', function(){
120 | var classified = classify(['f#m7', 'a', 'dadd9',
121 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
122 | wish(classified.get('easy') === 1.3433333333333333);
123 | wish(classified.get('medium') === 1.5060259259259259);
124 | wish(classified.get('hard') === 1.6884223991769547);
125 | });
126 | it('classifies again', function(){
127 | var classified = classify(['d', 'g', 'e', 'dm']);
128 | wish(classified.get('easy') === 2.023094827160494);
129 | wish(classified.get('medium') === 1.855758613168724);
130 | wish(classified.get('hard') === 1.855758613168724);
131 | });
132 | it('label probabilities', function(){
133 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
134 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
135 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
136 | });
137 | });
138 |
--------------------------------------------------------------------------------
/code/ch07/6_after_independent_data.js:
--------------------------------------------------------------------------------
1 | var songList = {
2 | difficulties: ['easy', 'medium', 'hard'],
3 | songs: [],
4 | addSong: function(name, chords, difficulty){
5 | this.songs.push({name: name,
6 | chords: chords,
7 | difficulty: this.difficulties[difficulty]})
8 | }
9 | };
10 |
11 | var classifier = {
12 | songs: [],
13 | allChords: new Set(),
14 | labelCounts: new Map(),
15 | labelProbabilities: new Map(),
16 | chordCountsInLabels: new Map(),
17 | probabilityOfChordsInLabels: new Map()
18 | };
19 |
20 | function train(chords, label){
21 | classifier.songs.push({label, chords});
22 | chords.forEach(chord => classifier.allChords.add(chord));
23 | if(Array.from(classifier.labelCounts.keys()).includes(label)){
24 | classifier.labelCounts.set(label, classifier.labelCounts.get(label) + 1);
25 | } else {
26 | classifier.labelCounts.set(label, 1);
27 | }
28 | };
29 |
30 | function setLabelProbabilities(){
31 | classifier.labelCounts.forEach(function(_count, label){
32 | classifier.labelProbabilities.set(label,
33 | classifier.labelCounts.get(label) / classifier.songs.length);
34 | });
35 | };
36 |
37 | function setChordCountsInLabels(){
38 | classifier.songs.forEach(function(song){
39 | if(classifier.chordCountsInLabels.get(song.label) === undefined){
40 | classifier.chordCountsInLabels.set(song.label, {});
41 | }
42 | song.chords.forEach(function(chord){
43 | if(classifier.chordCountsInLabels.get(song.label)[chord] > 0){
44 | classifier.chordCountsInLabels.get(song.label)[chord] += 1;
45 | } else {
46 | classifier.chordCountsInLabels.get(song.label)[chord] = 1;
47 | }
48 | });
49 | });
50 | }
51 |
52 | function setProbabilityOfChordsInLabels(){
53 | classifier.probabilityOfChordsInLabels = classifier.chordCountsInLabels;
54 | classifier.probabilityOfChordsInLabels.forEach(function(_chords, difficulty){
55 | Object.keys(classifier.probabilityOfChordsInLabels.get(difficulty)).forEach(
56 | function(chord){
57 | classifier.probabilityOfChordsInLabels.get(difficulty)[chord]
58 | /= classifier.songs.length;
59 | });
60 | });
61 | }
62 |
63 | function trainAll(){
64 | songList.songs.forEach(function(song){
65 | train(song.chords, song.difficulty);
66 | });
67 | setLabelsAndProbabilities();
68 | };
69 |
70 | function setLabelsAndProbabilities(){
71 | setLabelProbabilities();
72 | setChordCountsInLabels();
73 | setProbabilityOfChordsInLabels();
74 | };
75 |
76 | function classify(chords){
77 | var smoothing = 1.01;
78 | var classified = new Map();
79 | classifier.labelProbabilities.forEach(function(_probabilities, difficulty){
80 | var first = classifier.labelProbabilities.get(difficulty) + smoothing;
81 | chords.forEach(function(chord){
82 | var probabilityOfChordInLabel =
83 | classifier.probabilityOfChordsInLabels.get(difficulty)[chord];
84 | if(probabilityOfChordInLabel){
85 | first = first * (probabilityOfChordInLabel + smoothing);
86 | }
87 | });
88 | classified.set(difficulty, first);
89 | });
90 | return classified;
91 | };
92 |
93 | var wish = require('wish');
94 | describe('the file', function() {
95 | songList.addSong('imagine',
96 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
97 | songList.addSong('somewhereOverTheRainbow',
98 | ['c', 'em', 'f', 'g', 'am'], 0);
99 | songList.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
100 | songList.addSong('iWillFollowYouIntoTheDark',
101 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
102 | songList.addSong('babyOneMoreTime',
103 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
104 | songList.addSong('creep',
105 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
106 | songList.addSong('paperBag',
107 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
108 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
109 | 'b'], 2);
110 | songList.addSong('toxic',
111 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
112 | songList.addSong('bulletproof',
113 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
114 | trainAll();
115 | it('classifies', function(){
116 | var classified = classify(['f#m7', 'a', 'dadd9',
117 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
118 | wish(classified.get('easy') === 1.3433333333333333);
119 | wish(classified.get('medium') === 1.5060259259259259);
120 | wish(classified.get('hard') === 1.6884223991769547);
121 | });
122 | it('classifies again', function(){
123 | var classified = classify(['d', 'g', 'e', 'dm']);
124 | wish(classified.get('easy') === 2.023094827160494);
125 | wish(classified.get('medium') === 1.855758613168724);
126 | wish(classified.get('hard') === 1.855758613168724);
127 | });
128 | it('label probabilities', function(){
129 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
130 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
131 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
132 | });
133 | });
134 |
--------------------------------------------------------------------------------
/code/ch07/7_after_bringing_classify_in.js:
--------------------------------------------------------------------------------
1 | const songList = {
2 | difficulties: ['easy', 'medium', 'hard'],
3 | songs: [],
4 | addSong: function(name, chords, difficulty){
5 | this.songs.push({name: name,
6 | chords: chords,
7 | difficulty: this.difficulties[difficulty]})
8 | }
9 | };
10 |
11 | const classifier = {
12 | songs: [],
13 | allChords: new Set(),
14 | labelCounts: new Map(),
15 | labelProbabilities: new Map(),
16 | chordCountsInLabels: new Map(),
17 | probabilityOfChordsInLabels: new Map(),
18 | smoothing: 1.01,
19 | valueForChordDifficulty(difficulty, chord){
20 | const value =
21 | this.probabilityOfChordsInLabels.get(difficulty)[chord];
22 | return value ? value + this.smoothing : 1;
23 | },
24 | classify: function(chords){
25 | return new Map(Array.from(
26 | this.labelProbabilities.entries()).map((labelWithProbability) => {
27 | const difficulty = labelWithProbability[0];
28 | return [difficulty, chords.reduce((total, chord) => {
29 | return total * this.valueForChordDifficulty(difficulty, chord);
30 | }, this.labelProbabilities.get(difficulty) + this.smoothing)];
31 | }));
32 | }
33 | };
34 |
35 | function train(chords, label){
36 | classifier.songs.push({label, chords});
37 | chords.forEach(chord => classifier.allChords.add(chord));
38 | if(Array.from(classifier.labelCounts.keys()).includes(label)){
39 | classifier.labelCounts.set(label, classifier.labelCounts.get(label) + 1);
40 | } else {
41 | classifier.labelCounts.set(label, 1);
42 | }
43 | };
44 |
45 | function setLabelProbabilities(){
46 | classifier.labelCounts.forEach(function(_count, label){
47 | classifier.labelProbabilities.set(label,
48 | classifier.labelCounts.get(label) / classifier.songs.length);
49 | });
50 | };
51 |
52 | function setChordCountsInLabels(){
53 | classifier.songs.forEach(function(song){
54 | if(classifier.chordCountsInLabels.get(song.label) === undefined){
55 | classifier.chordCountsInLabels.set(song.label, {});
56 | }
57 | song.chords.forEach(function(chord){
58 | if(classifier.chordCountsInLabels.get(song.label)[chord] > 0){
59 | classifier.chordCountsInLabels.get(song.label)[chord] += 1;
60 | } else {
61 | classifier.chordCountsInLabels.get(song.label)[chord] = 1;
62 | }
63 | });
64 | });
65 | }
66 |
67 | function setProbabilityOfChordsInLabels(){
68 | classifier.probabilityOfChordsInLabels = classifier.chordCountsInLabels;
69 | classifier.probabilityOfChordsInLabels.forEach(function(_chords, difficulty){
70 | Object.keys(classifier.probabilityOfChordsInLabels.get(difficulty)).forEach(
71 | function(chord){
72 | classifier.probabilityOfChordsInLabels.get(difficulty)[chord]
73 | /= classifier.songs.length;
74 | });
75 | });
76 | }
77 |
78 | function trainAll(){
79 | songList.songs.forEach(function(song){
80 | train(song.chords, song.difficulty);
81 | });
82 | setLabelsAndProbabilities();
83 | };
84 |
85 | function setLabelsAndProbabilities(){
86 | setLabelProbabilities();
87 | setChordCountsInLabels();
88 | setProbabilityOfChordsInLabels();
89 | };
90 |
91 | const wish = require('wish');
92 | describe('the file', function() {
93 | songList.addSong('imagine',
94 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
95 | songList.addSong('somewhereOverTheRainbow',
96 | ['c', 'em', 'f', 'g', 'am'], 0);
97 | songList.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
98 | songList.addSong('iWillFollowYouIntoTheDark',
99 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
100 | songList.addSong('babyOneMoreTime',
101 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
102 | songList.addSong('creep',
103 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
104 | songList.addSong('paperBag',
105 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
106 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
107 | 'b'], 2);
108 | songList.addSong('toxic',
109 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
110 | songList.addSong('bulletproof',
111 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
112 | trainAll();
113 | it('classifies', function(){
114 | const classified = classifier.classify(['f#m7', 'a', 'dadd9',
115 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
116 | wish(classified.get('easy') === 1.3433333333333333);
117 | wish(classified.get('medium') === 1.5060259259259259);
118 | wish(classified.get('hard') === 1.6884223991769547);
119 | });
120 | it('classifies again', function(){
121 | const classified = classifier.classify(['d', 'g', 'e', 'dm']);
122 | wish(classified.get('easy') === 2.023094827160494);
123 | wish(classified.get('medium') === 1.855758613168724);
124 | wish(classified.get('hard') === 1.855758613168724);
125 | });
126 | it('label probabilities', function(){
127 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
128 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
129 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
130 | });
131 | });
132 |
--------------------------------------------------------------------------------
/code/ch07/8_after_untangling_coupled_values.js:
--------------------------------------------------------------------------------
1 | const songList = {
2 | difficulties: ['easy', 'medium', 'hard'],
3 | songs: [],
4 | addSong: function(name, chords, difficulty){
5 | this.songs.push({name: name,
6 | chords: chords,
7 | difficulty: this.difficulties[difficulty]})
8 | }
9 | };
10 |
11 | const classifier = {
12 | songs: [],
13 | allChords: new Set(),
14 | labelCounts: new Map(),
15 | labelProbabilities: new Map(),
16 | chordCountsInLabels: new Map(),
17 | smoothing: 1.01,
18 | likelihoodFromChord: function(difficulty, chord){
19 | return this.chordCountsInLabels
20 | .get(difficulty)[chord] / this.songs.length;
21 | },
22 | valueForChordDifficulty(difficulty, chord){
23 | const value = this.likelihoodFromChord(difficulty, chord);
24 | return value ? value + this.smoothing : 1;
25 | },
26 | classify: function(chords){
27 | return new Map(Array.from(
28 | this.labelProbabilities.entries()).map((labelWithProbability) => {
29 | const difficulty = labelWithProbability[0];
30 | return [difficulty, chords.reduce((total, chord) => {
31 | return total * this.valueForChordDifficulty(difficulty, chord);
32 | }, this.labelProbabilities.get(difficulty) + this.smoothing)];
33 | }));
34 | }
35 | };
36 |
37 | function train(chords, label){
38 | classifier.songs.push({label, chords});
39 | chords.forEach(chord => classifier.allChords.add(chord));
40 | if(Array.from(classifier.labelCounts.keys()).includes(label)){
41 | classifier.labelCounts.set(label, classifier.labelCounts.get(label) + 1);
42 | } else {
43 | classifier.labelCounts.set(label, 1);
44 | }
45 | };
46 |
47 | function setLabelProbabilities(){
48 | classifier.labelCounts.forEach(function(_count, label){
49 | classifier.labelProbabilities.set(label,
50 | classifier.labelCounts.get(label) / classifier.songs.length);
51 | });
52 | };
53 |
54 | function setChordCountsInLabels(){
55 | classifier.songs.forEach(function(song){
56 | if(classifier.chordCountsInLabels.get(song.label) === undefined){
57 | classifier.chordCountsInLabels.set(song.label, {});
58 | }
59 | song.chords.forEach(function(chord){
60 | if(classifier.chordCountsInLabels.get(song.label)[chord] > 0){
61 | classifier.chordCountsInLabels.get(song.label)[chord] += 1;
62 | } else {
63 | classifier.chordCountsInLabels.get(song.label)[chord] = 1;
64 | }
65 | });
66 | });
67 | }
68 |
69 | function trainAll(){
70 | songList.songs.forEach(function(song){
71 | train(song.chords, song.difficulty);
72 | });
73 | setLabelsAndProbabilities();
74 | };
75 |
76 | function setLabelsAndProbabilities(){
77 | setLabelProbabilities();
78 | setChordCountsInLabels();
79 | };
80 |
81 | const wish = require('wish');
82 | describe('the file', function() {
83 | songList.addSong('imagine',
84 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
85 | songList.addSong('somewhereOverTheRainbow',
86 | ['c', 'em', 'f', 'g', 'am'], 0);
87 | songList.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
88 | songList.addSong('iWillFollowYouIntoTheDark',
89 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
90 | songList.addSong('babyOneMoreTime',
91 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
92 | songList.addSong('creep',
93 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
94 | songList.addSong('paperBag',
95 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
96 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
97 | 'b'], 2);
98 | songList.addSong('toxic',
99 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
100 | songList.addSong('bulletproof',
101 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
102 | trainAll();
103 | it('classifies', function(){
104 | const classified = classifier.classify(['f#m7', 'a', 'dadd9',
105 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
106 | wish(classified.get('easy') === 1.3433333333333333);
107 | wish(classified.get('medium') === 1.5060259259259259);
108 | wish(classified.get('hard') === 1.6884223991769547);
109 | });
110 | it('classifies again', function(){
111 | const classified = classifier.classify(['d', 'g', 'e', 'dm']);
112 | wish(classified.get('easy') === 2.023094827160494);
113 | wish(classified.get('medium') === 1.855758613168724);
114 | wish(classified.get('hard') === 1.855758613168724);
115 | });
116 | it('label probabilities', function(){
117 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
118 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
119 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
120 | });
121 | });
122 |
--------------------------------------------------------------------------------
/code/ch07/9_after_bringing_other_functions_and_vars.js:
--------------------------------------------------------------------------------
1 | const classifier = {
2 | labelCounts: new Map(),
3 | labelProbabilities: new Map(),
4 | chordCountsInLabels: new Map(),
5 | smoothing: 1.01,
6 | songList: {
7 | allChords: new Set(),
8 | difficulties: ['easy', 'medium', 'hard'],
9 | songs: [],
10 | addSong: function(name, chords, difficulty){
11 | this.songs.push({name: name,
12 | chords: chords,
13 | difficulty: this.difficulties[difficulty]})
14 | }
15 | },
16 | chordCountForDifficulty: function(difficulty, testChord){
17 | return this.songList.songs.reduce(function(counter, song){
18 | if(song.difficulty === difficulty){
19 | counter += song.chords.filter(function(chord){
20 | return chord === testChord;
21 | }).length;
22 | }
23 | return counter;
24 | }, 0);
25 | },
26 |
27 | likelihoodFromChord: function(difficulty, chord){
28 | return this.chordCountForDifficulty(difficulty, chord) /
29 | this.songList.songs.length;
30 | },
31 | valueForChordDifficulty(difficulty, chord){
32 | const value = this.likelihoodFromChord(difficulty, chord);
33 | return value ? value + this.smoothing : 1;
34 | },
35 | trainAll: function(){
36 | this.songList.songs.forEach(function(song){
37 | this.train(song.chords, song.difficulty);
38 | }, this);
39 | this.setLabelProbabilities();
40 | },
41 |
42 | train: function(chords, label){
43 | chords.forEach(chord => { this.songList.allChords.add(chord) } );
44 | if(Array.from(this.labelCounts.keys()).includes(label)){
45 | this.labelCounts.set(label, this.labelCounts.get(label) + 1);
46 | } else {
47 | this.labelCounts.set(label, 1);
48 | }
49 | },
50 |
51 | setLabelProbabilities: function(){
52 | this.labelCounts.forEach(function(_count, label){
53 | this.labelProbabilities.set(label, this.labelCounts.get(label) /
54 | this.songList.songs.length);
55 | }, this);
56 | },
57 |
58 | classify: function(chords){
59 | return new Map(Array.from(
60 | this.labelProbabilities.entries()).map((labelWithProbability) => {
61 | const difficulty = labelWithProbability[0];
62 | return [difficulty, chords.reduce((total, chord) => {
63 | return total * this.valueForChordDifficulty(difficulty, chord);
64 | }, this.labelProbabilities.get(difficulty) + this.smoothing)];
65 | }));
66 | }
67 | };
68 |
69 | const wish = require('wish');
70 | describe('the file', function() {
71 | classifier.songList.addSong('imagine',
72 | ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'], 0);
73 | classifier.songList.addSong('somewhereOverTheRainbow',
74 | ['c', 'em', 'f', 'g', 'am'], 0);
75 | classifier.songList.addSong('tooManyCooks', ['c', 'g', 'f'], 0);
76 | classifier.songList.addSong('iWillFollowYouIntoTheDark',
77 | ['f', 'dm', 'bb', 'c', 'a', 'bbm'], 1);
78 | classifier.songList.addSong('babyOneMoreTime',
79 | ['cm', 'g', 'bb', 'eb', 'fm', 'ab'], 1);
80 | classifier.songList.addSong('creep',
81 | ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'], 1);
82 | classifier.songList.addSong('paperBag',
83 | ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em',
84 | 'a', 'cmaj7', 'em7', 'a7', 'f7',
85 | 'b'], 2);
86 | classifier.songList.addSong('toxic',
87 | ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'], 2);
88 | classifier.songList.addSong('bulletproof',
89 | ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'], 2);
90 | classifier.trainAll();
91 | it('classifies', function(){
92 | const classified = classifier.classify(['f#m7', 'a', 'dadd9',
93 | 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
94 | wish(classified.get('easy') === 1.3433333333333333);
95 | wish(classified.get('medium') === 1.5060259259259259);
96 | wish(classified.get('hard') === 1.6884223991769547);
97 | });
98 | it('classifies again', function(){
99 | const classified = classifier.classify(['d', 'g', 'e', 'dm']);
100 | wish(classified.get('easy') === 2.023094827160494);
101 | wish(classified.get('medium') === 1.855758613168724);
102 | wish(classified.get('hard') === 1.855758613168724);
103 | });
104 | it('label probabilities', function(){
105 | wish(classifier.labelProbabilities.get('easy') === 0.3333333333333333);
106 | wish(classifier.labelProbabilities.get('medium') === 0.3333333333333333);
107 | wish(classifier.labelProbabilities.get('hard') === 0.3333333333333333);
108 | });
109 | });
110 |
--------------------------------------------------------------------------------
/code/ch07/secrets.js:
--------------------------------------------------------------------------------
1 | // Constructor Function
2 | const Secret = function(){
3 | this.normalInfo = 'this is normal';
4 | const secret = 'sekrit';
5 | const secretFunction = function(){
6 | return secret;
7 | }
8 | this.notSecret = function(){
9 | return secret;
10 | };
11 | totallyNotSecret = "I'm defined in the global scope";
12 | };
13 | const s = new Secret();
14 | console.log(s.normalInfo); // 'this is normal'
15 | console.log(s.secret); // undefined
16 | console.log(s.secretFunction()); // error
17 | console.log(s.notSecret()); // 'sekrit'
18 | console.log(s.totallyNotSecret); // undefined
19 | console.log(totallyNotSecret); // I'm defined in the global scope
20 |
21 | // Factory Function
22 | var secretTemplate = (function(){
23 | var obj = {};
24 | obj.normalInfo = 'this is normal';
25 | const secret = 'sekrit';
26 | const secretFunction = function(){
27 | return secret;
28 | };
29 | obj.notSecret = function(){
30 | return secret;
31 | };
32 | totallyNotSecret = "I'm defined in the global scope";
33 | return obj;
34 | })();
35 | const s = Object.create(secretTemplate);
36 | console.log(s.normalInfo); // 'this is normal'
37 | console.log(s.secret); // undefined
38 | console.log(s.secretFunction()); // error
39 | console.log(s.notSecret()); // 'sekrit'
40 | console.log(s.totallyNotSecret); // undefined
41 | console.log(totallyNotSecret); // "I'm defined in the global scope"
42 |
43 | // Factory Function (concise)
44 | var secretTemplate = (function(){
45 | const secret = 'sekrit';
46 | const secretFunction = function(){
47 | return secret;
48 | };
49 | totallyNotSecret = "I'm defined in the global scope";
50 | return {normalInfo: 'this is normal',
51 | notSecret(){
52 | return secret;
53 | }}
54 | })();
55 | const s = Object.create(secretTemplate);
56 | console.log(s.normalInfo); // 'this is normal'
57 | console.log(s.secret); // undefined
58 | console.log(s.secretFunction()); // error
59 | console.log(s.notSecret()); // 'sekrit'
60 | console.log(s.totallyNotSecret); // undefined
61 | console.log(totallyNotSecret); // "I'm defined in the global scope"
62 |
63 | // Module Pattern
64 | var secretTemplate = (function(){
65 | const secret = 'sekrit';
66 | const secretFunction = function(){
67 | return secret;
68 | };
69 | totallyNotSecret = "I'm defined in the global scope";
70 | return {normalInfo: 'this is normal',
71 | notSecret(){
72 | return secret;
73 | }};
74 | })();
75 | const s = Object.create(secretTemplate);
76 | console.log(s.normalInfo); // 'this is normal'
77 | console.log(s.secret); // undefined
78 | console.log(s.secretFunction()); // error
79 | console.log(s.notSecret()); // 'sekrit'
80 | console.log(s.totallyNotSecret); // undefined
81 | console.log(totallyNotSecret); // "I'm defined in the global scope"
82 |
83 | // Revealing Module Pattern
84 | var secretTemplate = (function(){
85 | const secret = 'sekrit';
86 | const secretFunction = function(){
87 | return secret;
88 | };
89 | totallyNotSecret = "I'm defined in the global scope";
90 | const normalInfo = 'this is normal';
91 | const notSecret = function(){
92 | return secret;
93 | };
94 | return {normalInfo, notSecret};
95 | })();
96 | const s = Object.create(secretTemplate);
97 | console.log(s.normalInfo); // 'this is normal'
98 | console.log(s.secret); // undefined
99 | console.log(s.secretFunction()); // error
100 | console.log(s.notSecret()); // 'sekrit'
101 | console.log(s.totallyNotSecret); // undefined
102 | console.log(totallyNotSecret); // "I'm defined in the global scope"
103 |
--------------------------------------------------------------------------------
/code/ch08/1_lets_build_a_hierarchy.js:
--------------------------------------------------------------------------------
1 | // // Two Classes
2 | // class EnglishWord{
3 | // constructor(word){
4 | // this.word = word;
5 | // };
6 | // count(){
7 | // return this.word.length;
8 | // };
9 | // };
10 | // class JapaneseWord{
11 | // constructor(word){
12 | // this.word = word;
13 | // }
14 | // count(){
15 | // return this.word.length;
16 | // }
17 | // };
18 | // const japaneseWord = new JapaneseWord("犬");
19 | // const englishWord = new EnglishWord("dog");
20 | // console.log(japaneseWord.word);
21 | // console.log(japaneseWord.count());
22 | // console.log(englishWord.word);
23 | // console.log(englishWord.count());
24 | //
25 | // // With Parent Class
26 | // class Word{
27 | // constructor(word){
28 | // this.word = word;
29 | // }
30 | // count(){
31 | // return this.word.length;
32 | // }
33 | // };
34 | //
35 | // class EnglishWord extends Word{};
36 | // class JapaneseWord extends Word{};
37 | //
38 | // const japaneseWord = new JapaneseWord("犬");
39 | // const englishWord = new EnglishWord("dog");
40 | // console.log(japaneseWord.count());
41 | // console.log(japaneseWord.word);
42 | // console.log(englishWord.count());
43 | // console.log(englishWord.word);
44 |
45 | // Final
46 |
47 | class Word{
48 | constructor(word, language, lookUpUrl){
49 | this.word = word;
50 | this.language = language;
51 | this.lookUpUrl = lookUpUrl;
52 | };
53 | count(){
54 | return this.word.length;
55 | };
56 | lookUp(){
57 | return this.lookUpUrl + this.word;
58 | };
59 | };
60 | class EnglishWord extends Word{
61 | constructor(word){
62 | super(word, 'English', 'https://en.wiktionary.org/wiki/');
63 | };
64 | };
65 | class JapaneseWord extends Word{
66 | constructor(word){
67 | super(word, 'Japanese', 'http://jisho.org/search/');
68 | };
69 | };
70 |
71 | const japaneseWord = new JapaneseWord("犬");
72 | const englishWord = new EnglishWord("dog");
73 |
74 | const wish = require('wish');
75 | const deepEqual = require('deep-equal')
76 |
77 | // interfaces tests
78 | wish(japaneseWord.word === "犬");
79 | wish(japaneseWord.lookUp() === "http://jisho.org/search/犬");
80 | wish(japaneseWord.count() === 1);
81 |
82 | wish(englishWord.word === "dog");
83 | wish(englishWord.lookUp() === "https://en.wiktionary.org/wiki/dog");
84 | wish(englishWord.count() === 3);
85 |
86 | // internals tests
87 | wish(typeof japaneseWord === 'object');
88 | wish(typeof JapaneseWord === 'function');
89 | wish(japaneseWord instanceof JapaneseWord);
90 | wish(japaneseWord instanceof Word);
91 | wish(!(JapaneseWord instanceof Word));
92 |
93 | wish(japaneseWord.constructor === JapaneseWord);
94 | wish(Object.getPrototypeOf(JapaneseWord) === Word);
95 |
96 | // sketchy bits
97 | wish(deepEqual(Object.getPrototypeOf(japaneseWord), {}));
98 | console.log(Object.getPrototypeOf(japaneseWord));
99 | // reports JapaneseWord {}
100 |
--------------------------------------------------------------------------------
/code/ch08/2_lets_wreck_our_hierarchy.js:
--------------------------------------------------------------------------------
1 |
2 | function Word(word, language, lookUpUrl){
3 | this.word = word;
4 | this.language = language;
5 | this.lookUpUrl = lookUpUrl;
6 | this.count = function(){
7 | return this.word.length;
8 | };
9 | this.lookUp = function(){
10 | return this.lookUpUrl + this.word;
11 | };
12 | };
13 |
14 | function EnglishWord(word){
15 | Word.call(this, word, "English", 'https://en.wiktionary.org/wiki/');
16 | };
17 |
18 | function JapaneseWord(word){
19 | Word.call(this, word, "Japanese", 'http://jisho.org/search/');
20 | };
21 |
22 | /* Variant
23 | function Word(word, language, lookUpUrl){
24 | this.word = word;
25 | this.language = language;
26 | this.lookUpUrl = lookUpUrl;
27 | this.count = function(){
28 | return this.word.length;
29 | };
30 | this.lookUp = function(){
31 | return this.lookUpUrl + this.word;
32 | };
33 | };
34 |
35 | function EnglishWord(word){
36 | Word.call(this, word, "English", 'https://en.wiktionary.org/wiki/');
37 | };
38 |
39 | function JapaneseWord(word){
40 | Word.call(this, word, "Japanese", 'http://jisho.org/search/');
41 | };
42 | */
43 |
44 |
45 | JapaneseWord.prototype = Object.create(Word.prototype);
46 | JapaneseWord.prototype.constructor = JapaneseWord;
47 | EnglishWord.prototype = Object.create(Word.prototype);
48 | EnglishWord.prototype.constructor = EnglishWord;
49 |
50 |
51 | Word.prototype.reportLanguage = function(){
52 | return `The language is: ${this.language}`;
53 | };
54 | const japaneseWord = new JapaneseWord("犬");
55 | console.log(japaneseWord.reportLanguage());
56 |
57 | const englishWord = new EnglishWord("dog");
58 |
59 | const wish = require('wish');
60 | const deepEqual = require('deep-equal');
61 |
62 | // interfaces tests
63 | wish(japaneseWord.word === "犬");
64 | wish(japaneseWord.lookUp() === "http://jisho.org/search/犬");
65 | wish(japaneseWord.count() === 1);
66 |
67 | wish(englishWord.word === "dog");
68 | wish(englishWord.lookUp() === "https://en.wiktionary.org/wiki/dog");
69 | wish(englishWord.count() === 3);
70 |
71 | // internals tests
72 | wish(typeof japaneseWord === 'object');
73 | wish(typeof JapaneseWord === 'function');
74 | wish(japaneseWord instanceof JapaneseWord);
75 | wish(japaneseWord instanceof Word);
76 | wish(!(JapaneseWord instanceof Word));
77 |
78 | wish(japaneseWord.constructor === JapaneseWord);
79 | // wish(Object.getPrototypeOf(JapaneseWord) === Word);
80 |
81 | // sketchy bits
82 | // wish(deepEqual(Object.getPrototypeOf(japaneseWord), {}));
83 | console.log(Object.getPrototypeOf(japaneseWord));
84 | // JapaneseWord { constructor: [Function: JapaneseWord] }
85 |
--------------------------------------------------------------------------------
/code/ch08/3_object_literals.js:
--------------------------------------------------------------------------------
1 | const word = {
2 | count(){
3 | return this.word.length;
4 | },
5 | lookUp(){
6 | return this.lookUpUrl + this.word;
7 | }
8 | };
9 | /* Variant
10 | const englishWord = Object.create(word);
11 | englishWord.word = 'dog';
12 | englishWord.language = 'English';
13 | englishWord.lookUpUrl = 'https://en.wiktionary.org/wiki/';
14 |
15 | const japaneseWord = Object.create(word);
16 | japaneseWord.word = '犬';
17 | japaneseWord.language = 'Japanese';
18 | japaneseWord.lookUpUrl = 'http://jisho.org/search/';
19 | */
20 |
21 | const englishWord = Object.assign(Object.create(word),
22 | {word: 'dog',
23 | language: 'English',
24 | lookUpUrl: 'https://en.wiktionary.org/wiki/'});
25 |
26 | const japaneseWord = Object.assign(Object.create(word),
27 | {word: '犬',
28 | language: 'japanese',
29 | lookUpUrl: 'http://jisho.org/search/'});
30 |
31 |
32 |
33 | const wish = require('wish');
34 | const deepEqual = require('deep-equal');
35 |
36 | // interfaces tests
37 | wish(japaneseWord.word === "犬");
38 | wish(japaneseWord.lookUp() === "http://jisho.org/search/犬");
39 | wish(japaneseWord.count() === 1);
40 |
41 | wish(englishWord.word === "dog");
42 | wish(englishWord.lookUp() === "https://en.wiktionary.org/wiki/dog");
43 | wish(englishWord.count() === 3);
44 |
45 | // internals tests
46 | wish(typeof japaneseWord === 'object');
47 | console.log(Object.getPrototypeOf(japaneseWord));
48 |
--------------------------------------------------------------------------------
/code/ch08/4_factory_functions.js:
--------------------------------------------------------------------------------
1 | const word = {
2 | count(){
3 | return this.word.length;
4 | },
5 | lookUp(){
6 | return this.lookUpUrl + this.word;
7 | }
8 | }
9 | const englishWordFactory = (theWord) => {
10 | return Object.assign(Object.create(word),
11 | {word: theWord,
12 | language: 'English',
13 | lookUpUrl: 'https://en.wiktionary.org/wiki/'})
14 | };
15 |
16 | const japaneseWordFactory = (theWord) => {
17 | return Object.assign(Object.create(word),
18 | {word: theWord,
19 | language: 'Japanese',
20 | lookUpUrl: 'http://jisho.org/search/'})
21 | };
22 |
23 |
24 | const wish = require('wish');
25 | const deepEqual = require('deep-equal');
26 |
27 |
28 | const englishWord = englishWordFactory('dog');
29 | const japaneseWord = japaneseWordFactory('犬');
30 |
31 | japaneseWord.prototype = word;
32 | englishWord.prototype = word;
33 |
34 | word.reportLanguage = function(){
35 | return `The language is: ${this.language}`;
36 | };
37 |
38 | console.log(japaneseWord.reportLanguage());
39 | console.log(englishWord.reportLanguage());
40 |
41 |
42 |
43 | // interfaces tests
44 | wish(japaneseWord.word === "犬");
45 | wish(japaneseWord.lookUp() === "http://jisho.org/search/犬");
46 | wish(japaneseWord.count() === 1);
47 |
48 | wish(englishWord.word === "dog");
49 | wish(englishWord.lookUp() === "https://en.wiktionary.org/wiki/dog");
50 | wish(englishWord.count() === 3);
51 |
--------------------------------------------------------------------------------
/code/ch08/4_factory_functions_with_prototype.js:
--------------------------------------------------------------------------------
1 | const wordFactory = function(){
2 | return {count(){
3 | return this.word.length;
4 | },
5 | lookUp(){
6 | return this.lookUpUrl + this.word;
7 | }
8 | };
9 | };
10 |
11 | const englishWordFactory = (theWord) => {
12 | let copy = Object.assign(wordFactory(),
13 | {word: theWord,
14 | language: 'English',
15 | lookUpUrl: 'https://en.wiktionary.org/wiki/'})
16 | return Object.setPrototypeOf(copy, wordFactory);
17 | };
18 |
19 | const japaneseWordFactory = (theWord) =>{
20 | let copy = Object.assign(wordFactory(),
21 | {word: theWord,
22 | language: 'Japanese',
23 | lookUpUrl: 'http://jisho.org/search/'})
24 | return Object.setPrototypeOf(copy, wordFactory);
25 | };
26 | const englishWord = englishWordFactory('dog');
27 | const japaneseWord = japaneseWordFactory('犬');
28 |
29 | wordFactory.reportLanguage = function(){
30 | return `The language is: ${this.language}`;
31 | };
32 |
33 | console.log(japaneseWord.reportLanguage());
34 | console.log(englishWord.reportLanguage());
35 |
36 | const wish = require('wish');
37 | const deepEqual = require('deep-equal');
38 |
39 |
40 | // interfaces tests
41 | wish(japaneseWord.word === "犬");
42 | wish(japaneseWord.lookUp() === "http://jisho.org/search/犬");
43 | wish(japaneseWord.count() === 1);
44 |
45 | wish(englishWord.word === "dog");
46 | wish(englishWord.lookUp() === "https://en.wiktionary.org/wiki/dog");
47 | wish(englishWord.count() === 3);
48 |
--------------------------------------------------------------------------------
/code/ch08/5_multiple_inheritance.js:
--------------------------------------------------------------------------------
1 | // class Barky{
2 | // bark(){ console.log('woof woof')};
3 | // };
4 | // class Bitey{
5 | // bark(){ console.log('grrr')};
6 | // bite(){ console.log('real bite')};
7 | // };
8 | // class Animal{
9 | // beFluffy(){ console.log('fluffy')};
10 | // bite(){ console.log('normal bite')};
11 | // };
12 | //
13 | // // this is not possible:
14 | // class Dog extends (Animal, Barky, Bitey) { };
15 | // dog = new Dog;
16 | // dog.bite();
17 | // dog.beFluffy(); // this won't work
18 |
19 | // Multiple Inheritance
20 |
21 | const barky = {
22 | bark(){ console.log('woof woof')}
23 | };
24 | const bitey = {
25 | bark(){ console.log('grrr')},
26 | bite(){ console.log('real bite')}
27 | };
28 | const animal = {
29 | beFluffy(){ console.log('fluffy')},
30 | bite(){ console.log('normal bite')}
31 | };
32 | const myPet = Object.assign(Object.create(animal), barky, bitey);
33 | myPet.beFluffy();
34 | myPet.bite();
35 |
--------------------------------------------------------------------------------
/code/ch08/6_hyperextension.js:
--------------------------------------------------------------------------------
1 | class Report{
2 | constructor(params){
3 | this.params = params;
4 | };
5 | printReport(params){
6 | return params;
7 | };
8 | }
9 | class GenericReport extends Report{
10 | constructor(params){
11 | super(params);
12 | this.params = params;
13 | };
14 | printReport(params){
15 | return super.printReport(Object.assign(this.params, params));
16 | };
17 | };
18 | class ClientReport extends GenericReport{
19 | constructor(params){
20 | super(params);
21 | this.params = params;
22 | };
23 | printReport(params){
24 | return super.printReport(Object.assign(this.params, params));
25 | };
26 | };
27 | class SpecificClientReport extends ClientReport{
28 | constructor(params){
29 | super(params);
30 | this.params = params;
31 | };
32 | printReport(params){
33 | return super.printReport(Object.assign(this.params, params));
34 | };
35 | };
36 | report = new SpecificClientReport({whatever: 'we want', to: 'add'});
37 |
38 | const wish = require('wish');
39 | const deepEqual = require('deep-equal');
40 | wish(deepEqual(report.printReport({extra: 'params'}),
41 | {whatever:"we want", to:"add", extra:"params"}));
42 |
--------------------------------------------------------------------------------
/code/ch08/6_hyperextension_fixed.js:
--------------------------------------------------------------------------------
1 | class Report{
2 | constructor(params){
3 | this.params = params;
4 | };
5 | printReport(params){
6 | return Object.assign(this.params, params);
7 | };
8 | };
9 |
10 |
11 | const wish = require('wish');
12 | const deepEqual = require('deep-equal');
13 | const report = new Report({whatever: 'we want', to: 'add'});
14 | wish(deepEqual(report.printReport({extra: 'params'}),
15 | {whatever:"we want", to:"add", extra:"params"}))
16 |
--------------------------------------------------------------------------------
/code/ch08/7_goat_cabbage_wolf.js:
--------------------------------------------------------------------------------
1 | class Agent{
2 | constructor(name, type){
3 | this.name = 'name';
4 | if(Math.random() > .5){
5 | this.type = 'user';
6 | }else{
7 | this.type = 'project';
8 | }
9 | };
10 | static makeProjectOrUser(agent){
11 | if(agent.type === 'user'){
12 | return Object.assign(Object.create(new User), agent);
13 | }else{
14 | return Object.assign(Object.create(new Project), agent);
15 | }
16 | };
17 | };
18 |
19 | class User extends Agent{
20 | sayName(){
21 | return `my name is ${this.name}`;
22 | }
23 | };
24 |
25 | class Project extends Agent{
26 | sayTheName(){
27 | return `the project name is ${this.name}`;
28 | }
29 | };
30 |
31 | const agent = new Agent('name');
32 | const projectOrUser = Agent.makeProjectOrUser(agent);
33 | if(projectOrUser.type === 'user'){
34 | console.log(projectOrUser.sayName());
35 | }else{
36 | console.log(projectOrUser.sayTheName());
37 | }
38 | const wish = require('wish');
39 | if(projectOrUser.type === 'user'){
40 | wish(projectOrUser.sayName() === "my name is name");
41 | }else{
42 | wish(projectOrUser.sayTheName() === "the project name is name");
43 | }
44 |
--------------------------------------------------------------------------------
/code/ch08/7_goat_cabbage_wolf_fixed.js:
--------------------------------------------------------------------------------
1 | function coinToss(){
2 | return Math.random() > .5;
3 | };
4 |
5 | class User{
6 | constructor(name){
7 | this.name = name;
8 | };
9 | sayName(){
10 | return `my name is ${this.name}`;
11 | };
12 | };
13 |
14 | class Project{
15 | constructor(name){
16 | this.name = name;
17 | };
18 | sayTheName(){
19 | return `the project name is ${this.name}`;
20 | };
21 | };
22 |
23 | let agent;
24 | if(coinToss()){
25 | agent = new User('name');
26 | }else{
27 | agent = new Project('name');
28 | }
29 |
30 | const wish = require('wish');
31 | if(agent instanceof User){
32 | wish(agent.sayName() === "my name is name");
33 | }else{
34 | wish(agent.sayTheName() === "the project name is name");
35 | }
36 |
37 | wish(new User('name', 'user').sayName() === "my name is name");
38 | wish(new Project('name', 'project').sayTheName()
39 | === "the project name is name");
40 |
--------------------------------------------------------------------------------
/code/ch09/1_template.js:
--------------------------------------------------------------------------------
1 | class Person{
2 | constructor(binaryKnower){
3 | this.binaryKnower = binaryKnower;
4 | };
5 | whatIs(number){ return number };
6 | whatIsInBinary(number){ return Number('0b' + number) };
7 | };
8 |
9 | const personOne = new Person(true);
10 | const personTwo = new Person(false);
11 |
12 | [personOne, personTwo].forEach(person => {
13 | if(person.binaryKnower){
14 | console.log(person.whatIsInBinary(10));
15 | } else{
16 | console.log(person.whatIs(10));
17 | }
18 | });
19 |
--------------------------------------------------------------------------------
/code/ch09/1_template_final.js:
--------------------------------------------------------------------------------
1 | class Person{
2 | log(number){
3 | console.log(this.whatIs(number));
4 | };
5 | };
6 |
7 | class BinaryKnower extends Person{
8 | whatIs(number){ return Number('0b' + number) };
9 | };
10 |
11 | class BinaryOblivious extends Person{
12 | whatIs(number){ return number };
13 | };
14 | const personOne = new BinaryKnower();
15 | const personTwo = new BinaryOblivious();
16 | [personOne, personTwo].forEach(person => person.log(10));
17 |
--------------------------------------------------------------------------------
/code/ch09/1_template_functional.js:
--------------------------------------------------------------------------------
1 | function log(person, number){
2 | console.log(person.whatIs(number));
3 | };
4 | class BinaryKnower{ whatIs(number){ return Number('0b' + number) } };
5 | class BinaryOblivious{ whatIs(number){ return number } };
6 | const personOne = new BinaryKnower();
7 | const personTwo = new BinaryOblivious();
8 | [personOne, personTwo].forEach(person => { log(person, 10) });
9 |
10 |
11 | // No objects
12 | // function log(fun, number){
13 | // console.log(fun(number));
14 | // };
15 | // function whatIsInBinary(number){return Number('0b' + number)};
16 | // function whatIs(number){return number};
17 | //
18 | // [whatIsInBinary, whatIs].forEach(fun => { log(fun, 10) });
19 |
--------------------------------------------------------------------------------
/code/ch09/2_strategy.js:
--------------------------------------------------------------------------------
1 | class Person{
2 | constructor(whatIs){ this.whatIs = whatIs };
3 | log(number){ console.log(this.whatIs(number)) };
4 | };
5 |
6 | const binary = {
7 | aware(number){ return Number('0b' + number) },
8 | oblivious(number){ return number }
9 | };
10 |
11 | const personOne = new Person(binary.aware);
12 | const personTwo = new Person(binary.oblivious);
13 |
14 | [personOne, personTwo].forEach(person => { person.log(10) });
15 |
--------------------------------------------------------------------------------
/code/ch09/3_state.js:
--------------------------------------------------------------------------------
1 | class Person{
2 | constructor(readKnowledge, andKnowledge, xorKnowledge){
3 | this.read = readKnowledge;
4 | this.and = andKnowledge;
5 | this.xor = xorKnowledge;
6 | };
7 | };
8 |
9 | const binary = {
10 | readAware(number){
11 | return Number('0b' + number);
12 | },
13 | readOblivious(number){
14 | return number;
15 | },
16 | andAware(numberOne, numberTwo){
17 | return numberOne & numberTwo;
18 | },
19 | andOblivious(numberOne, numberTwo){
20 | return "unknown";
21 | },
22 | xorAware(numberOne, numberTwo){
23 | return numberOne ^ numberTwo;
24 | },
25 | xorOblivious(numberOne, numberTwo){
26 | return "unknown";
27 | }
28 | };
29 |
30 | const personOne = new Person(binary.readAware,
31 | binary.andAware,
32 | binary.xorAware);
33 | const personTwo = new Person(binary.readOblivious,
34 | binary.andOblivious,
35 | binary.xorOblivious);
36 |
37 | [personOne, personTwo].forEach(person => {
38 | console.log(person.read(10));
39 | console.log(person.and(2, 3));
40 | console.log(person.xor(2, 3));
41 | });
42 |
--------------------------------------------------------------------------------
/code/ch09/3_state_final.js:
--------------------------------------------------------------------------------
1 | class Person{
2 | constructor(binaryKnowledge){
3 | this.binaryKnowledge = Object.create(
4 | Object.assign(
5 | {person: this},
6 | binaryKnowledge));
7 | };
8 | change(binaryKnowledge){
9 | this.binaryKnowledge = Object.create(
10 | Object.assign(
11 | {person: this},
12 | binaryKnowledge));
13 | };
14 | };
15 |
16 | const binaryAwareness = {
17 | read(number){
18 | return Number('0b' + number);
19 | },
20 | and(numberOne, numberTwo){
21 | return numberOne & numberTwo;
22 | },
23 | xor(numberOne, numberTwo){
24 | return numberOne ^ numberTwo;
25 | },
26 | forget(){
27 | this.person.change(binaryObliviousness);
28 | }
29 | }
30 | const binaryObliviousness = {
31 | read(number){
32 | return number;
33 | },
34 | and(numberOne, numberTwo){
35 | return "unknown";
36 | },
37 | xor(number){
38 | return "unknown";
39 | },
40 | learn(){
41 | this.person.change(binaryAwareness);
42 | }
43 | };
44 |
45 | const personOne = new Person(binaryAwareness);
46 | const personTwo = new Person(binaryObliviousness);
47 |
48 | [personOne, personTwo].forEach(person => {
49 | console.log(person.binaryKnowledge.read(10));
50 | console.log(person.binaryKnowledge.and(2, 3));
51 | console.log(person.binaryKnowledge.xor(2, 3));
52 | });
53 |
54 | personOne.binaryKnowledge.forget();
55 | personTwo.binaryKnowledge.learn();
56 |
57 | [personOne, personTwo].forEach(person => {
58 | console.log(person.binaryKnowledge.read(10));
59 | console.log(person.binaryKnowledge.and(2, 3));
60 | console.log(person.binaryKnowledge.xor(2, 3));
61 | });
62 | personTwo.binaryKnowledge.forget();
63 | personTwo.binaryKnowledge.read = () => 'will not assign both';
64 | [personOne, personTwo].forEach(person => {
65 | console.log(person.binaryKnowledge.read(3));
66 | });
67 |
--------------------------------------------------------------------------------
/code/ch09/4_null_object.js:
--------------------------------------------------------------------------------
1 | class Person {
2 | constructor(name){
3 | this.name = name;
4 | }
5 | };
6 | class AnonymousPerson extends Person {
7 | constructor(){
8 | super();
9 | this.name = null;
10 | }
11 | };
12 |
13 | function capitalize(string) {
14 | if(string === null){
15 | return null;
16 | }else{
17 | return string[0].toUpperCase() + string.substring(1);
18 | }
19 | };
20 |
21 | function tigerify(string) {
22 | if(string === null){
23 | return null;
24 | }else{
25 | return `${string}, the tiger`;
26 | }
27 | };
28 |
29 | function display(string){
30 | if(string === null){
31 | return '';
32 | }else{
33 | return string;
34 | }
35 | };
36 |
37 | personOne = new Person("tony");
38 | personTwo = new AnonymousPerson("tony");
39 | console.log(display(tigerify(capitalize(personOne.name))));
40 | console.log(display(tigerify(capitalize(personTwo.name))));
41 |
--------------------------------------------------------------------------------
/code/ch09/4_null_object_final.js:
--------------------------------------------------------------------------------
1 | class Person {
2 | constructor(name){
3 | this.name = new NameString(name);
4 | }
5 | };
6 |
7 | class AnonymousPerson extends Person {
8 | constructor(){
9 | super();
10 | this.name = new NullString;
11 | }
12 | };
13 |
14 | class NullString{
15 | capitalize(){
16 | return this; // same as new NullString in this case
17 | };
18 | tigerify() {
19 | return this; // same as new NullString in this case
20 | };
21 | display() {
22 | return '';
23 | };
24 | };
25 |
26 | class NameString extends String{
27 | capitalize() {
28 | return new NameString(this[0].toUpperCase() + this.substring(1));
29 | };
30 | tigerify() {
31 | return new NameString(`${this}, the tiger`);
32 | };
33 | display(){
34 | return this.toString();
35 | };
36 | }
37 |
38 | personOne = new Person("tony");
39 | personTwo = new AnonymousPerson("tony");
40 | console.log(personOne.name.capitalize().tigerify().display());
41 | console.log(personTwo.name.capitalize().tigerify().display());
42 |
--------------------------------------------------------------------------------
/code/ch09/5_adapter.js:
--------------------------------------------------------------------------------
1 | class Target{
2 | hello(){
3 | console.log('hello');
4 | };
5 | goodbye(){
6 | console.log('goodbye');
7 | };
8 |
9 | };
10 | class Adaptee{
11 | hi(){
12 | console.log('hi');
13 | };
14 | bye(){
15 | console.log('bye');
16 | };
17 | };
18 |
19 | const formal = new Target;
20 | formal.hello();
21 | formal.goodbye();
22 |
23 | const casual = new Adaptee;
24 | casual.hi();
25 | casual.bye();
26 |
27 | class Adapter{
28 | constructor(adaptee){
29 | this.hello = adaptee.hi;
30 | this.goodbye = adaptee.bye;
31 | };
32 | };
33 | const adaptedCasual = new Adapter(new Adaptee);
34 | adaptedCasual.hello();
35 | adaptedCasual.goodbye();
36 |
--------------------------------------------------------------------------------
/code/ch09/5_wrapper_dog.js:
--------------------------------------------------------------------------------
1 | class Dog{
2 | constructor(){
3 | this.cost = 50;
4 | }
5 | displayPrice(){
6 | return `The dog costs $${this.cost}.`;
7 | }
8 | };
9 |
10 | function Cute(dog){
11 | const cuteDog = Object.create(dog);
12 | cuteDog.cost = dog.cost + 20;
13 | return cuteDog;
14 | };
15 |
16 | function Trained(dog){
17 | const trainedDog = Object.create(dog);
18 | trainedDog.cost = dog.cost + 60;
19 | return trainedDog;
20 | };
21 |
22 | const test = require('tape');
23 |
24 | test("trained/cute dog price", (assert) => {
25 | assert.equal(Trained(Cute(new Dog)).displayPrice(),
26 | 'The dog costs $130.');
27 | assert.end();
28 | });
29 |
30 | test("cute dog price", (assert) => {
31 | assert.equal((Cute(new Dog)).displayPrice(), 'The dog costs $70.');
32 | assert.end();
33 | });
34 |
35 | test("base dog price", (assert) => {
36 | assert.equal((new Dog).displayPrice(), 'The dog costs $50.');
37 | assert.end();
38 | });
39 |
--------------------------------------------------------------------------------
/code/ch09/5_wrapper_null_object.js:
--------------------------------------------------------------------------------
1 | class Person {
2 | constructor(name){
3 | this.name = new NameString(name);
4 | }
5 | };
6 |
7 | class AnonymousPerson extends Person {
8 | constructor(){
9 | super();
10 | this.name = null;
11 | }
12 | };
13 |
14 | class NameString extends String{
15 | capitalize() {
16 | return new NameString(this[0].toUpperCase() + this.substring(1));
17 | };
18 | tigerify() {
19 | return new NameString(`${this}, the tiger`);
20 | };
21 | display(){
22 | return this.toString();
23 | };
24 | };
25 |
26 | class NullString{
27 | capitalize(){
28 | return this;
29 | };
30 | tigerify() {
31 | return this;
32 | };
33 | display() {
34 | return '';
35 | };
36 | };
37 |
38 | function WithoutNull(person){
39 | personWithoutNull = Object.create(person);
40 | if(personWithoutNull.name === null){
41 | personWithoutNull.name = new NullString;
42 | };
43 | return personWithoutNull;
44 | };
45 |
46 | const test = require('tape');
47 |
48 | test("Displaying a person", (assert) => {
49 | const personOne = new Person("tony");
50 | assert.equal(personOne.name.capitalize().tigerify().display(),
51 | 'Tony, the tiger');
52 | assert.end();
53 | });
54 | test("Displaying an anonymous person", (assert) => {
55 | const personTwo = new AnonymousPerson("tony");
56 | assert.equal(WithoutNull(personTwo)
57 | .name.capitalize().tigerify().display(),
58 | '');
59 | assert.end();
60 | });
61 |
--------------------------------------------------------------------------------
/code/ch09/6_facade.js:
--------------------------------------------------------------------------------
1 | const page = {
2 | say(string){
3 | console.log(string);
4 | },
5 | yell(string){
6 | alert(string);
7 | },
8 | addNewLine(){
9 | document.body.appendChild(document.createElement("br"));
10 | },
11 |
12 | addButton(text){
13 | const button = document.createElement("button");
14 | button.appendChild(document.createTextNode(text));
15 | document.body.appendChild(button);
16 | },
17 | addText(text){
18 | const span = document.createElement("span");
19 | span.appendChild(document.createTextNode(text));
20 | document.body.appendChild(span);
21 | },
22 | changeBackground(color){
23 | document.body.style.background = color;
24 | },
25 | now(asNumber = false){
26 | if(asNumber === false){
27 | return new Date().toLocaleTimeString();
28 | }else{
29 | return new Date().getTime();
30 | }
31 | },
32 | timeOnPage(){
33 | return ((this.now(true) - this._start) / 1000) + " seconds";
34 | },
35 | loadTime(){
36 | return ((this._start - this._loaded) / 1000) + " seconds";
37 | },
38 | eventsSoFar(){
39 | console.info(this._events);
40 | },
41 | _events: [],
42 | _start: 'nothing yet',
43 | _loaded: 'nothing yet'
44 | };
45 |
46 | window.onload = function(){
47 | page._start = page.now(true);
48 | page._loaded = performance.timing.navigationStart;
49 | document.onclick = function(event) {
50 | page._events.push(event.target + " clicked at " + page.now());
51 | };
52 | };
53 |
--------------------------------------------------------------------------------
/code/ch10/1_why_async.js:
--------------------------------------------------------------------------------
1 | const http = require('http');
2 | const response = http.get('http://refactoringjs.com')
3 |
4 |
5 | let theResult = [];
6 | http.get('http://refactoringjs.com', (result) => {
7 | result.on('data', (chunk) => {
8 | theResult.push(chunk.toString());
9 | });
10 | });
11 | setTimeout(function(){console.log(theResult)}, 500);
12 |
--------------------------------------------------------------------------------
/code/ch10/2_fixing_pyramid_of_doom.js:
--------------------------------------------------------------------------------
1 | const http = require('http');
2 | const getBody = {
3 | bodyArray: [],
4 | saveBody: function(chunk){
5 | this.bodyArray.push(chunk);
6 | },
7 | getResult: function(result){
8 | result.on('data', this.saveBody.bind(this));
9 | result.on('end', this.printBody.bind(this));
10 | },
11 | printBody: function(){
12 | console.log(this.bodyArray.join(''))
13 | this.allDone();
14 | },
15 | allDone: function(){}
16 | };
17 |
18 | const test = require('tape');
19 |
20 | const testDouble = require('testdouble');
21 |
22 | function setup(){
23 | return Object.create(getBody);
24 | };
25 |
26 | test('our async routine', function (assert) {
27 | const newBody = setup();
28 | newBody.allDone = testDouble.function();
29 | testDouble.when(newBody.allDone()).thenDo(function(){
30 | assert.notEqual(newBody.bodyArray.length, 0)
31 | assert.end()
32 | });
33 | http.get('http://refactoringjs.com',
34 | newBody.getResult.bind(newBody));
35 | });
36 |
--------------------------------------------------------------------------------
/code/ch10/3_callbacks_and_testing.js:
--------------------------------------------------------------------------------
1 | // function addOne(addend){
2 | // console.log(addend + 1);
3 | // };
4 | // addOne(2);
5 | //
6 | // function two(callback){
7 | // callback(2);
8 | // };
9 | // two((addend) => console.log(addend + 1));
10 | //
11 | // two(addOne);
12 | //
13 | // function three(callback){
14 | // setTimeout(function(){
15 | // callback(3);
16 | // },
17 | // 500);
18 | // };
19 | // three(addOne);
20 |
21 | // function three(){
22 | // setTimeout(function(){
23 | // return 3
24 | // },
25 | // 500);
26 | // }
27 | // function addOne(addend){
28 | // console.log(addend + 1);
29 | // };
30 | // addOne(three());
31 |
32 | function addOne(addend, callback){
33 | callback(addend + 1);
34 | };
35 | function three(callback){
36 | setTimeout(function(){
37 | callback(3, console.log);
38 | },
39 | 500);
40 | };
41 | function addOneSync(addend){
42 | return addend + 1;
43 | };
44 |
45 | const test = require('tape');
46 |
47 | test('our addOne function', (assert) => {
48 | addOne(3, (result) => {
49 | assert.equal(result, 4);
50 | assert.end();
51 | });
52 | });
53 |
54 | test('our three function', (assert) => {
55 | three((result, callback) => {
56 | assert.equal(result, 3);
57 | assert.equal(callback, console.log);
58 | });
59 | assert.end();
60 | });
61 |
62 |
63 | test('our addOneSync function', (assert) => {
64 | assert.equal(addOneSync(3), 4);
65 | assert.end();
66 | });
67 |
68 | const testDouble = require('testdouble');
69 | test('our end-to-end test', (assert) => {
70 | testDouble.replace(console, 'log')
71 | three((result, callback) => {
72 | addOne(result, callback)
73 | testDouble.verify(console.log(4));
74 | testDouble.reset();
75 | assert.end();
76 | });
77 | });
78 |
--------------------------------------------------------------------------------
/code/ch10/4_promises.js:
--------------------------------------------------------------------------------
1 | function addOne(addend){
2 | return Promise.resolve(addend + 1);
3 | };
4 |
5 | function four(){
6 | return new Promise((resolve, _reject) => {
7 | setTimeout(() => resolve(4), 500);
8 | });
9 | };
10 |
11 | const test = require('tape');
12 | const testdouble = require('testdouble');
13 |
14 | test('our addOne function', (assert) => {
15 | addOne(3).then((result) => {
16 | assert.equal(result, 4);
17 | assert.end();
18 | });
19 | });
20 |
21 | test('our four function', (assert) => {
22 | four().then((result) => {
23 | assert.equal(result, 4);
24 | assert.end();
25 | });
26 | });
27 |
28 | test('our end-to-end test', (assert) => {
29 | testdouble.replace(console, 'log')
30 | four()
31 | .then(addOne)
32 | .then(console.log)
33 | .then(() => {
34 | testdouble.verify(console.log(5));
35 | assert.pass();
36 | testdouble.reset();
37 | assert.end();
38 | }).catch((e) => {
39 | testdouble.reset();
40 | console.log(e);
41 | });
42 | });
43 |
--------------------------------------------------------------------------------
/code/ch11/10_functional_version_of_nbc.js:
--------------------------------------------------------------------------------
1 | //naive_bayes_functional.js
2 | R = require('ramda');
3 |
4 | const smoothing = 1.01;
5 |
6 | function wordCountForLabel(testWord, relevantTexts){
7 | const equalsTestword = R.equals(testWord);
8 | return R.filter(equalsTestword, _allWords(relevantTexts)).length;
9 | };
10 |
11 | function likelihoodOfWord(word, relevantTexts, numberOfTexts){
12 | return wordCountForLabel(word,
13 | relevantTexts) / numberOfTexts + smoothing;
14 | };
15 |
16 | function likelihoodByLabel(label, newWords, trainedSet){
17 | const relevantTexts = textsForLabel(trainedSet.texts, label)
18 | const initialValue = trainedSet.probabilities[label] + smoothing;
19 | const likelihood = R.product(
20 | newWords.map(newWord =>
21 | likelihoodOfWord(newWord,
22 | relevantTexts,
23 | trainedSet.texts.length))) * initialValue;
24 | return {[label]: likelihood}
25 | }
26 |
27 | function textsForLabel(texts, label){
28 | return R.filter(text => text.label === label)(texts);
29 | }
30 |
31 | function _allWords(theTexts){
32 | return R.flatten(R.pluck('words', theTexts));
33 | };
34 |
35 | function addText(words, label, existingText = []){
36 | return R.concat(existingText, [{words: words, label: label}]);
37 | };
38 |
39 | function train(allTexts) {
40 | const overTextLength = R.divide(R.__, allTexts.length);
41 | return {texts: allTexts,
42 | probabilities: R.map(overTextLength,
43 | R.countBy(R.identity,
44 | R.pluck('label', allTexts)))};
45 | };
46 |
47 | function classify(newWords, trainedSet){
48 | const labelNames = R.keys(trainedSet.probabilities);
49 | return R.reduce((acc, label) =>
50 | R.merge(acc, likelihoodByLabel(label, newWords, trainedSet))
51 | , {}, labelNames);
52 | };
53 |
54 | module.exports = {_allWords: _allWords,
55 | addText: addText,
56 | train: train,
57 | classify: classify}
58 |
--------------------------------------------------------------------------------
/code/ch11/10_functional_version_of_nbc_test.js:
--------------------------------------------------------------------------------
1 | //naive_bayes_functional_test.js
2 | const NB = require('./10_functional_version_of_nbc.js');
3 |
4 | const wish = require('wish');
5 | describe('the file', () => {
6 | const english = NB.addText(['a', 'b', 'c', 'd', 'e', 'f', 'g',
7 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q'],
8 | 'yes')
9 | const moreEnglish = NB.addText(['a', 'e', 'i', 'o', 'u'],
10 | 'yes', english)
11 | const allTexts = NB.addText(['あ', 'い', 'う', 'え', 'お',
12 | 'か', 'き', 'く', 'け', 'こ'],
13 | 'no', moreEnglish)
14 |
15 | var trainedSet = NB.train(allTexts);
16 |
17 | it('works', () => {
18 | wish(true);
19 | })
20 | it('classifies', () =>{
21 | const classified = NB.classify(['お', 'は', 'よ', 'う', 'ご', 'ざ', 'い', 'ま', 'す'], trainedSet);
22 | wish(classified['yes'] === 1.833745640534112);
23 | wish(classified['no'] === 3.456713680099012);
24 | });
25 | it('number of words', ()=>{
26 | wish(NB._allWords(trainedSet.texts).length === 32);
27 | });
28 |
29 | it('label probabilities', ()=>{
30 | wish(trainedSet.probabilities['yes'] === 0.6666666666666666);
31 | wish(trainedSet.probabilities['no'] === 0.3333333333333333);
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/code/ch11/1_restrictions_and_benefits.js:
--------------------------------------------------------------------------------
1 | function factorial(number){
2 | if(number < 2){
3 | return 1;
4 | } else {
5 | return(number * factorial(number - 1));
6 | }
7 | };
8 | factorial(3); //returns 6
9 |
10 | function memoizedFactorial(number, lookupTable = {}){
11 | if(number in lookupTable){
12 | console.log("cached");
13 | return lookupTable[number];
14 | }
15 | else{
16 | console.log("calculating");
17 | var reduceValue;
18 | if(number < 2){
19 | reduceValue = 1;
20 | } else {
21 | reduceValue = number * (memoizedFactorial(number - 1, lookupTable))['result'];
22 | };
23 | lookupTable[number] = reduceValue;
24 | return {result: reduceValue, lookupTable: lookupTable};
25 | }
26 | };
27 | const lookup = memoizedFactorial(10)['lookupTable'];
28 | console.log(memoizedFactorial(10, lookup));
29 |
--------------------------------------------------------------------------------
/code/ch11/2_the_basics.js:
--------------------------------------------------------------------------------
1 | const test = require('tape');
2 | const testdouble = require('testdouble');
3 | var x;
4 |
5 | const impure = {
6 | setGlobalFromAddition(addend1, addend2){
7 | x = add(addend1, addend2);
8 | },
9 | readAddition(addend1, addend2, done){
10 | console.log(add(addend1, addend2));
11 | done();
12 | }
13 | };
14 |
15 | function add(addend1, addend2){
16 | return addend1 + addend2;
17 | };
18 |
19 | test('addition', (assert) => {
20 | assert.equal(add(2, 3), 5);
21 | assert.end();
22 | });
23 |
24 | test('setting global', (assert) => {
25 | impure.setGlobalFromAddition(2, 3);
26 | assert.equal(x, 5);
27 | assert.end();
28 | });
29 |
30 | test('setting global again', (assert) => {
31 | impure.setGlobalFromAddition(2, 8);
32 | assert.equal(x, 10);
33 | assert.end();
34 | });
35 |
36 | test('calling console', (assert) => {
37 | testdouble.replace(console, 'log');
38 | impure.readAddition(2, 3, () => {
39 | testdouble.verify(console.log(5));
40 | assert.pass();
41 | testdouble.reset();
42 | assert.end();
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/code/ch11/3_currying.js:
--------------------------------------------------------------------------------
1 | R = require('ramda');
2 |
3 | function add(numberOne, numberTwo){
4 | return numberOne + numberTwo;
5 | };
6 |
7 | const curriedAdd = R.curry(add);
8 |
9 | console.log(curriedAdd(1));
10 | console.log(curriedAdd(1)(2));
11 | console.log(curriedAdd(1, 2));
12 |
13 | const increment = curriedAdd(1);
14 | console.log(increment(3));
15 |
16 |
17 | const square = (thing) => thing * thing;
18 |
19 | const mapSquares = R.map(square);
20 | console.log(mapSquares([2, 4, 5]));
21 |
22 | console.log(R.map(square, [2, 4, 5]));
23 |
--------------------------------------------------------------------------------
/code/ch11/4_function_composition.js:
--------------------------------------------------------------------------------
1 | _ = require('lodash');
2 | const square = (thing) => thing * thing;
3 |
4 | // const mapSquares = _.map(square);
5 | const mapSquares = (data) => _.map(data, square);
6 | // console.log(mapSquares([2, 4, 5]));
7 |
8 |
9 | // [3, 4, 2].forEach((element) => console.log(element));
10 | // [3, 4, 2].forEach(console.log);
11 |
12 | const logFirst = (first) => console.log(first);
13 | [3, 4, 2].forEach(logFirst);
14 |
15 | R = require('ramda');
16 | console.log(R.map(square, R.map(square, [2, 4, 5])));
17 |
18 | const fourthPower = R.compose(square, square);
19 | console.log(R.map(fourthPower, [2, 4, 5]));
20 |
21 | const mapFourthPower = R.map(fourthPower);
22 | console.log(mapFourthPower([2, 4, 5]));
23 |
24 | const printFourthPower = R.compose(console.log, square, square);
25 | R.map(printFourthPower, [2, 4, 5]);
26 |
27 | R.map(printFourthPower) ([2, 4, 5]);
28 |
29 |
30 | var factorial = R.memoize(n => R.product(R.range(1, n + 1)));
31 |
32 | var printFact = R.compose(console.log, factorial);
33 | printFact(3);
34 |
35 |
36 | var printFactPipe = R.pipe(factorial, console.log);
37 | printFactPipe(3);
38 |
--------------------------------------------------------------------------------
/code/ch11/5_types.js:
--------------------------------------------------------------------------------
1 | console.log(Math.random("beaver"));
2 |
3 |
4 | // types
5 | // add :: Number -> Number -> Number
6 | // add :: (Number, Number) -> Number
7 | // addOne :: Number -> Number
8 | // map :: (a -> b) -> [a] -> [b]
9 | // Maybe#concat :: Semigroup a => Maybe a ~> Maybe a -> Maybe a
10 |
--------------------------------------------------------------------------------
/code/ch11/6_introducing_sanctuary.js:
--------------------------------------------------------------------------------
1 | const {create, env} = require('sanctuary');
2 | const S = create({checkTypes: true, env: env});
3 | R = require('ramda');
4 | // R.add("hello", 3);
5 | // NaN
6 | // S.add("hello", 3);
7 |
8 | // TypeError: Invalid value
9 | // add :: FiniteNumber -> FiniteNumber -> FiniteNumber
10 | // ^^^^^^^^^^^^
11 | // 1
12 | // 1) "hello" :: String
13 | // The value at position 1 is not a member of ‘FiniteNumber’.
14 |
15 | const getAThree = S.find(R.equals(3));
16 | getAThree([3, 4]);
17 | getAThree([8, 4]);
18 |
--------------------------------------------------------------------------------
/code/ch11/7_null_object_again.js:
--------------------------------------------------------------------------------
1 | const {create, env} = require('sanctuary');
2 | const S = create({checkTypes: false, env: env});
3 |
4 | class Person {
5 | constructor(name){
6 | this.name = S.Just(name);
7 | }
8 | };
9 | class AnonymousPerson extends Person {
10 | constructor(){
11 | super();
12 | this.name = S.Nothing();
13 | }
14 | };
15 | const capitalize = (string) => string[0].toUpperCase()
16 | + string.substring(1);
17 | const tigerify = (string) => `${string}, the tiger`;
18 | const display = (string) => string.toString();
19 |
20 | const test = require('tape');
21 | test("Displaying a person", (assert) => {
22 | const personOne = new Person("tony");
23 | assert.equal(S.fromMaybe('',
24 | personOne.name.map(capitalize).map(tigerify)),
25 | 'Tony, the tiger');
26 | assert.end();
27 | });
28 | test("Displaying an anonymous person", (assert) => {
29 | const personTwo = new AnonymousPerson(null);
30 | assert.equal(S.fromMaybe('',
31 | personTwo.name.map(capitalize).map(tigerify)),
32 | '');
33 | assert.end();
34 | });
35 |
--------------------------------------------------------------------------------
/code/ch11/8_functional_refactoring_with_maybe.js:
--------------------------------------------------------------------------------
1 | const {create, env} = require('sanctuary');
2 | const S = create({checkTypes: false, env: env});
3 |
4 | class Person {
5 | constructor(name){
6 | this.name = S.toMaybe(name);
7 | };
8 | };
9 | const capitalize = (string) => string[0].toUpperCase()
10 | + string.substring(1);
11 | const tigerify = (string) => `${string}, the tiger`;
12 | const display = (string) => string.toString();
13 | const capitalTiger = S.compose(capitalize, tigerify);
14 |
15 | const test = require('tape');
16 | test("Displaying a person", (assert) => {
17 | const personOne = new Person("tony");
18 | assert.equal(personOne.name.chain(capitalTiger), 'Tony, the tiger');
19 | assert.end();
20 | });
21 | test("Displaying an anonymous person", (assert) => {
22 | const personTwo = new Person(null);
23 | assert.equal(S.maybe('', capitalTiger, personTwo.name), '');
24 | assert.end();
25 | });
26 |
--------------------------------------------------------------------------------
/code/ch11/9_functional_refactoring_with_either.js:
--------------------------------------------------------------------------------
1 | const {create, env} = require('sanctuary');
2 | const S = create({checkTypes: false, env: env});
3 | const R = require('ramda');
4 |
5 | class Person {
6 | constructor(name){
7 | this.name = S.maybeToEither('', S.toMaybe(name));
8 | }
9 | };
10 |
11 | const capitalize = (string) => string[0].toUpperCase()
12 | + string.substring(1);
13 | const tigerify = (string) => `${string}, the tiger`;
14 |
15 | const capitalTiger = S.compose(capitalize, tigerify);
16 |
17 | const test = require('tape');
18 | test("Displaying a person", (assert) => {
19 | const personOne = new Person("tony");
20 | assert.equal(personOne.name.map(capitalTiger).value,
21 | 'Tony, the tiger');
22 | assert.end();
23 | });
24 | test("Displaying an anonymous person", (assert) => {
25 | const personTwo = new Person(null);
26 | assert.equal(personTwo.name.map(capitalTiger).value, '');
27 | assert.end();
28 | });
29 |
--------------------------------------------------------------------------------
/console_with_context.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/console_with_context.png
--------------------------------------------------------------------------------
/copyright.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Refactoring JavaScript
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | {{ title }}
14 |
15 |
16 | by ??? ?. ???
17 |
18 |
19 | Copyright © 2015 ???. All rights reserved.
20 |
21 | Printed in the United States of America.
22 |
23 |
24 | Published by O’Reilly Media, Inc. , 1005 Gravenstein Highway North, Sebastopol, CA 95472.
25 |
26 | O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most
27 | titles (http://safaribooksonline.com ). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com .
28 |
29 |
30 |
31 | Acquisitions Editor: FILL IN
32 | Editor: TO COME
33 | Production Editor: TO COME
34 | Copyeditor: TO COME
35 | Proofreader: TO COME
36 | Indexer: TO COME
37 | Interior Designer: David Futato
38 | Cover Designer: TO COME
39 | Illustrator: Rebecca Demarest
40 |
41 |
42 |
43 |
44 | Month Year: First Edition
45 |
46 |
47 |
48 |
49 |
Revision History for the First Edition
50 |
51 | yyyy-mm-dd: First Release
52 |
53 |
54 |
55 |
56 | See http://oreilly.com/catalog/errata.csp?isbn=XXXXXXXXXXXXX for release details.
57 |
58 |
59 |
60 |
The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. {{ title }} , the cover image, and related trade dress are trademarks of O’Reilly Media, Inc.
61 |
62 |
While the publisher and the authors have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights.
63 |
64 |
65 |
66 |
xxx-x-xxx-xxxx-x
67 |
[???]
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/cover.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Refactoring JavaScript
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/cover.png
--------------------------------------------------------------------------------
/dedication.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Refactoring JavaScript
6 |
7 |
8 |
9 |
10 |
11 | Dedication Title
12 |
13 | Put your dedication here
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/diagramming_language.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagramming_language.afdesign
--------------------------------------------------------------------------------
/diagrams/diary_creating_function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/diary_creating_function.png
--------------------------------------------------------------------------------
/diagrams/function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function.png
--------------------------------------------------------------------------------
/diagrams/function_anonymous.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_anonymous.png
--------------------------------------------------------------------------------
/diagrams/function_attached_to_anObject.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_attached_to_anObject.png
--------------------------------------------------------------------------------
/diagrams/function_diary_privateUnlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_diary_privateUnlock.png
--------------------------------------------------------------------------------
/diagrams/function_diary_privateUnlock_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_diary_privateUnlock_2.png
--------------------------------------------------------------------------------
/diagrams/function_diary_privateUnlock_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_diary_privateUnlock_3.png
--------------------------------------------------------------------------------
/diagrams/function_diary_read.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_diary_read.png
--------------------------------------------------------------------------------
/diagrams/function_diary_tryLock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_diary_tryLock.png
--------------------------------------------------------------------------------
/diagrams/function_returning_number.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_returning_number.png
--------------------------------------------------------------------------------
/diagrams/function_setIt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_setIt.png
--------------------------------------------------------------------------------
/diagrams/function_with_bulk_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_bulk_1.png
--------------------------------------------------------------------------------
/diagrams/function_with_bulk_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_bulk_2.png
--------------------------------------------------------------------------------
/diagrams/function_with_complex_output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_complex_output.png
--------------------------------------------------------------------------------
/diagrams/function_with_global_implicit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_global_implicit.png
--------------------------------------------------------------------------------
/diagrams/function_with_no_return.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_no_return.png
--------------------------------------------------------------------------------
/diagrams/function_with_on_explicit_input_one_implicit_input.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_on_explicit_input_one_implicit_input.png
--------------------------------------------------------------------------------
/diagrams/function_with_side_effect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_side_effect.png
--------------------------------------------------------------------------------
/diagrams/function_with_two_explicit_inputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_two_explicit_inputs.png
--------------------------------------------------------------------------------
/diagrams/function_with_two_non_local_inputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_two_non_local_inputs.png
--------------------------------------------------------------------------------
/diagrams/function_with_undefined_implicit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diagrams/function_with_undefined_implicit.png
--------------------------------------------------------------------------------
/diary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary.png
--------------------------------------------------------------------------------
/diary10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary10.png
--------------------------------------------------------------------------------
/diary3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary3.png
--------------------------------------------------------------------------------
/diary4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary4.png
--------------------------------------------------------------------------------
/diary5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary5.png
--------------------------------------------------------------------------------
/diary6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary6.png
--------------------------------------------------------------------------------
/diary7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary7.png
--------------------------------------------------------------------------------
/diary8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary8.png
--------------------------------------------------------------------------------
/diary9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary9.png
--------------------------------------------------------------------------------
/diary_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/diary_2.png
--------------------------------------------------------------------------------
/dynamic_this.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/dynamic_this.png
--------------------------------------------------------------------------------
/fantasy_land_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/fantasy_land_diagram.png
--------------------------------------------------------------------------------
/function-circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/function-circle.png
--------------------------------------------------------------------------------
/function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/function.png
--------------------------------------------------------------------------------
/functions_with_bulk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/functions_with_bulk.png
--------------------------------------------------------------------------------
/haskell_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/haskell_diagram.png
--------------------------------------------------------------------------------
/images/__red_green_refactor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/__red_green_refactor.png
--------------------------------------------------------------------------------
/images/_red_green_refactor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/_red_green_refactor.png
--------------------------------------------------------------------------------
/images/_testing_in_action_.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/_testing_in_action_.png
--------------------------------------------------------------------------------
/images/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/cover.png
--------------------------------------------------------------------------------
/images/red_green_refactor.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/red_green_refactor.afdesign
--------------------------------------------------------------------------------
/images/red_green_refactor_.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/red_green_refactor_.png
--------------------------------------------------------------------------------
/images/tarsier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/tarsier.png
--------------------------------------------------------------------------------
/images/testing_in_action.afdesign:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/testing_in_action.afdesign
--------------------------------------------------------------------------------
/images/testing_in_action.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/testing_in_action.png
--------------------------------------------------------------------------------
/images/testing_in_action_.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/images/testing_in_action_.png
--------------------------------------------------------------------------------
/ix.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Refactoring JavaScript
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/mocha_results.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/mocha_results.png
--------------------------------------------------------------------------------
/mocha_test_output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/mocha_test_output.png
--------------------------------------------------------------------------------
/nbc.js:
--------------------------------------------------------------------------------
1 | fs = require('fs');
2 | //songs
3 | imagine = ['c', 'cmaj7', 'f', 'am', 'dm', 'g', 'e7'];
4 | somewhere_over_the_rainbow = ['c', 'em', 'f', 'g', 'am'];
5 | tooManyCooks = ['c', 'g', 'f'];
6 | iWillFollowYouIntoTheDark = ['f', 'dm', 'bb', 'c', 'a', 'bbm'];
7 | babyOneMoreTime = ['cm', 'g', 'bb', 'eb', 'fm', 'ab'];
8 | creep = ['g', 'gsus4', 'b', 'bsus4', 'c', 'cmsus4', 'cm6'];
9 | army = ['ab', 'ebm7', 'dbadd9', 'fm7', 'bbm', 'abmaj7', 'ebm'];
10 | paperBag = ['bm7', 'e', 'c', 'g', 'b7', 'f', 'em', 'a', 'cmaj7', 'em7', 'a7', 'f7', 'b'];
11 | toxic = ['cm', 'eb', 'g', 'cdim', 'eb7', 'd7', 'db7', 'ab', 'gmaj7', 'g7'];
12 | bulletproof = ['d#m', 'g#', 'b', 'f#', 'g#m', 'c#'];
13 | song_11 = [];
14 | var songs = [];
15 | var labels = [];
16 | var allChords = [];
17 | var labelCounts = [];
18 | var labelProbabilities = [];
19 | var chordCountsInLabels = {};
20 | var probabilityOfChordsInLabels = {};
21 | function train(chords, label){
22 | songs.push([label, chords]);
23 | labels.push(label);
24 | for (var i = 0; i < chords.length; i++){
25 | if(!allChords.includes(chords[i])){
26 | allChords.push(chords[i]);
27 | } }
28 | if(!!(Object.keys(labelCounts).includes(label))){
29 | labelCounts[label] = labelCounts[label] + 1;
30 | } else {
31 | labelCounts[label] = 1;
32 | } };
33 | function getNumberOfSongs(){
34 | return songs.length;
35 | };
36 | function setLabelProbabilities(){
37 | Object.keys(labelCounts).forEach(function(label){
38 | var numberOfSongs = getNumberOfSongs();
39 | labelProbabilities[label] = labelCounts[label] / numberOfSongs;
40 | })
41 | };
42 | function setChordCountsInLabels(){
43 | songs.forEach(function(i){
44 | if(chordCountsInLabels[i[0]] === undefined){
45 | chordCountsInLabels[i[0]] = {}
46 | }
47 | i[1].forEach(function(j){
48 | if(chordCountsInLabels[i[0]][j] > 0){
49 | chordCountsInLabels[i[0]][j] = chordCountsInLabels[i[0]][j] + 1;
50 | } else {
51 | chordCountsInLabels[i[0]][j] = 1;
52 | } });
53 | }); }
54 | function setProbabilityOfChordsInLabels(){
55 | probabilityOfChordsInLabels = chordCountsInLabels;
56 | Object.keys(probabilityOfChordsInLabels).forEach(function(i){
57 | Object.keys(probabilityOfChordsInLabels[i]).forEach(function(j){
58 | probabilityOfChordsInLabels[i][j] = probabilityOfChordsInLabels[i][j] * 1.0 / songs.length;
59 | }) })
60 | }
61 | train(imagine, 'easy');
62 | train(somewhere_over_the_rainbow, 'easy');
63 | train(tooManyCooks, 'easy');
64 | train(iWillFollowYouIntoTheDark, 'medium');
65 | train(babyOneMoreTime, 'medium');
66 | train(creep, 'medium');
67 | train(paperBag, 'hard');
68 | train(toxic, 'hard');
69 | train(bulletproof, 'hard');
70 | setLabelProbabilities();
71 | setChordCountsInLabels();
72 | setProbabilityOfChordsInLabels();
73 | function classify(chords){
74 | var ttal = labelProbabilities;
75 | console.log(ttal);
76 | var classified = {}
77 | Object.keys(ttal).forEach(function(obj){
78 | var first = labelProbabilities[obj] + 1.01;
79 | chords.forEach(function(chord){
80 | var probabilityOfChordInLabel = probabilityOfChordsInLabels[obj][chord]
81 | if(probabilityOfChordInLabel === undefined){
82 | first + 1.01
83 | } else {
84 | first = first * (probabilityOfChordInLabel + 1.01)
85 | }
86 | })
87 | classified[obj] = first
88 | });
89 | console.log(classified);
90 | };
91 | classify(['d', 'g', 'e', 'dm']);
92 | classify(['f#m7', 'a', 'dadd9', 'dmaj7', 'bm', 'bm7', 'd', 'f#m']);
93 |
--------------------------------------------------------------------------------
/not_global_context.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/not_global_context.png
--------------------------------------------------------------------------------
/null.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/null.png
--------------------------------------------------------------------------------
/private_function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/private_function.png
--------------------------------------------------------------------------------
/red_green_refactor_.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/red_green_refactor_.png
--------------------------------------------------------------------------------
/side_effect_to_other_this.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/side_effect_to_other_this.png
--------------------------------------------------------------------------------
/testing_in_action.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/testing_in_action.png
--------------------------------------------------------------------------------
/testing_in_action_.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/testing_in_action_.png
--------------------------------------------------------------------------------
/theme/epub/epub.css:
--------------------------------------------------------------------------------
1 | /* Add your custom CSS styles for the EPUB here */
--------------------------------------------------------------------------------
/theme/html/html.css:
--------------------------------------------------------------------------------
1 | /* Add CSS styles for the HTML format here */
2 |
3 | /* Add your custom CSS styles for the HTML here */
--------------------------------------------------------------------------------
/theme/mobi/mobi.css:
--------------------------------------------------------------------------------
1 | /* Add your custom CSS styles for the MOBI here */
--------------------------------------------------------------------------------
/theme/pdf/pdf.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | /*--------Put Your Custom CSS Rules Below--------*/
4 | /*--- This oneoff overrides the code in https://github.com/oreillymedia/animal_theme/blob/master/pdf/pdf.css---*/
5 |
6 | /*--- Font fallback for Japanese characters not represented in standard ---*/
7 | /*--- monospace font. ---*/
8 | pre, code { font-family: UbuntuMono, monospace, ArialUnicodeMS }
9 |
10 | /*----Uncomment to turn on automatic code wrapping
11 |
12 | pre {
13 | white-space: pre-wrap;
14 | word-wrap: break-word;
15 | }
16 | ----*/
17 |
18 | /*----Uncomment to change the TOC start page (set
19 | the number to one page _after_ the one you want;
20 | so 6 to start on v, 8 to start on vii, etc.)
21 |
22 | @page toc:first {
23 | counter-reset: page 6;
24 | }
25 | ----*/
26 |
27 | /*----Uncomment to fix a bad break in the title
28 | (increase padding value to push down, decrease
29 | value to pull up)
30 |
31 | section[data-type="titlepage"] h1 {
32 | padding-left: 1.5in;
33 | }
34 | ----*/
35 |
36 | /*----Uncomment to fix a bad break in the subtitle
37 | (increase padding value to push down, decrease
38 | value to pull up)
39 |
40 | section[data-type="titlepage"] h2 {
41 | padding-left: 1in;
42 | }
43 | ----*/
44 |
45 | /*----Uncomment to fix a bad break in the author names
46 | (increase padding value to push down, decrease
47 | value to pull up)
48 |
49 | section[data-type="titlepage"] p.author {
50 | padding-left: 3in;
51 | }
52 | ----*/
53 |
--------------------------------------------------------------------------------
/timeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/timeline.png
--------------------------------------------------------------------------------
/titlepage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Refactoring JavaScript
6 |
7 |
8 |
9 |
10 |
11 | Book Title Here
12 | ??? Edition
13 | Subtitle Goes Here
14 | Some Author
15 |
16 |
17 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/todo:
--------------------------------------------------------------------------------
1 | ask for tech reviewer, and email nan the name
2 | nan will give all details
3 |
4 | sometime next week, nan will let me know
5 |
6 | 350 is ok. 400 is kind of ok.
7 |
8 | Finish everything in the end of october.
9 | Ally's edits
10 | don't worry about index tags
11 |
12 | how to handle fowler refactorings
13 |
14 | appendix/glossary - table of fowler refactorings
15 |
16 | nan - looking into fowler catalog
17 |
18 | sales?
19 |
20 | images
21 |
22 | tell nan when manual updates for early release are needed
23 |
24 |
--------------------------------------------------------------------------------
/using_strict.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/using_strict.png
--------------------------------------------------------------------------------
/with_inputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/with_inputs.png
--------------------------------------------------------------------------------
/with_outputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/with_outputs.png
--------------------------------------------------------------------------------
/with_side_effects.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EvanBurchard/refactoringjavascript/7cf4a4809c5034a88587a7d47e76b992fb92e8fa/with_side_effects.png
--------------------------------------------------------------------------------