├── API ├── db.json └── start.bat ├── CH 00 ReactApp Linting 2 ├── .eslintrc.json ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ └── index.html └── src │ ├── App.js │ └── index.js ├── CH 00 ReactApp Linting ├── .eslintrc.json ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ └── index.html └── src │ ├── App.js │ └── index.js ├── CH 00 Setup ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── hello.js ├── package-lock.json ├── package.json └── run.bat ├── CH 02 Immutable Data ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── mutation.js ├── package-lock.json ├── package.json └── run.bat ├── CH 03 Functions ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── arrow.js ├── functions.js ├── package-lock.json ├── package.json ├── pure.js └── run.bat ├── CH 04 Array ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── changing.js ├── filter-objects.js ├── filter.js ├── find.js ├── forEach.js ├── impure.js ├── map-objects.js ├── map.js ├── package-lock.json ├── package.json ├── push.js ├── reduce-objects.js ├── reduce.js ├── run.bat └── sort.js ├── CH 05 First-Class Functions ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── array-of-functions.js ├── counter.js ├── higher-order-sort.js ├── higher-order.js ├── package-lock.json ├── package.json ├── run.bat └── run.js ├── CH 06 Statements ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── break-every.js ├── break-find.js ├── for.js ├── if-return.js ├── if-side-effects.js ├── if.js ├── package-lock.json ├── package.json ├── run.bat ├── switch-cond.js ├── switch-map.js ├── switch-return.js ├── switch.js ├── ternary.js ├── var.js └── while.js ├── CH 07 Recursion ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── factorial-conditional.js ├── factorial-recursive.js ├── factorial-tc.js ├── factorial.js ├── package-lock.json ├── package.json ├── run.bat ├── stack-overflow-loop.js ├── stack-overflow.js ├── sum-loop.js ├── sum-recursive.js ├── trampoline.js └── tree.js ├── CH 08 Pipelines ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── chaining-2.js ├── chaining-3.js ├── chaining.js ├── currying-1.js ├── currying-2.js ├── currying-3.js ├── currying-4.js ├── package-lock.json ├── package.json ├── pipe-weather.js ├── pipe.js ├── reduce.js └── run.bat ├── CH 09 Functors ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── F.js ├── array.js ├── array_weather.js ├── mapping.js ├── package-lock.json ├── package.json ├── run.bat ├── test_F.js └── test_F_weather.js ├── CH 10 Monads ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── Applicative.js ├── M-bind.js ├── M.js ├── Maybe.js ├── flat.js ├── flatMap.js ├── map.js ├── package-lock.json ├── package.json ├── run.bat ├── test_Array_laws.js ├── test_M.js ├── test_M_bind.js └── test_M_laws.js ├── CH 11 Immutable Collections ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── List-Operations.js ├── List.js ├── Map-Operations.js ├── Map.js ├── package-lock.json ├── package.json └── run.bat ├── CH 12 Lazy ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── Range.js ├── Seq.js ├── chain.js ├── filter.js ├── find.js ├── map.js ├── package-lock.json ├── package.json ├── prime-2.js ├── prime-3.js ├── prime.js ├── run.bat └── transducer.js ├── CH 13 Generators ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── fibonacci.js ├── forEach.js ├── package-lock.json ├── package.json ├── pipe.js ├── range-finite.js ├── range.js ├── run.bat ├── take.js ├── test_fibonacci.js ├── test_forEach.js ├── test_range.js ├── test_range_finite.js ├── test_range_take.js ├── test_toArray.js └── toArray.js ├── CH 14 Promises AJAX ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── all.js ├── allSettled.js ├── fetch.js ├── index.html ├── insequence.js ├── package-lock.json ├── package.json ├── race.js └── run.bat ├── CH 14 Promises ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── catch.js ├── chain.js ├── delay.js ├── flat-mapping-2.js ├── flat-mapping-3.js ├── flat-mapping.js ├── laws-functor-1.js ├── laws-functor-2.js ├── laws-monad-3.js ├── mappable.js ├── package-lock.json ├── package.json ├── run.bat └── timeout.js ├── CH 15 Observables AJAX ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── ajax.js ├── concatAll.js ├── forkJoin.js ├── hoo.js ├── index.html ├── insequence.js ├── package-lock.json ├── package.json ├── race.js └── run.bat ├── CH 15 Observables ├── .babelrc ├── .eslintrc.json ├── .gitignore ├── combine.js ├── create.js ├── filter.js ├── from.js ├── interval.js ├── laws1.js ├── laws2.js ├── map.js ├── mergeAll.js ├── mergeMap.js ├── observers.js ├── of.js ├── package-lock.json ├── package.json ├── pipe.js ├── run.bat ├── subscription.js └── take.js ├── CH 16 Elm Architecture ├── .gitignore ├── elm.json ├── run.bat └── src │ ├── Counter.elm │ ├── RandomGenerator-update.js │ └── RandomGenerator.elm ├── CH 16 Redux Commands ├── .eslintrc.json ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ └── index.html └── src │ ├── Counter.js │ ├── index.js │ └── store.js ├── CH 16 Redux ├── .eslintrc.json ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ └── index.html └── src │ ├── Counter.js │ ├── index.js │ └── store.js ├── CH 17 Redux Axios ├── .eslintrc.json ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ └── index.html └── src │ ├── List.jsx │ ├── index.js │ └── store.js └── readme.md /API/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "todos": [ 3 | {"id":1, "title": "To Do 1"} 4 | ], 5 | "dictionaries": [ 6 | { "name": "Dictionary 1"} 7 | ] 8 | } -------------------------------------------------------------------------------- /API/start.bat: -------------------------------------------------------------------------------- 1 | json-server --watch db.json -------------------------------------------------------------------------------- /CH 00 ReactApp Linting 2/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app"], 3 | "plugins": [ 4 | "immutable" 5 | ], 6 | "rules": { 7 | "immutable/no-this": "error", 8 | "immutable/no-mutation": "error" 9 | } 10 | } -------------------------------------------------------------------------------- /CH 00 ReactApp Linting 2/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting 2/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting 2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-scripts": "3.4.1" 12 | }, 13 | "scripts": { 14 | "start": "npx eslint src && react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | }, 34 | "devDependencies": { 35 | "eslint": "^7.1.0", 36 | "eslint-plugin-immutable": "^1.0.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting 2/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting 2/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function App() { 4 | return ( 5 |
6 | Learn React 7 |
8 | ); 9 | } 10 | 11 | export default App; 12 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting 2/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | const a = { 6 | x:1 7 | } 8 | 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | document.getElementById('root') 14 | ); 15 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app"], 3 | "plugins": [ 4 | "functional" 5 | ], 6 | "rules": { 7 | "functional/no-this-expression": "error" 8 | } 9 | } -------------------------------------------------------------------------------- /CH 00 ReactApp Linting/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-scripts": "3.4.1" 12 | }, 13 | "scripts": { 14 | "start": "npx eslint src && react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | }, 34 | "devDependencies": { 35 | "eslint": "^7.1.0", 36 | "eslint-plugin-functional": "^3.0.1" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function App() { 4 | return ( 5 |
6 | Learn React 7 |
8 | ); 9 | } 10 | 11 | export default App; 12 | -------------------------------------------------------------------------------- /CH 00 ReactApp Linting/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /CH 00 Setup/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 00 Setup/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 11, 8 | "sourceType": "module" 9 | }, 10 | "plugins": [ 11 | "functional" 12 | ], 13 | "rules": { 14 | "functional/no-this-expression": "error" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CH 00 Setup/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 00 Setup/hello.js: -------------------------------------------------------------------------------- 1 | console.log("hello"); -------------------------------------------------------------------------------- /CH 00 Setup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^7.6.0", 18 | "eslint-plugin-functional": "^3.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CH 00 Setup/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node hello.js -------------------------------------------------------------------------------- /CH 02 Immutable Data/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 02 Immutable Data/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CH 02 Immutable Data/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 02 Immutable Data/mutation.js: -------------------------------------------------------------------------------- 1 | const counter = { 2 | value: 1 3 | }; 4 | 5 | counter.value = 2; 6 | delete counter.value; 7 | Object.assign(counter, { value: 2 }); 8 | 9 | //Modifying properties of existing object not allowed. -------------------------------------------------------------------------------- /CH 02 Immutable Data/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^7.1.0", 18 | "eslint-plugin-functional": "^3.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CH 02 Immutable Data/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node object.js -------------------------------------------------------------------------------- /CH 03 Functions/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 03 Functions/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "globals": { 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 11, 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "functional/no-this-expression": "error", 15 | "functional/immutable-data": "error" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CH 03 Functions/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 03 Functions/arrow.js: -------------------------------------------------------------------------------- 1 | const sum = (x, y) => x + y; 2 | 3 | console.log(sum(1,2)); -------------------------------------------------------------------------------- /CH 03 Functions/functions.js: -------------------------------------------------------------------------------- 1 | console.log(sum(1,2)); 2 | 3 | function sum(x, y){ 4 | return x + y; 5 | } -------------------------------------------------------------------------------- /CH 03 Functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^7.1.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CH 03 Functions/pure.js: -------------------------------------------------------------------------------- 1 | function sum(x, y){ 2 | return x + y; 3 | } 4 | 5 | function sumAll(arr){ 6 | return arr.reduce(sum, 0); 7 | } 8 | 9 | console.log(sumAll([1, 2, 3])); 10 | //6 -------------------------------------------------------------------------------- /CH 03 Functions/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node functions.js -------------------------------------------------------------------------------- /CH 04 Array/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 04 Array/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 04 Array/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 04 Array/changing.js: -------------------------------------------------------------------------------- 1 | const games = [ 2 | {id: 1, title: 'WarCraft'}, 3 | {id: 2, title: 'X-COM: UFO Defense' } 4 | ] 5 | 6 | //adding 7 | const newValue = {id: 3, title: 'The Settlers'}; 8 | const newGames = games.concat([newValue]); 9 | console.log(newGames); 10 | //[ { id: 1, title: 'WarCraft' }, 11 | // { id: 2, title: 'X-COM: UFO Defense' }, 12 | // { id: 3, title: 'The Settlers' } ] 13 | 14 | //editing 15 | const id = 1; 16 | const newValue2 = {id, title: 'WarCraft 2'}; 17 | 18 | const newGames2 = games.map(game => 19 | (game.id === id) 20 | ? newValue2 21 | : game 22 | ); 23 | console.log(newGames2); 24 | //[ { id: 1, title: 'WarCraft 2' }, 25 | // { id: 2, title: 'X-COM: UFO Defense' } ] 26 | 27 | //deleting 28 | const newGames3 = games.filter(game => game.id !== id); 29 | console.log(newGames3); 30 | //[ { id: 2, title: 'X-COM: UFO Defense' } ] -------------------------------------------------------------------------------- /CH 04 Array/filter-objects.js: -------------------------------------------------------------------------------- 1 | const games = [ 2 | { title: 'Starcraft', genre: 'RTS' }, 3 | { title: 'Command and Conquer', genre: 'RTS' }, 4 | { title: 'Heroes of Might and Magic', genre: 'TBS' }, 5 | { title: 'World of Warcraft', genre : 'MMORPG'} 6 | ] 7 | 8 | function isStrategy(game){ 9 | const strategyGenres = ['RTS', 'RTT', 'TBS', 'TBT']; 10 | return strategyGenres.includes(game.genre); 11 | } 12 | 13 | const strategyGames = games 14 | .filter(isStrategy); 15 | 16 | console.log(strategyGames); 17 | //[ { title: 'Starcraft', genre: 'RTS' }, 18 | // { title: 'Command and Conquer', genre: 'RTS' }, 19 | // { title: 'Heroes of Might and Magic', genre: 'TBS' } ] -------------------------------------------------------------------------------- /CH 04 Array/filter.js: -------------------------------------------------------------------------------- 1 | const numbers = [1, 2, 3, 4]; 2 | 3 | function isEven(n) { 4 | return n % 2 == 0; 5 | } 6 | 7 | const evenNumbers = numbers.filter(isEven); 8 | console.log(evenNumbers); -------------------------------------------------------------------------------- /CH 04 Array/find.js: -------------------------------------------------------------------------------- 1 | const games = [ 2 | { title: 'Starcraft 2', genre: 'RTS' }, 3 | { title: 'Desperados 3', genre : 'RTT'}, 4 | { title: 'Candy Crush Saga ', genre : 'Tile-matching'} 5 | ]; 6 | 7 | function isStrategy(game){ 8 | const strategyGenres = ['RTS', 'RTT', 'TBS', 'TBT']; 9 | return strategyGenres.includes(game.genre); 10 | } 11 | 12 | const first = games.find(isStrategy); 13 | console.log(first); 14 | //{ title: 'Starcraft 2', genre: 'RTS' } 15 | 16 | const firstIndex = games.findIndex(isStrategy); 17 | console.log(firstIndex); 18 | //0 19 | 20 | const areAll = games.every(isStrategy); 21 | console.log(areAll); 22 | //false 23 | 24 | const hasOne = games.some(isStrategy); 25 | console.log(hasOne); 26 | //true -------------------------------------------------------------------------------- /CH 04 Array/forEach.js: -------------------------------------------------------------------------------- 1 | const numbers = [1, 2, 3, 4] 2 | 3 | function log(value){ 4 | console.log(value); 5 | } 6 | 7 | numbers 8 | .forEach(log); 9 | -------------------------------------------------------------------------------- /CH 04 Array/impure.js: -------------------------------------------------------------------------------- 1 | const arr = [1, 2, 3]; 2 | 3 | arr.push(4); 4 | arr.pop(); 5 | 6 | arr.unshift(0); 7 | arr.shift(); 8 | 9 | //Modifying an array is not allowed 10 | -------------------------------------------------------------------------------- /CH 04 Array/map-objects.js: -------------------------------------------------------------------------------- 1 | const games = [ 2 | { title: 'Starcraft', genre: 'RTS' }, 3 | { title: 'Command and Conquer', genre: 'RTS' }, 4 | { title: 'Heroes of Might and Magic', genre: 'TBS' }, 5 | { title: 'World of Warcraft', genre : 'MMORPG'} 6 | ] 7 | 8 | function toGameHtml(game){ 9 | return `
${game.title}
`; 10 | } 11 | 12 | const htmlRows = games 13 | .map(toGameHtml); 14 | 15 | console.log(htmlRows); 16 | // [ '
Starcraft
', 17 | // '
Command and Conquer
', 18 | // '
Heroes of Might and Magic
', 19 | // '
World of Warcraft
' ] 20 | 21 | console.log(htmlRows.join('')); 22 | //"
Starcraft
Command and Conquer
Heroes of Might and Magic
World of Warcraft
" 23 | -------------------------------------------------------------------------------- /CH 04 Array/map.js: -------------------------------------------------------------------------------- 1 | const numbers = [1, 2, 3, 4]; 2 | 3 | function triple(n) { 4 | return n * 3; 5 | } 6 | 7 | const newNumbers = numbers.map(triple); 8 | console.log(newNumbers); -------------------------------------------------------------------------------- /CH 04 Array/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^7.1.0", 18 | "eslint-plugin-functional": "^3.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CH 04 Array/push.js: -------------------------------------------------------------------------------- 1 | const arr = [1, 2, 3]; 2 | newArr.push(4); 3 | 4 | console.log(newArr); -------------------------------------------------------------------------------- /CH 04 Array/reduce-objects.js: -------------------------------------------------------------------------------- 1 | const games = [ 2 | { title: 'Starcraft', genre: 'RTS' }, 3 | { title: 'Command and Conquer', genre: 'RTS' }, 4 | { title: 'Heroes of Might and Magic', genre: 'TBS' }, 5 | { title: 'World of Warcraft', genre : 'MMORPG'} 6 | ]; 7 | 8 | function countByGenre(countMap, game){ 9 | const count = countMap[game.genre] || 0; 10 | return { 11 | ...countMap, 12 | [game.genre]: count + 1 13 | } 14 | } 15 | 16 | const gamesByGenreCounts = games 17 | .reduce(countByGenre, {}); 18 | 19 | console.log(gamesByGenreCounts); 20 | //{ RTS: 2, TBS: 1, MMORPG: 1 } 21 | -------------------------------------------------------------------------------- /CH 04 Array/reduce.js: -------------------------------------------------------------------------------- 1 | const numbers = [1, 3, 5, 7]; 2 | 3 | function add(total, n) { 4 | return total + n; 5 | } 6 | 7 | const total = numbers.reduce(add, 0); 8 | console.log(total); -------------------------------------------------------------------------------- /CH 04 Array/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node ma-objects.js -------------------------------------------------------------------------------- /CH 04 Array/sort.js: -------------------------------------------------------------------------------- 1 | const numbers = [4, 2, 3, 1]; 2 | 3 | function asc(a, b) { 4 | if(a === b){ 5 | return 0 6 | } else { 7 | if (a < b){ 8 | return -1; 9 | } else { 10 | return 1; 11 | } 12 | } 13 | } 14 | 15 | const sortedNumbers = numbers.slice().sort(asc); 16 | console.log(sortedNumbers); -------------------------------------------------------------------------------- /CH 05 First-Class Functions/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 05 First-Class Functions/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "globals": { 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 11, 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "functional/no-this-expression": "error", 15 | "functional/immutable-data": "error" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CH 05 First-Class Functions/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 05 First-Class Functions/array-of-functions.js: -------------------------------------------------------------------------------- 1 | function toUpperCase(text){ 2 | return text.toUpperCase(); 3 | } 4 | 5 | function toLowerCase(text){ 6 | return text.toLowerCase(); 7 | } 8 | 9 | function trim(text){ 10 | return text.trim(); 11 | } 12 | 13 | const functions = [ 14 | toUpperCase, 15 | toLowerCase, 16 | trim 17 | ]; 18 | 19 | const text = ' StARt!'; 20 | functions.forEach(f =>{ 21 | const newText = f(text); 22 | console.log(newText); 23 | }); 24 | //"START!" 25 | //"start!" 26 | //"StARt!" -------------------------------------------------------------------------------- /CH 05 First-Class Functions/counter.js: -------------------------------------------------------------------------------- 1 | function createCount(){ 2 | let counter = 0; 3 | return function (){ 4 | counter = counter + 1; 5 | return counter; 6 | } 7 | } 8 | 9 | const count = createCount(); 10 | console.log(count()); 11 | console.log(count()); 12 | console.log(count()); -------------------------------------------------------------------------------- /CH 05 First-Class Functions/higher-order-sort.js: -------------------------------------------------------------------------------- 1 | const courses = [ 2 | { 3 | title: '2', 4 | author: '2' 5 | }, 6 | { 7 | title: '3', 8 | author: '3' 9 | }, 10 | { 11 | title: '1', 12 | author: '1' 13 | } 14 | ]; 15 | 16 | function by(name){ 17 | return function(a, b){ 18 | return a[name].localeCompare(b[name]); 19 | } 20 | } 21 | 22 | courses 23 | .slice().sort(by('author')); -------------------------------------------------------------------------------- /CH 05 First-Class Functions/higher-order.js: -------------------------------------------------------------------------------- 1 | const objects = [ 2 | { id: 1 }, 3 | { id: 2 } 4 | ]; 5 | 6 | function hasId(id){ 7 | return function(element){ 8 | return element.id === id; 9 | } 10 | } 11 | 12 | console.log(objects.find(hasId(1))); -------------------------------------------------------------------------------- /CH 05 First-Class Functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^7.1.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CH 05 First-Class Functions/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node functions.js -------------------------------------------------------------------------------- /CH 05 First-Class Functions/run.js: -------------------------------------------------------------------------------- 1 | function run(){ 2 | const value = 1; 3 | 4 | function logValue(){ 5 | console.log(value); 6 | } 7 | 8 | logValue(); 9 | } 10 | 11 | run(); -------------------------------------------------------------------------------- /CH 06 Statements/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 06 Statements/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 06 Statements/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 06 Statements/break-every.js: -------------------------------------------------------------------------------- 1 | const words = [ 2 | 'ability', 3 | 'calculate', 4 | 'calendar', 5 | 'double', 6 | 'door' 7 | ]; 8 | 9 | const stopTo = 'double'; 10 | for(let word of words){ 11 | if(word === stopTo){ 12 | break; 13 | } 14 | 15 | console.log(word); 16 | } 17 | 18 | words.every(w => { 19 | if(w.startsWith(stopTo)){ 20 | return false; 21 | } else { 22 | console.log(w); 23 | return true; 24 | } 25 | }); -------------------------------------------------------------------------------- /CH 06 Statements/break-find.js: -------------------------------------------------------------------------------- 1 | const products = [ 2 | {id:1, name: 'apple'}, 3 | {id:2, name:'mango'} 4 | ]; 5 | 6 | const id = 1; 7 | let product; 8 | for(let i=0; i p.id === id); 19 | console.log(product1) -------------------------------------------------------------------------------- /CH 06 Statements/for.js: -------------------------------------------------------------------------------- 1 | const book = { 2 | title : "How JavaScript Works", 3 | author : "Douglas Crockford" 4 | }; 5 | 6 | for (const propName in book) { 7 | console.log(book[propName]) 8 | } 9 | 10 | Object.keys(book).forEach(propName => { 11 | console.log(book[propName]) 12 | }); 13 | 14 | const arr = [1, 2, 3]; 15 | for (const element of arr) { 16 | console.log(element); 17 | } 18 | 19 | arr.forEach(element => { 20 | console.log(element); 21 | }); -------------------------------------------------------------------------------- /CH 06 Statements/if-return.js: -------------------------------------------------------------------------------- 1 | function isEven(n){ 2 | if(n % 2 === 0){ 3 | return true; 4 | } else { 5 | return false; 6 | } 7 | } 8 | 9 | console.log(isEven(3)); -------------------------------------------------------------------------------- /CH 06 Statements/if-side-effects.js: -------------------------------------------------------------------------------- 1 | const isValid = true; 2 | 3 | if(isValid) { 4 | doSomething() 5 | } 6 | 7 | // short circuit 8 | isValid && doSomething() 9 | 10 | function doSomething(){ 11 | console.log("side-effect"); 12 | } -------------------------------------------------------------------------------- /CH 06 Statements/if.js: -------------------------------------------------------------------------------- 1 | const n = 3; 2 | let isEven; 3 | 4 | if(n % 2 === 0){ 5 | isEven = true; 6 | } else { 7 | isEven = false; 8 | } 9 | 10 | console.log(isEven) 11 | 12 | -------------------------------------------------------------------------------- /CH 06 Statements/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2", 15 | "ramda": "^0.27.0" 16 | }, 17 | "devDependencies": { 18 | "eslint": "^7.1.0", 19 | "eslint-plugin-functional": "^3.0.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CH 06 Statements/run.bat: -------------------------------------------------------------------------------- 1 | npx eslint . 2 | npx babel-node if.js -------------------------------------------------------------------------------- /CH 06 Statements/switch-cond.js: -------------------------------------------------------------------------------- 1 | import {cond, equals, identity, T} from 'ramda' 2 | 3 | function increment(x){ 4 | return x + 1; 5 | } 6 | 7 | function decrement(x){ 8 | return x - 1; 9 | } 10 | 11 | const doAction = cond([ 12 | [equals('increment'), () => increment], 13 | [equals('decrement'), () => decrement], 14 | [T, ()=> identity] 15 | ]); 16 | 17 | const number = 0; 18 | const newNumber = doAction('increment')(number); 19 | console.log(newNumber) -------------------------------------------------------------------------------- /CH 06 Statements/switch-map.js: -------------------------------------------------------------------------------- 1 | function increment(x){ 2 | return x + 1; 3 | } 4 | 5 | function decrement(x){ 6 | return x - 1; 7 | } 8 | 9 | const actionMap = { 10 | increment, 11 | decrement 12 | } 13 | 14 | function doAction(x, actionName){ 15 | const action = actionMap[actionName]; 16 | return action 17 | ? action(x) 18 | : x; 19 | } 20 | 21 | const number = 0; 22 | const newNumber = doAction(number, 'increment'); 23 | console.log(newNumber) -------------------------------------------------------------------------------- /CH 06 Statements/switch-return.js: -------------------------------------------------------------------------------- 1 | function increment(x){ 2 | return x + 1; 3 | } 4 | 5 | function decrement(x){ 6 | return x - 1; 7 | } 8 | 9 | function doAction(x, actionName){ 10 | switch(actionName){ 11 | case 'increment': 12 | return increment(x); 13 | case 'decrement': 14 | return decrement(x); 15 | default: 16 | return x; 17 | } 18 | } 19 | 20 | const number = 0; 21 | const newNumber = doAction(number, 'increment'); 22 | console.log(newNumber) -------------------------------------------------------------------------------- /CH 06 Statements/switch.js: -------------------------------------------------------------------------------- 1 | function increment(x){ 2 | return x + 1; 3 | } 4 | 5 | function decrement(x){ 6 | return x - 1; 7 | } 8 | 9 | function doAction(actionName){ 10 | switch(actionName){ 11 | case "increment": 12 | number = increment(number); 13 | break; 14 | case "decrement": 15 | number = decrement(number); 16 | break; 17 | } 18 | } 19 | 20 | let number = 0; 21 | doAction('increment') 22 | console.log(number) -------------------------------------------------------------------------------- /CH 06 Statements/ternary.js: -------------------------------------------------------------------------------- 1 | const n = 3; 2 | let isEven = (n % 2 === 0) 3 | ? true 4 | : false; 5 | 6 | console.log(isEven); 7 | 8 | function getPrice(product){ 9 | return product 10 | ? product.price 11 | ? product.price 12 | : product.lastPrice 13 | : null 14 | } 15 | 16 | console.log(getPrice(null)); 17 | console.log(getPrice({lastPrice: 10})); 18 | console.log(getPrice({price:5, lastPrice: 10})); -------------------------------------------------------------------------------- /CH 06 Statements/var.js: -------------------------------------------------------------------------------- 1 | var number = 1; 2 | console.log(number); -------------------------------------------------------------------------------- /CH 06 Statements/while.js: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | 3 | // eslint-disable-next-line functional/no-loop-statement 4 | while(i < 10){ 5 | console.log(i); 6 | i = i + 1; 7 | } -------------------------------------------------------------------------------- /CH 07 Recursion/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 07 Recursion/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 07 Recursion/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 07 Recursion/factorial-conditional.js: -------------------------------------------------------------------------------- 1 | function factorial(n) { 2 | return (n === 0) 3 | ? 1 4 | : n * factorial(n - 1); 5 | } 6 | 7 | console.log(factorial(5)); -------------------------------------------------------------------------------- /CH 07 Recursion/factorial-recursive.js: -------------------------------------------------------------------------------- 1 | function factorial(n) { 2 | if (n === 0) { 3 | return 1; 4 | } 5 | else { 6 | return n * factorial(n - 1); 7 | } 8 | } 9 | 10 | console.log(factorial(5)); -------------------------------------------------------------------------------- /CH 07 Recursion/factorial-tc.js: -------------------------------------------------------------------------------- 1 | function factorial(n, result = 1) { 2 | //console.log(`n=${n}, result=${result}`); 3 | 4 | return (n === 0) 5 | ? result 6 | : factorial(n - 1, n * result); 7 | } 8 | 9 | console.log(factorial(5)); -------------------------------------------------------------------------------- /CH 07 Recursion/factorial.js: -------------------------------------------------------------------------------- 1 | function factorial(n) { 2 | let result = 1; 3 | 4 | let i = 2; 5 | while(i <= n){ 6 | result = result * i; 7 | console.log(`i=${i}, result=${result}`); 8 | i = i + 1; 9 | } 10 | 11 | return result; 12 | } 13 | 14 | console.log(factorial(5)); -------------------------------------------------------------------------------- /CH 07 Recursion/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^7.1.0", 18 | "eslint-plugin-functional": "^3.0.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CH 07 Recursion/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node filter.js -------------------------------------------------------------------------------- /CH 07 Recursion/stack-overflow-loop.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristi-salcescu/functional-programming-in-javascript/30775e7a0cbd55323a12553aea568cdfa840f557/CH 07 Recursion/stack-overflow-loop.js -------------------------------------------------------------------------------- /CH 07 Recursion/stack-overflow.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristi-salcescu/functional-programming-in-javascript/30775e7a0cbd55323a12553aea568cdfa840f557/CH 07 Recursion/stack-overflow.js -------------------------------------------------------------------------------- /CH 07 Recursion/sum-loop.js: -------------------------------------------------------------------------------- 1 | function sumAll(n) { 2 | let total = 0; 3 | let i = 1; 4 | while(i <= n){ 5 | total = total + i; 6 | i = i + 1; 7 | } 8 | 9 | return total; 10 | } 11 | 12 | console.log(sumAll(10000)); -------------------------------------------------------------------------------- /CH 07 Recursion/sum-recursive.js: -------------------------------------------------------------------------------- 1 | function sumAll(n, i = 0, result = 0) { 2 | return (i > n) 3 | ? result 4 | : sumAll(n, i + 1, i + result); 5 | } 6 | 7 | console.log(sumAll(10000)); -------------------------------------------------------------------------------- /CH 07 Recursion/trampoline.js: -------------------------------------------------------------------------------- 1 | function sumAll(n, i = 0, result = 0) { 2 | return (i > n) 3 | ? () => result 4 | : () => sumAll(n, i + 1, i + result); 5 | } 6 | 7 | const _sumAll = trampoline(sumAll) 8 | 9 | function trampoline(f) { 10 | return function(...args){ 11 | let result = f(...args); 12 | while (typeof(result) === 'function'){ 13 | result = result(); 14 | } 15 | 16 | return result; 17 | } 18 | } 19 | 20 | 21 | console.log(_sumAll(10000)); -------------------------------------------------------------------------------- /CH 07 Recursion/tree.js: -------------------------------------------------------------------------------- 1 | const tree = { 2 | value: 0, 3 | checked: false, 4 | children: [{ 5 | value: 1, 6 | checked: true, 7 | children: [{ 8 | value: 11, 9 | checked: true, 10 | children: null 11 | }] 12 | }, { 13 | value: 2, 14 | checked: false, 15 | children: [{ 16 | value: 22, 17 | checked: true, 18 | children: null 19 | },{ 20 | value: 23, 21 | checked: true, 22 | children: null 23 | }] 24 | }] 25 | } 26 | 27 | function toChildTopSelection(selection, childNode){ 28 | return selection.concat(getTopSelection(childNode)); 29 | } 30 | 31 | function getTopSelection(node){ 32 | return node.checked 33 | ? [node.value] 34 | : node.children !== null 35 | ? node.children.reduce(toChildTopSelection, []) 36 | : [] 37 | } 38 | 39 | console.log(getTopSelection(tree)); -------------------------------------------------------------------------------- /CH 08 Pipelines/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 08 Pipelines/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 08 Pipelines/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 08 Pipelines/chaining-2.js: -------------------------------------------------------------------------------- 1 | const debts = [ 2 | { 3 | contractNo : 1, 4 | daysSinceLastPayment: 91, 5 | currency : 'EUR' 6 | }, 7 | { 8 | contractNo : 2, 9 | daysSinceLastPayment: 35, 10 | currency : 'USD' 11 | }, 12 | { 13 | contractNo : 3, 14 | daysSinceLastPayment: 45, 15 | currency : 'USD' 16 | } 17 | ] 18 | 19 | function getPriority(debt){ 20 | const days = debt.daysSinceLastPayment; 21 | return days > 90 22 | ? 1 23 | : days > 60 24 | ? 2 25 | : 3 26 | } 27 | 28 | function toDebtView(debt){ 29 | return { 30 | ...debt, 31 | priority : getPriority(debt) 32 | } 33 | } 34 | 35 | function isCurrency(currency){ 36 | return function (debt){ 37 | return debt.currency === currency; 38 | } 39 | } 40 | 41 | function logIdentity(value){ 42 | console.log(value); 43 | return value; 44 | } 45 | 46 | debts 47 | .filter(isCurrency('EUR')) 48 | .map(toDebtView) 49 | .map(logIdentity) -------------------------------------------------------------------------------- /CH 08 Pipelines/chaining-3.js: -------------------------------------------------------------------------------- 1 | const debts = [ 2 | { 3 | contractNo : 1, 4 | daysSinceLastPayment: 91, 5 | currency : 'EUR' 6 | }, 7 | { 8 | contractNo : 2, 9 | daysSinceLastPayment: 35, 10 | currency : 'USD' 11 | }, 12 | { 13 | contractNo : 3, 14 | daysSinceLastPayment: 45, 15 | currency : 'USD' 16 | } 17 | ] 18 | 19 | function getPriority(debt){ 20 | const days = debt.daysSinceLastPayment; 21 | return days > 90 22 | ? 1 23 | : days > 60 24 | ? 2 25 | : 3 26 | } 27 | 28 | function toDebtView(debt){ 29 | return { 30 | ...debt, 31 | priority : getPriority(debt) 32 | } 33 | } 34 | 35 | function isCurrency(currencies){ 36 | return function (debt){ 37 | return currencies.includes(debt.currency); 38 | } 39 | } 40 | 41 | function logIdentity(value){ 42 | console.log(value); 43 | return value; 44 | } 45 | 46 | debts 47 | .filter(isCurrency(['EUR', 'USD'])) 48 | .map(toDebtView) 49 | .map(logIdentity) -------------------------------------------------------------------------------- /CH 08 Pipelines/chaining.js: -------------------------------------------------------------------------------- 1 | const debts = [ 2 | { 3 | contractNo : 1, 4 | daysSinceLastPayment: 91, 5 | currency : 'EUR' 6 | }, 7 | { 8 | contractNo : 2, 9 | daysSinceLastPayment: 35, 10 | currency : 'USD' 11 | }, 12 | { 13 | contractNo : 3, 14 | daysSinceLastPayment: 45, 15 | currency : 'USD' 16 | } 17 | ] 18 | 19 | function getPriority(debt){ 20 | const days = debt.daysSinceLastPayment; 21 | return days > 90 22 | ? 1 23 | : days > 60 24 | ? 2 25 | : 3 26 | } 27 | 28 | function toDebtView(debt){ 29 | return { 30 | ...debt, 31 | priority : getPriority(debt) 32 | } 33 | } 34 | 35 | function isEur(debt){ 36 | return debt.currency === "EUR"; 37 | } 38 | 39 | function logIdentity(value){ 40 | console.log(value); 41 | return value; 42 | } 43 | 44 | debts 45 | .filter(isEur) 46 | .map(toDebtView) 47 | .map(logIdentity) -------------------------------------------------------------------------------- /CH 08 Pipelines/currying-1.js: -------------------------------------------------------------------------------- 1 | const words = [ 2 | 'ability', 3 | 'calculate', 4 | 'calendar', 5 | 'double', 6 | 'door' 7 | ]; 8 | 9 | function startsWith(word, term){ 10 | return word.startsWith(term); 11 | } 12 | 13 | function trace(value){ 14 | console.log(value); 15 | return value; 16 | } 17 | 18 | words 19 | .filter(w => startsWith(w, 'a')) 20 | .forEach(trace); -------------------------------------------------------------------------------- /CH 08 Pipelines/currying-2.js: -------------------------------------------------------------------------------- 1 | const words = [ 2 | 'ability', 3 | 'calculate', 4 | 'calendar', 5 | 'double', 6 | 'door' 7 | ]; 8 | 9 | function startsWith(term){ 10 | return function(word){ 11 | return word.startsWith(term); 12 | } 13 | } 14 | 15 | function trace(value){ 16 | console.log(value); 17 | return value; 18 | } 19 | 20 | words 21 | .filter(startsWith('a')) 22 | .forEach(trace); -------------------------------------------------------------------------------- /CH 08 Pipelines/currying-3.js: -------------------------------------------------------------------------------- 1 | const words = [ 2 | 'ability', 3 | 'calculate', 4 | 'calendar', 5 | 'double', 6 | 'door' 7 | ]; 8 | 9 | const startsWith = term => word => { 10 | return word.startsWith(term); 11 | } 12 | 13 | function trace(value){ 14 | console.log(value); 15 | return value; 16 | } 17 | 18 | words 19 | .filter(startsWith('a')) 20 | .forEach(trace); -------------------------------------------------------------------------------- /CH 08 Pipelines/currying-4.js: -------------------------------------------------------------------------------- 1 | import { curry } from 'lodash/fp'; 2 | 3 | const words = [ 4 | 'ability', 5 | 'calculate', 6 | 'calendar', 7 | 'double', 8 | 'door' 9 | ]; 10 | 11 | const startsWith = curry(function(term, word) { 12 | return word.startsWith(term); 13 | }); 14 | 15 | function trace(value){ 16 | console.log(value); 17 | return value; 18 | } 19 | 20 | words 21 | .filter(startsWith('a')) 22 | .forEach(trace); -------------------------------------------------------------------------------- /CH 08 Pipelines/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2", 15 | "lodash": "^4.17.15" 16 | }, 17 | "devDependencies": { 18 | "eslint": "^7.1.0", 19 | "eslint-plugin-functional": "^3.0.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CH 08 Pipelines/pipe-weather.js: -------------------------------------------------------------------------------- 1 | import { pipe } from 'lodash/fp'; 2 | 3 | function toCelsius(kelvin){ 4 | return 300 - kelvin; 5 | } 6 | 7 | function describeTemperature(temperature){ 8 | return temperature < 0 9 | ? "Freezing" 10 | : temperature < 15 11 | ? "Cold" 12 | : temperature < 28 13 | ? "Warm" 14 | : "Hot"; 15 | } 16 | 17 | const temperatureDescription = pipe( 18 | toCelsius, 19 | describeTemperature 20 | )(273); 21 | 22 | console.log(temperatureDescription); -------------------------------------------------------------------------------- /CH 08 Pipelines/pipe.js: -------------------------------------------------------------------------------- 1 | import { pipe } from 'lodash/fp'; 2 | 3 | function capitalize(text) { 4 | return text.charAt(0).toUpperCase() + text.slice(1); 5 | } 6 | 7 | function shortenText(text) { 8 | return text.substring(0, 8).trim(); 9 | } 10 | 11 | const shortText = shortenText(capitalize("this is a long text")); 12 | console.log(shortText); 13 | //"This is" 14 | 15 | const shortText1 = pipe( 16 | capitalize, 17 | shortenText 18 | )("this is a long text"); 19 | console.log(shortText1); 20 | //"This is" -------------------------------------------------------------------------------- /CH 08 Pipelines/reduce.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cristi-salcescu/functional-programming-in-javascript/30775e7a0cbd55323a12553aea568cdfa840f557/CH 08 Pipelines/reduce.js -------------------------------------------------------------------------------- /CH 08 Pipelines/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node pipe.js -------------------------------------------------------------------------------- /CH 09 Functors/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 09 Functors/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "globals": { 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 11, 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "functional/no-this-expression": "error", 15 | "functional/immutable-data": "error", 16 | "no-var": "error", 17 | "functional/no-conditional-statement": ["error", { 18 | "allowReturningBranches": "ifExhaustive" 19 | }], 20 | "functional/no-loop-statement": "error", 21 | "functional/no-throw-statement": "error" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CH 09 Functors/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 09 Functors/F.js: -------------------------------------------------------------------------------- 1 | function F(value){ 2 | 3 | function map(f){ 4 | const newValue = f(value); 5 | return F(newValue); 6 | } 7 | 8 | return { 9 | map 10 | } 11 | } 12 | 13 | export default F; 14 | -------------------------------------------------------------------------------- /CH 09 Functors/array.js: -------------------------------------------------------------------------------- 1 | function increment(n){ 2 | return n + 1; 3 | } 4 | 5 | function double(n){ 6 | return n * 2 7 | } 8 | 9 | function identity(n){ 10 | return n; 11 | } 12 | 13 | function logIdentity(value){ 14 | console.log(value); 15 | return value; 16 | } 17 | 18 | [1, 2, 3] 19 | .map(identity) 20 | .map(logIdentity); 21 | //1 22 | //2 23 | //3 24 | 25 | [1, 2, 3] 26 | .map(increment) 27 | .map(double) 28 | .map(logIdentity); 29 | //4 30 | //6 31 | //8 32 | 33 | [1, 2, 3] 34 | .map(x => double(increment(x))) 35 | .map(logIdentity); 36 | //4 37 | //6 38 | //8 -------------------------------------------------------------------------------- /CH 09 Functors/array_weather.js: -------------------------------------------------------------------------------- 1 | import { compose } from 'lodash/fp' 2 | 3 | function toCelsius(kelvin){ 4 | const celsius = (kelvin - 273.15); 5 | return Math.round(celsius * 100) / 100; 6 | } 7 | 8 | function describeTemperature(temperature){ 9 | return temperature < 0 10 | ? "Freezing" 11 | : temperature < 15 12 | ? "Cold" 13 | : temperature < 28 14 | ? "Warm" 15 | : "Hot"; 16 | } 17 | 18 | function logIdentity(value){ 19 | console.log(value); 20 | return value; 21 | } 22 | 23 | [300] 24 | .map(toCelsius) 25 | .map(describeTemperature) 26 | .map(logIdentity); 27 | 28 | [300] 29 | .map(x => describeTemperature(toCelsius(x))) 30 | .map(logIdentity); 31 | 32 | [300] 33 | .map(compose(describeTemperature, toCelsius)) 34 | .map(logIdentity); -------------------------------------------------------------------------------- /CH 09 Functors/mapping.js: -------------------------------------------------------------------------------- 1 | function toCelsius(kelvin){ 2 | const celsius = (kelvin - 273.15); 3 | return Math.round(celsius * 100) / 100; 4 | } 5 | 6 | function describeTemperature(temperature){ 7 | return temperature < 0 8 | ? "Freezing" 9 | : temperature < 15 10 | ? "Cold" 11 | : temperature < 28 12 | ? "Warm" 13 | : "Hot"; 14 | } 15 | 16 | const kelvin = 300; 17 | const celsius = toCelsius(kelvin); 18 | const description = describeTemperature(celsius); 19 | 20 | console.log(celsius); 21 | //26.85 22 | console.log(description) 23 | //"Warm" -------------------------------------------------------------------------------- /CH 09 Functors/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2", 15 | "lodash": "^4.17.15" 16 | }, 17 | "devDependencies": { 18 | "eslint": "^7.1.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CH 09 Functors/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node F.js -------------------------------------------------------------------------------- /CH 09 Functors/test_F.js: -------------------------------------------------------------------------------- 1 | import F from './F'; 2 | 3 | function increment(n){ 4 | return n + 1; 5 | } 6 | 7 | function double(n){ 8 | return n * 2 9 | } 10 | 11 | function identity(n){ 12 | return n; 13 | } 14 | 15 | function logIdentity(value){ 16 | console.log(value); 17 | return value; 18 | } 19 | 20 | //identity laws 21 | F(1).map(identity).map(logIdentity); 22 | 23 | //composition laws 24 | F(1).map(increment).map(double).map(logIdentity); 25 | F(1).map(n => double(increment(n))).map(logIdentity); 26 | 27 | //chain 28 | F(1) 29 | .map(increment) 30 | .map(increment) 31 | .map(double) 32 | .map(logIdentity) -------------------------------------------------------------------------------- /CH 09 Functors/test_F_weather.js: -------------------------------------------------------------------------------- 1 | import F from './F'; 2 | import { compose } from 'lodash/fp' 3 | 4 | function toCelsius(kelvin){ 5 | const celsius = (kelvin - 273.15); 6 | return Math.round(celsius * 100) / 100; 7 | } 8 | 9 | function describeTemperature(temperature){ 10 | return temperature < 0 11 | ? "Freezing" 12 | : temperature < 15 13 | ? "Cold" 14 | : temperature < 28 15 | ? "Warm" 16 | : "Hot"; 17 | } 18 | 19 | function logIdentity(value){ 20 | console.log(value); 21 | return value; 22 | } 23 | 24 | F(300) 25 | .map(toCelsius) 26 | .map(describeTemperature) 27 | .map(logIdentity) 28 | 29 | F(300) 30 | .map(x => describeTemperature(toCelsius(x))) 31 | .map(logIdentity) 32 | 33 | F(300) 34 | .map(compose(describeTemperature, toCelsius)) 35 | .map(logIdentity) -------------------------------------------------------------------------------- /CH 10 Monads/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 10 Monads/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "globals": { 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 11, 11 | "sourceType": "module" 12 | }, 13 | "rules": { 14 | "functional/no-this-expression": "error", 15 | "functional/immutable-data": "error", 16 | "no-var": "error", 17 | "functional/no-conditional-statement": ["error", { 18 | "allowReturningBranches": "ifExhaustive" 19 | }], 20 | "functional/no-loop-statement": "error", 21 | "functional/no-throw-statement": "error" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CH 10 Monads/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 10 Monads/Applicative.js: -------------------------------------------------------------------------------- 1 | function M(value){ 2 | 3 | function map(f){ 4 | const newValue = f(value); 5 | return M(newValue); 6 | } 7 | 8 | function ap(otherMonad){ 9 | const f = value;//The value must be a function 10 | const newValue = otherMonad.map(f); 11 | return newValue; 12 | } 13 | 14 | return { 15 | map, 16 | ap 17 | } 18 | } 19 | 20 | function logIdentity(n){ 21 | console.log(n); 22 | return n; 23 | } 24 | 25 | 26 | function add(x){ 27 | return function(y){ 28 | return x + y; 29 | } 30 | } 31 | 32 | M(1) 33 | .map(add) //The value is now a function 34 | .ap(M(2)) 35 | .map(logIdentity) 36 | 37 | M(2) 38 | .map(add) //The value is now a function 39 | .ap(M(1)) 40 | .map(logIdentity) 41 | 42 | function multiply(x){ 43 | return function(y){ 44 | return function(z){ 45 | return x * y * z; 46 | } 47 | } 48 | } 49 | 50 | M(2) 51 | .map(multiply) //The value is now a function 52 | .ap(M(3)) //The value is still a function 53 | .ap(M(4)) 54 | .map(logIdentity) -------------------------------------------------------------------------------- /CH 10 Monads/M-bind.js: -------------------------------------------------------------------------------- 1 | export default (function defineMonad(){ 2 | function M(value){ 3 | function map(f){ 4 | const newValue = f(value); 5 | return M(newValue); 6 | } 7 | 8 | function bind(f){ 9 | const newValue = f(value); 10 | return newValue; 11 | } 12 | 13 | return { 14 | map, 15 | bind 16 | } 17 | } 18 | 19 | function unit(value){ 20 | return M(value); 21 | } 22 | 23 | return { 24 | unit 25 | } 26 | })(); -------------------------------------------------------------------------------- /CH 10 Monads/M.js: -------------------------------------------------------------------------------- 1 | function M(value){ 2 | 3 | function map(f){ 4 | const newValue = f(value); 5 | return M(newValue); 6 | } 7 | 8 | function flatMap(f){ 9 | const newValue = f(value); 10 | return newValue; 11 | } 12 | 13 | return { 14 | map, 15 | flatMap 16 | } 17 | } 18 | 19 | 20 | export default M; -------------------------------------------------------------------------------- /CH 10 Monads/Maybe.js: -------------------------------------------------------------------------------- 1 | function Maybe(value){ 2 | 3 | function map(f){ 4 | if (!value) { 5 | return Maybe(null); 6 | } 7 | 8 | const newValue = f(value); 9 | return Maybe(newValue); 10 | } 11 | 12 | function flatMap(f){ 13 | const newValue = f(value); 14 | return newValue; 15 | } 16 | 17 | return { 18 | map, 19 | flatMap 20 | } 21 | } 22 | 23 | 24 | function prop(name){ 25 | return function(obj){ 26 | return obj[name]; 27 | } 28 | } 29 | 30 | const book = { 31 | name: "Mastering JavaScript Functional Programming", 32 | author: { 33 | name: "Federico Kereki" 34 | } 35 | } 36 | 37 | const author = prop("author")(book); 38 | const name = prop("name")(author); 39 | console.log(name); 40 | //"Federico Kereki" 41 | 42 | Maybe(book) 43 | .map(prop("author")) 44 | .map(prop("name")) 45 | .flatMap(console.log) 46 | 47 | const book2 = { 48 | name: "JavaScript: The Good Parts", 49 | author: null 50 | } 51 | 52 | Maybe(book2) 53 | .map(prop("author")) 54 | .map(prop("name")) 55 | .flatMap(console.log) 56 | -------------------------------------------------------------------------------- /CH 10 Monads/flat.js: -------------------------------------------------------------------------------- 1 | const arr = [1, [2, 3]]; 2 | 3 | console.log(arr); 4 | console.log(arr.flat()); 5 | //[ 1, 2, 3 ] 6 | 7 | function split(text){ 8 | return text.split(' '); 9 | } 10 | 11 | const newArray = ["raspberry strawberry", "blueberry"] 12 | .map(split) 13 | .flat(); 14 | 15 | console.log(newArray); 16 | //[ 'raspberry', 'strawberry', 'blueberry' ] -------------------------------------------------------------------------------- /CH 10 Monads/flatMap.js: -------------------------------------------------------------------------------- 1 | function split(text){ 2 | return text.split(' '); 3 | } 4 | 5 | const arr2 = ["raspberry strawberry", "blueberry"] 6 | .flatMap(split) 7 | 8 | console.log(arr2); 9 | //[ 'raspberry', 'strawberry', 'blueberry' ] -------------------------------------------------------------------------------- /CH 10 Monads/map.js: -------------------------------------------------------------------------------- 1 | function toUpperCase(text){ 2 | return text.toUpperCase(); 3 | } 4 | 5 | const arr1 = ["raspberry strawberry", "blueberry"] 6 | .map(toUpperCase); 7 | console.log(arr1); 8 | //[ 'RASPBERRY STRAWBERRY', 'BLUEBERRY' ] 9 | 10 | function split(text){ 11 | return text.split(' '); 12 | } 13 | 14 | const arr2 = ["raspberry strawberry", "blueberry"] 15 | .map(split) 16 | 17 | console.log(arr2); 18 | //[ [ 'raspberry', 'strawberry' ], [ 'blueberry' ] ] -------------------------------------------------------------------------------- /CH 10 Monads/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^7.1.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /CH 10 Monads/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node M.js -------------------------------------------------------------------------------- /CH 10 Monads/test_Array_laws.js: -------------------------------------------------------------------------------- 1 | function M(n){ 2 | return [n]; 3 | } 4 | 5 | function duplicate(word){ 6 | return [word, word] 7 | } 8 | 9 | function split(text){ 10 | return text.split(" "); 11 | } 12 | 13 | //Left identity: M(x).flatMap(f) === f(x) 14 | const w = Array.of('mango') 15 | .flatMap(duplicate) 16 | //['mango', 'mango'] 17 | console.log(w) 18 | 19 | console.log(duplicate('mango')); 20 | //['mango', 'mango'] 21 | 22 | //Right identity: m.flatMap(M) === m 23 | const z = M('lemon') 24 | .flatMap(M) 25 | //['lemon'] 26 | console.log(z) 27 | 28 | //Associativity 29 | const y = M('orange kiwi') 30 | .flatMap(split) 31 | .flatMap(duplicate); 32 | //[ 'orange', 'orange', 'kiwi', 'kiwi' ] 33 | 34 | const x = M('orange kiwi') 35 | .flatMap(s => split(s).flatMap(duplicate)) 36 | //[ 'orange', 'orange', 'kiwi', 'kiwi' ] 37 | console.log(x) 38 | console.log(y) 39 | -------------------------------------------------------------------------------- /CH 10 Monads/test_M.js: -------------------------------------------------------------------------------- 1 | import M from './M'; 2 | 3 | function logIdentity(n){ 4 | console.log(n); 5 | return n; 6 | } 7 | 8 | function incrementAsM(n){ 9 | return M(n+1) 10 | } 11 | 12 | M(1) 13 | .flatMap(incrementAsM) 14 | .map(logIdentity) 15 | //M(2) 16 | -------------------------------------------------------------------------------- /CH 10 Monads/test_M_bind.js: -------------------------------------------------------------------------------- 1 | import M from './M-bind'; 2 | 3 | function logIdentity(n){ 4 | console.log(n); 5 | return n; 6 | } 7 | 8 | function f(){ 9 | return M.unit(2) 10 | } 11 | 12 | function g(){ 13 | return M.unit(3) 14 | } 15 | 16 | //1. bind(unit(x), f) ≡ f(x) 17 | M.unit(1) 18 | .bind(f) 19 | .map(logIdentity) 20 | //M 2 21 | 22 | //2. bind(m, unit) ≡ m 23 | 24 | M.unit(1) 25 | .bind(M.unit) 26 | .map(logIdentity) 27 | //M 1 28 | 29 | //3. bind(bind(m, f), g) ≡ bind(m, x ⇒ bind(f(x), g)) 30 | 31 | M.unit(1) 32 | .bind(f) 33 | .bind(g) 34 | .map(logIdentity) 35 | //M 3 36 | 37 | M.unit(1) 38 | .bind(x => f(x).bind(g)) 39 | .map(logIdentity) -------------------------------------------------------------------------------- /CH 10 Monads/test_M_laws.js: -------------------------------------------------------------------------------- 1 | import M from './M'; 2 | 3 | function f(){ 4 | return M(2) 5 | } 6 | 7 | function g(){ 8 | return M(3) 9 | } 10 | 11 | function logIdentity(n){ 12 | console.log(n); 13 | return n; 14 | } 15 | 16 | //Left identity: M(x).flatMap(f) === f(x) 17 | M(1) 18 | .flatMap(f) 19 | .map(logIdentity); 20 | //M(2) 21 | 22 | f(1) 23 | //M(2) 24 | 25 | //Right identity: m.flatMap(M) === m 26 | M(1) 27 | .flatMap(M) 28 | .map(logIdentity); 29 | //M(1) 30 | 31 | //Associativity 32 | M(1) 33 | .flatMap(f) 34 | .flatMap(g) 35 | .map(logIdentity) 36 | //M(3) 37 | 38 | M(1) 39 | .flatMap(n => f(n).flatMap(g)) 40 | .map(logIdentity) 41 | //M(3) -------------------------------------------------------------------------------- /CH 11 Immutable Collections/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 11 Immutable Collections/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 11 Immutable Collections/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 11 Immutable Collections/List-Operations.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const list = List([ 4 | { name: 'Cascais', country: 'Portugal'}, 5 | { name: 'Sintra ', country: 'Portugal' }, 6 | { name: 'Nerja ', country: 'Spain' }, 7 | ]); 8 | 9 | function inCountry(country){ 10 | return function(destination){ 11 | return destination.country === country; 12 | } 13 | } 14 | 15 | list 16 | .filter(inCountry('Portugal')) 17 | //List [{ name: 'Cascais', country: 'Portugal' }, 18 | // { name: 'Sintra ', country: 'Portugal' }] 19 | 20 | const destination = list.find(inCountry('Portugal')); 21 | console.log(destination) 22 | //{ name: 'Cascais', country: 'Portugal' } 23 | 24 | const otherDestination = list.findLast(inCountry('Portugal')); 25 | console.log(otherDestination); 26 | //{ name: 'Sintra ', country: 'Portugal' } -------------------------------------------------------------------------------- /CH 11 Immutable Collections/List.js: -------------------------------------------------------------------------------- 1 | import { List } from 'immutable'; 2 | 3 | const emptyList = List(); 4 | console.log(emptyList); 5 | // List [] 6 | 7 | const list = List([ 8 | { name: 'Rhodes' }, 9 | { name: 'Malaga' } 10 | ]); 11 | console.log(list) 12 | // List [{ name: 'Rhodes' }, { name: 'Malaga' }] 13 | 14 | //Editing 15 | const newList = list.set(0, { name: 'Riomaggiore' }); 16 | console.log(newList) 17 | // List [{ name: 'Riomaggiore' }, { name: 'Malaga' }] 18 | console.log(list === newList); 19 | //false 20 | 21 | //Adding 22 | const newList2 = list.push({name: 'Funchal'}); 23 | console.log(newList2) 24 | // List [{ name: 'Rhodes' }, { name: 'Malaga' }, { name: 'Funchal' }] 25 | 26 | const newList3 = list.concat([{name: 'Funchal'}]); 27 | console.log(newList3) 28 | // List [{ name: 'Rhodes' }, { name: 'Malaga' }, { name: 'Funchal' }] 29 | 30 | 31 | //Deleting 32 | const newList4 = list.delete(0); 33 | console.log(newList4) 34 | // List [{ name: 'Malaga' }] 35 | 36 | 37 | //Clear 38 | const newList5 = list.clear(); 39 | console.log(newList5) 40 | // List [] -------------------------------------------------------------------------------- /CH 11 Immutable Collections/Map-Operations.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | function logIdentity(x){ 4 | console.log(x); 5 | return x 6 | } 7 | 8 | Map({ a: 1, b: 2 }) 9 | .map(x => 10 * x) 10 | .map(logIdentity) 11 | 12 | const x = Map({ a: 1, b: 2, c:3, d:4 }) 13 | .filter(x => x%2 === 0) 14 | .map(logIdentity) 15 | 16 | console.log(x) -------------------------------------------------------------------------------- /CH 11 Immutable Collections/Map.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'immutable'; 2 | 3 | const countryMap = Map({ 4 | "1": { name: 'Italy'}, 5 | "2": { name: 'Portugal'}, 6 | "3": { name: 'UK'} 7 | }); 8 | console.log(countryMap); 9 | 10 | console.log(countryMap.get("3")); 11 | //{ name: 'UK' } 12 | 13 | const newMap = countryMap.set("2", { name: 'Spain'}); 14 | console.log(newMap.get("2")); 15 | //{ name: 'Spain' } 16 | 17 | const newMap2 = countryMap.delete("3"); 18 | console.log(newMap2.has("3")); 19 | //false 20 | console.log(newMap2.get("3")); 21 | //undefined 22 | -------------------------------------------------------------------------------- /CH 11 Immutable Collections/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2", 15 | "immutable": "^4.0.0-rc.12", 16 | "ramda": "^0.27.0" 17 | }, 18 | "devDependencies": { 19 | "eslint": "^7.1.0", 20 | "eslint-plugin-functional": "^3.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CH 11 Immutable Collections/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node List.js -------------------------------------------------------------------------------- /CH 12 Lazy/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 12 Lazy/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 12 Lazy/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 12 Lazy/Range.js: -------------------------------------------------------------------------------- 1 | import { Range } from 'immutable'; 2 | 3 | function logIdentity(n){ 4 | console.log(n); 5 | return n; 6 | } 7 | 8 | Range(1, 4) 9 | .forEach(logIdentity); 10 | 11 | 12 | -------------------------------------------------------------------------------- /CH 12 Lazy/Seq.js: -------------------------------------------------------------------------------- 1 | import { Seq } from 'immutable'; 2 | 3 | function isEven(n){ 4 | return n % 2 === 0; 5 | } 6 | 7 | function double(n){ 8 | return n * 2; 9 | } 10 | 11 | function logIdentity(n){ 12 | console.log(n); 13 | return n; 14 | } 15 | 16 | const sequence = Seq([ 1, 2, 3, 4]) 17 | .filter(isEven) 18 | .map(double) 19 | .map(logIdentity); 20 | 21 | console.log(sequence); 22 | 23 | const newArray = sequence.toArray(); 24 | console.log(newArray); -------------------------------------------------------------------------------- /CH 12 Lazy/chain.js: -------------------------------------------------------------------------------- 1 | function isEven(n){ 2 | return n % 2 === 0; 3 | } 4 | 5 | function double(n){ 6 | return n * 2; 7 | } 8 | 9 | const numbers = [1, 2, 3, 4]; 10 | const newNumbers = 11 | numbers 12 | .filter(isEven) 13 | .map(double); 14 | console.log(newNumbers); -------------------------------------------------------------------------------- /CH 12 Lazy/filter.js: -------------------------------------------------------------------------------- 1 | function filter(test) { 2 | return function reducer(arr, value) { 3 | if (test(value)) { 4 | return [...arr, value] 5 | } else { 6 | return arr; 7 | } 8 | } 9 | } 10 | 11 | function isEven(n){ 12 | return n % 2 === 0; 13 | } 14 | 15 | const numbers = [1 ,2 ,3 , 4]; 16 | const newNumbers = numbers.reduce(filter(isEven), []); 17 | console.log(newNumbers); -------------------------------------------------------------------------------- /CH 12 Lazy/find.js: -------------------------------------------------------------------------------- 1 | function find(test) { 2 | return function reducer(foundValue, value) { 3 | if (test(value) && !foundValue) { 4 | return value 5 | } else { 6 | return foundValue; 7 | } 8 | } 9 | } 10 | 11 | function isEven(n){ 12 | return n % 2 === 0; 13 | } 14 | 15 | const numbers = [1, 2, 3, 4]; 16 | const element = numbers.reduce(find(isEven), undefined); 17 | console.log(element); -------------------------------------------------------------------------------- /CH 12 Lazy/map.js: -------------------------------------------------------------------------------- 1 | function map(transform) { 2 | return function reducer(arr, value) { 3 | const newValue = transform(value); 4 | return [...arr, newValue]; 5 | } 6 | } 7 | 8 | function double(n){ 9 | return n * 2; 10 | } 11 | 12 | const numbers = [1, 2, 3]; 13 | const newNumbers = numbers.reduce(map(double), []); 14 | console.log(newNumbers); -------------------------------------------------------------------------------- /CH 12 Lazy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2", 15 | "immutable": "^4.0.0-rc.12", 16 | "lodash": "^4.17.15" 17 | }, 18 | "devDependencies": { 19 | "eslint": "^7.1.0", 20 | "eslint-plugin-functional": "^3.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CH 12 Lazy/prime-2.js: -------------------------------------------------------------------------------- 1 | import { Range } from 'immutable'; 2 | 3 | Range(0, 10) 4 | .filter(isPrime) 5 | .forEach(log); 6 | 7 | function log(number) { 8 | console.log(number); 9 | } 10 | 11 | function isPrime(number){ 12 | if (number <= 1){ 13 | return false; 14 | } 15 | 16 | for (let i=2; i<=number/2; i++) 17 | { 18 | if (number % i === 0 ) 19 | return false; 20 | } 21 | 22 | return true; 23 | } -------------------------------------------------------------------------------- /CH 12 Lazy/prime-3.js: -------------------------------------------------------------------------------- 1 | import { Range } from 'immutable'; 2 | 3 | Range(0, 10) 4 | .filter(isPrime) 5 | .forEach(log); 6 | 7 | function isPrime(number) { 8 | if (number > 2){ 9 | const firstDivisor = findFirstDivisor(number); 10 | return !firstDivisor; 11 | } else { 12 | return (number === 2) 13 | ? true 14 | : false; 15 | } 16 | } 17 | 18 | function findFirstDivisor(number){ 19 | return Range(2, (number / 2) + 1) 20 | .find(isDivisorOf(number)); 21 | } 22 | 23 | function isDivisorOf(number) { 24 | return function(divisor) { 25 | return (number > 1) 26 | ? number % divisor === 0 27 | : false 28 | }; 29 | } 30 | 31 | function log(number) { 32 | console.log(number); 33 | } -------------------------------------------------------------------------------- /CH 12 Lazy/prime.js: -------------------------------------------------------------------------------- 1 | function isPrime(number){ 2 | if (number <= 1){ 3 | return false; 4 | } 5 | 6 | for (let i=2; i<=number/2; i++) 7 | { 8 | if (number % i === 0 ) 9 | return false; 10 | } 11 | 12 | return true; 13 | } 14 | 15 | for(let i=0; i<10; i++){ 16 | if(isPrime(i)){ 17 | console.log(i) 18 | } 19 | } -------------------------------------------------------------------------------- /CH 12 Lazy/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node Sequence.js -------------------------------------------------------------------------------- /CH 12 Lazy/transducer.js: -------------------------------------------------------------------------------- 1 | import { pipe, compose } from 'lodash/fp'; 2 | 3 | function _filter(test) { 4 | return function(reducer){ 5 | return function filterReducer(arr, value){ 6 | if(test(value)){ 7 | return reducer(arr, value); 8 | } else { 9 | return arr; 10 | } 11 | } 12 | } 13 | } 14 | 15 | const filter = test => reducer => { 16 | return function filterReducer(arr, value){ 17 | return test(value) 18 | ? reducer(arr, value) 19 | : arr 20 | } 21 | } 22 | 23 | const map = transform => reducer => { 24 | return function mapReducer(arr, value){ 25 | const newValue = transform(value); 26 | return reducer(arr, newValue); 27 | } 28 | } 29 | 30 | const toArrayReducer = (arr, value) => { 31 | return [...arr, value] 32 | }; 33 | 34 | function isEven(n){ 35 | return n % 2 === 0; 36 | } 37 | 38 | function double(n){ 39 | return n * 2; 40 | } 41 | 42 | const numbers = [1 , 2, 3, 4]; 43 | 44 | // 45 | const filterTransducer = filter(isEven); 46 | const mapTransducer = map(double); 47 | 48 | const newNumbers = numbers 49 | .reduce(filterTransducer(mapTransducer(toArrayReducer)), []); 50 | console.log(newNumbers); 51 | 52 | // 53 | const newReducer = compose( 54 | filter(isEven), 55 | map(double) 56 | )(toArrayReducer); 57 | console.log(numbers.reduce(newReducer, [])); -------------------------------------------------------------------------------- /CH 13 Generators/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 13 Generators/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 13 Generators/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 13 Generators/fibonacci.js: -------------------------------------------------------------------------------- 1 | function fibonacci() { 2 | let a = 0; 3 | let b = 1; 4 | return function fibonacciGenerator() { 5 | const aResult = a; 6 | a = b; 7 | b = aResult + b; 8 | return aResult; 9 | }; 10 | } 11 | 12 | export default fibonacci; -------------------------------------------------------------------------------- /CH 13 Generators/forEach.js: -------------------------------------------------------------------------------- 1 | function forEach(callback){ 2 | return function(generate){ 3 | let value = generate(); 4 | // eslint-disable-next-line functional/no-loop-statement 5 | while(value !== undefined){ 6 | callback(value); 7 | value = generate(); 8 | } 9 | } 10 | 11 | } 12 | 13 | export default forEach; -------------------------------------------------------------------------------- /CH 13 Generators/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2", 15 | "immutable": "^4.0.0-rc.12", 16 | "lodash": "^4.17.15" 17 | }, 18 | "devDependencies": { 19 | "eslint": "^7.1.0", 20 | "eslint-plugin-functional": "^3.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CH 13 Generators/pipe.js: -------------------------------------------------------------------------------- 1 | import { pipe } from 'lodash/fp'; 2 | 3 | function capitalize(text) { 4 | return text.charAt(0).toUpperCase() + text.slice(1); 5 | } 6 | 7 | function shortenText(text) { 8 | return text.substring(0, 8).trim(); 9 | } 10 | 11 | const shortText = shortenText(capitalize("this is a long text")); 12 | console.log(shortText); 13 | 14 | const shortText1 = pipe( 15 | capitalize, 16 | shortenText 17 | )("this is a long text"); 18 | console.log(shortText1); -------------------------------------------------------------------------------- /CH 13 Generators/range-finite.js: -------------------------------------------------------------------------------- 1 | function range(from, to){ 2 | let count = from; 3 | return function(){ 4 | if(count < to){ 5 | const result = count; 6 | count += 1; 7 | return result; 8 | } else { 9 | return undefined; 10 | } 11 | } 12 | } 13 | 14 | export default range; -------------------------------------------------------------------------------- /CH 13 Generators/range.js: -------------------------------------------------------------------------------- 1 | function range() { 2 | let count = 0; 3 | return function rangeGenerator() { 4 | const result = count; 5 | count += 1; 6 | return result; 7 | } 8 | } 9 | 10 | export default range; -------------------------------------------------------------------------------- /CH 13 Generators/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node sequence.js -------------------------------------------------------------------------------- /CH 13 Generators/take.js: -------------------------------------------------------------------------------- 1 | function take(n) { 2 | return function(generate) { 3 | let count = 0; 4 | return function() { 5 | if (count < n) { 6 | count += 1; 7 | return generate(); 8 | } 9 | }; 10 | }; 11 | } 12 | 13 | export default take; -------------------------------------------------------------------------------- /CH 13 Generators/test_fibonacci.js: -------------------------------------------------------------------------------- 1 | import { pipe } from 'lodash/fp'; 2 | import fibonacci from './fibonacci'; 3 | import take from './take'; 4 | import forEach from './forEach'; 5 | 6 | const generateFibonacci = fibonacci(); 7 | pipe( 8 | take(5), 9 | forEach(console.log) 10 | )(generateFibonacci); 11 | //0 12 | //1 13 | //1 14 | //2 15 | //3 -------------------------------------------------------------------------------- /CH 13 Generators/test_forEach.js: -------------------------------------------------------------------------------- 1 | import range from './range-finite'; 2 | import forEach from './forEach'; 3 | 4 | function log(x){ 5 | console.log(x); 6 | } 7 | 8 | const nextNumber = range(1, 4); 9 | forEach(log)(nextNumber); 10 | //1 11 | //2 12 | //3 -------------------------------------------------------------------------------- /CH 13 Generators/test_range.js: -------------------------------------------------------------------------------- 1 | import range from './range'; 2 | 3 | const nextNumber = range(); 4 | console.log(nextNumber()); //0 5 | console.log(nextNumber()); //1 6 | console.log(nextNumber()); //2 -------------------------------------------------------------------------------- /CH 13 Generators/test_range_finite.js: -------------------------------------------------------------------------------- 1 | import range from './range-finite'; 2 | 3 | const nextNumber = range(0, 3) 4 | console.log(nextNumber()); 5 | console.log(nextNumber()); 6 | console.log(nextNumber()); 7 | console.log(nextNumber()); -------------------------------------------------------------------------------- /CH 13 Generators/test_range_take.js: -------------------------------------------------------------------------------- 1 | import { pipe } from 'lodash/fp'; 2 | import range from './range'; 3 | import take from './take'; 4 | import forEach from './forEach'; 5 | 6 | const nextNumber = range(); 7 | pipe( 8 | take(3), 9 | forEach(console.log) 10 | )(nextNumber); -------------------------------------------------------------------------------- /CH 13 Generators/test_toArray.js: -------------------------------------------------------------------------------- 1 | import range from './range-finite'; 2 | import toArray from './toArray'; 3 | 4 | const numbers = toArray(range(1, 5)); 5 | console.log(numbers); 6 | //[1, 2, 3, 4] -------------------------------------------------------------------------------- /CH 13 Generators/toArray.js: -------------------------------------------------------------------------------- 1 | function toArray(generate) { 2 | let arr = []; 3 | let value = generate(); 4 | // eslint-disable-next-line functional/no-loop-statement 5 | while (value !== undefined) { 6 | arr = [...arr, value]; 7 | value = generate(); 8 | } 9 | return arr; 10 | } 11 | 12 | export default toArray; -------------------------------------------------------------------------------- /CH 14 Promises AJAX/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 14 Promises AJAX/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 14 Promises AJAX/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .cache 3 | node_modules 4 | dist 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /CH 14 Promises AJAX/all.js: -------------------------------------------------------------------------------- 1 | const dictionariesPromise = fetch('http://localhost:3000/dictionaries').then(toJson); 2 | const todosPromise = fetch('http://localhost:3000/todos').then(toJson); 3 | 4 | function logIdentity(x){ 5 | console.log(x); 6 | return x; 7 | } 8 | 9 | function toJson(response){ 10 | return response.json(); 11 | } 12 | 13 | Promise.all([ 14 | dictionariesPromise, 15 | todosPromise 16 | ]).then(logIdentity); 17 | 18 | -------------------------------------------------------------------------------- /CH 14 Promises AJAX/allSettled.js: -------------------------------------------------------------------------------- 1 | const dictionariesPromise = fetch('http://localhost:3000/dictionaries').then(toJson); 2 | const todosPromise = fetch('http://localhost:3000/todos').then(toJson); 3 | const rejectedPromise = Promise.reject('Error'); 4 | 5 | function toJson(response){ 6 | return response.json(); 7 | } 8 | 9 | function logIdentity(x){ 10 | console.log(x); 11 | return x; 12 | } 13 | 14 | Promise.allSettled([ 15 | dictionariesPromise, 16 | todosPromise, 17 | rejectedPromise 18 | ]) 19 | .then(logIdentity) 20 | //[ 21 | //{status: "fulfilled", value: Array(1)}, 22 | //{status: "fulfilled", value: Array(1)}, 23 | //{status: "rejected", reason: "Error"} 24 | //] 25 | 26 | 27 | -------------------------------------------------------------------------------- /CH 14 Promises AJAX/fetch.js: -------------------------------------------------------------------------------- 1 | function toJson(response){ 2 | return response.json(); 3 | } 4 | 5 | function logIdentity(x){ 6 | console.log(x); 7 | return x; 8 | } 9 | 10 | function logError(errorMsg){ 11 | console.error(errorMsg); 12 | } 13 | 14 | fetch('https://api.github.ssscom/gists/public') 15 | .then(toJson) 16 | .then(logIdentity) 17 | .catch(logError); -------------------------------------------------------------------------------- /CH 14 Promises AJAX/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /CH 14 Promises AJAX/insequence.js: -------------------------------------------------------------------------------- 1 | function fetchDictionaries(){ 2 | return fetch('http://localhost:3000/dictionaries') 3 | .then(toJson); 4 | } 5 | 6 | function fetchTodos(){ 7 | return fetch('http://localhost:3000/todos') 8 | .then(toJson); 9 | } 10 | 11 | function logIdentity(x){ 12 | console.log(x); 13 | return x; 14 | } 15 | 16 | function toJson(response){ 17 | return response.json(); 18 | } 19 | 20 | fetchDictionaries() 21 | .then(fetchTodos) 22 | .then(logIdentity); 23 | 24 | -------------------------------------------------------------------------------- /CH 14 Promises AJAX/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "observables", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "parcel index.html" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@babel/core": "^7.10.2", 14 | "@babel/node": "^7.10.1", 15 | "@babel/preset-env": "^7.10.2", 16 | "rxjs": "^6.5.5" 17 | }, 18 | "devDependencies": { 19 | "eslint": "^7.1.0", 20 | "eslint-plugin-functional": "^3.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CH 14 Promises AJAX/race.js: -------------------------------------------------------------------------------- 1 | const dictionariesPromise = fetch('http://localhost:3000/dictionaries').then(toJson); 2 | const todosPromise = fetch('http://localhost:3000/todos').then(toJson); 3 | 4 | function logIdentity(x){ 5 | console.log(x); 6 | return x; 7 | } 8 | 9 | function toJson(response){ 10 | return response.json(); 11 | } 12 | 13 | Promise.race([ 14 | dictionariesPromise, 15 | todosPromise 16 | ]).then(logIdentity); 17 | 18 | -------------------------------------------------------------------------------- /CH 14 Promises AJAX/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node from.js -------------------------------------------------------------------------------- /CH 14 Promises/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 14 Promises/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 14 Promises/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 14 Promises/catch.js: -------------------------------------------------------------------------------- 1 | function devideBy(divisor){ 2 | return function(number){ 3 | const result = number / divisor; 4 | return (divisor !== 0) 5 | ? Promise.resolve(result) 6 | : Promise.reject("Can't divide by 0") 7 | } 8 | } 9 | 10 | function logIdentity(value){ 11 | console.log(value); 12 | return value; 13 | } 14 | 15 | function logError(error){ 16 | console.error(error); 17 | } 18 | 19 | Promise.resolve(9) 20 | .then(logIdentity) 21 | .then(devideBy(0)) 22 | .then(logIdentity) 23 | .catch(logError) -------------------------------------------------------------------------------- /CH 14 Promises/chain.js: -------------------------------------------------------------------------------- 1 | function toCelsius(kelvin){ 2 | const celsius = (kelvin - 273.15); 3 | return Math.round(celsius * 100) / 100; 4 | } 5 | 6 | function toChangeAction(temperature){ 7 | return { 8 | type : 'CHANGE_TEMPERATURE', 9 | temperature 10 | } 11 | } 12 | 13 | function logIdentity(value){ 14 | console.log(value); 15 | return value; 16 | } 17 | 18 | Promise.resolve(280) 19 | .then(logIdentity) 20 | .then(toCelsius) 21 | .then(logIdentity) 22 | .then(toChangeAction) 23 | .then(logIdentity); 24 | -------------------------------------------------------------------------------- /CH 14 Promises/delay.js: -------------------------------------------------------------------------------- 1 | function delay(interval){ 2 | return new Promise(function(resolve){ 3 | return setTimeout(resolve, interval); 4 | }); 5 | } 6 | 7 | function logDone(){ 8 | console.log('Done'); 9 | } 10 | 11 | delay(1000) 12 | .then(logDone); -------------------------------------------------------------------------------- /CH 14 Promises/flat-mapping-2.js: -------------------------------------------------------------------------------- 1 | function devideBy(divisor){ 2 | return function(n){ 3 | return n / divisor; 4 | } 5 | } 6 | 7 | function logIdentity(value){ 8 | console.log(value); 9 | return value; 10 | } 11 | 12 | Promise.resolve(9) 13 | .then(logIdentity) 14 | .then(devideBy(3)) 15 | .then(logIdentity); -------------------------------------------------------------------------------- /CH 14 Promises/flat-mapping-3.js: -------------------------------------------------------------------------------- 1 | function devideBy(divisor){ 2 | return function(number){ 3 | const result = number / divisor; 4 | return Promise.resolve(result); 5 | } 6 | } 7 | 8 | function logIdentity(value){ 9 | console.log(value); 10 | return value; 11 | } 12 | 13 | Promise.resolve(9) 14 | .then(logIdentity) 15 | .then(devideBy(3)) 16 | .then(logIdentity); -------------------------------------------------------------------------------- /CH 14 Promises/flat-mapping.js: -------------------------------------------------------------------------------- 1 | function devideBy(n, divisor){ 2 | return n / divisor; 3 | } 4 | 5 | function logIdentity(value){ 6 | console.log(value); 7 | return value; 8 | } 9 | 10 | Promise.resolve(9) 11 | .then(logIdentity) 12 | .then(n => devideBy(n, 3)) 13 | .then(logIdentity); -------------------------------------------------------------------------------- /CH 14 Promises/laws-functor-1.js: -------------------------------------------------------------------------------- 1 | //Identity Law 2 | 3 | function identity(n){ 4 | return n; 5 | } 6 | 7 | Promise.resolve(1) 8 | .then(identity) 9 | //Promise 1 10 | 11 | //Composition Law 12 | 13 | function f(x){ 14 | return x + 1; 15 | } 16 | 17 | function g(x){ 18 | return x * 2; 19 | } 20 | 21 | function logIdentity(value){ 22 | console.log(value); 23 | return value; 24 | } 25 | 26 | Promise.resolve(1) 27 | .then(f) 28 | .then(g) 29 | .then(logIdentity) 30 | 31 | Promise.resolve(1) 32 | .then(x => g(f(x))) 33 | .then(logIdentity) 34 | 35 | -------------------------------------------------------------------------------- /CH 14 Promises/laws-functor-2.js: -------------------------------------------------------------------------------- 1 | function f(x){ 2 | return Promise.resolve(x + 1); 3 | } 4 | 5 | function g(x){ 6 | return x * 2; 7 | } 8 | 9 | function logIdentity(value){ 10 | console.log(value); 11 | return value; 12 | } 13 | 14 | Promise.resolve(1) 15 | .then(f) 16 | .then(g) 17 | .then(logIdentity); 18 | 19 | Promise.resolve(1) 20 | .then(x => g(f(x))) 21 | .then(logIdentity); 22 | 23 | //It breaks the Functor Laws 24 | 25 | 26 | -------------------------------------------------------------------------------- /CH 14 Promises/laws-monad-3.js: -------------------------------------------------------------------------------- 1 | 2 | //Left Identity 3 | //M(x).flatMap(f) === f(x) 4 | 5 | Promise.resolve(1) 6 | .then(f) 7 | .then(logIdentity); 8 | //Promise 2 9 | 10 | f(1) 11 | //Promise 2 12 | 13 | //Right Identity 14 | //monad.flatMap(M) === monad 15 | 16 | function unit(value){ 17 | return Promise.resolve(value) 18 | } 19 | 20 | Promise.resolve(1) 21 | .then(unit) 22 | .then(logIdentity); 23 | //Promise 1 24 | 25 | //Associativity 26 | //m.flatMap(f).flatMap(g) ==== m.flatMap(x => f(x).flatMap(g)) 27 | 28 | function f(x){ 29 | return Promise.resolve(x + 1); 30 | } 31 | 32 | function g(x){ 33 | return Promise.resolve(x * 2); 34 | } 35 | 36 | function logIdentity(value){ 37 | console.log(value); 38 | return value; 39 | } 40 | 41 | Promise.resolve(1) 42 | .then(f) 43 | .then(g) 44 | .then(logIdentity); 45 | 46 | Promise.resolve(1) 47 | .then(x => f(x).then(g)) 48 | .then(logIdentity); 49 | 50 | -------------------------------------------------------------------------------- /CH 14 Promises/mappable.js: -------------------------------------------------------------------------------- 1 | function toUpperCase(text){ 2 | return text.toUpperCase(); 3 | } 4 | 5 | function logIdentity(value){ 6 | console.log(value); 7 | return value; 8 | } 9 | 10 | Promise.resolve('sTudY') 11 | .then(logIdentity) 12 | .then(toUpperCase) 13 | .then(logIdentity); -------------------------------------------------------------------------------- /CH 14 Promises/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2", 15 | "immutable": "^4.0.0-rc.12", 16 | "ramda": "^0.27.0" 17 | }, 18 | "devDependencies": { 19 | "eslint": "^7.1.0", 20 | "eslint-plugin-functional": "^3.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CH 14 Promises/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node list.js -------------------------------------------------------------------------------- /CH 14 Promises/timeout.js: -------------------------------------------------------------------------------- 1 | function timeout(interval){ 2 | return new Promise(function(resolve, reject){ 3 | return setTimeout(reject, interval); 4 | }); 5 | } 6 | 7 | function logFailed(){ 8 | console.log('Failed'); 9 | } 10 | 11 | timeout(1000) 12 | .catch(logFailed); -------------------------------------------------------------------------------- /CH 15 Observables AJAX/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 15 Observables AJAX/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 15 Observables AJAX/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .cache 3 | node_modules 4 | dist 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /CH 15 Observables AJAX/ajax.js: -------------------------------------------------------------------------------- 1 | import { ajax } from 'rxjs/ajax'; 2 | import { map } from 'rxjs/operators'; 3 | 4 | ajax('https://api.github.com/gists/public').subscribe(console.log); 5 | 6 | ajax('https://api.github.com/gists/public').pipe( 7 | map(data => data.response), 8 | ).subscribe(console.log); 9 | 10 | ajax.getJSON('https://api.github.com/gists/public') 11 | .subscribe(console.log); -------------------------------------------------------------------------------- /CH 15 Observables AJAX/concatAll.js: -------------------------------------------------------------------------------- 1 | import { from } from 'rxjs'; 2 | import { ajax } from 'rxjs/ajax'; 3 | import { map, concatAll } from 'rxjs/operators'; 4 | 5 | const observable = from([ 6 | 'http://localhost:3000/dictionaries', 7 | 'http://localhost:3000/todos' 8 | ]); 9 | 10 | observable.pipe( 11 | map(ajax.getJSON), 12 | concatAll() 13 | ).subscribe(console.log); 14 | //[{name: 'Dictionary'}] 15 | //[{title: 'To Do'}] 16 | -------------------------------------------------------------------------------- /CH 15 Observables AJAX/forkJoin.js: -------------------------------------------------------------------------------- 1 | import { ajax } from 'rxjs/ajax'; 2 | import { forkJoin } from 'rxjs'; 3 | 4 | const dictionariesObservable = ajax.getJSON('http://localhost:3000/dictionaries'); 5 | const todosObservable = ajax.getJSON('http://localhost:3000/todos'); 6 | 7 | forkJoin({ 8 | dictionaries: dictionariesObservable, 9 | todos: todosObservable 10 | } 11 | ) 12 | .subscribe(console.log); 13 | //{dictionaries: Array(), todos: Array()} 14 | -------------------------------------------------------------------------------- /CH 15 Observables AJAX/hoo.js: -------------------------------------------------------------------------------- 1 | import { from } from 'rxjs'; 2 | import { ajax } from 'rxjs/ajax'; 3 | import { map } from 'rxjs/operators'; 4 | 5 | const observable = from([ 6 | 'http://localhost:3000/dictionaries', 7 | 'http://localhost:3000/todos' 8 | ]); 9 | 10 | observable.pipe( 11 | map(ajax.getJSON) 12 | ).subscribe(console.log); 13 | -------------------------------------------------------------------------------- /CH 15 Observables AJAX/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CH 15 Observables AJAX/insequence.js: -------------------------------------------------------------------------------- 1 | import { map, mapTo, take, tap, concatAll } from 'rxjs/operators'; 2 | import { interval } from 'rxjs'; 3 | 4 | const obs1 = interval(10000).pipe( 5 | take(1), 6 | mapTo('1st') 7 | ); 8 | 9 | const obs2 = interval(3000) 10 | .pipe( 11 | take(1), 12 | mapTo('2nd') 13 | ); 14 | 15 | obs1 16 | .pipe( 17 | tap(console.log), 18 | //map(() => obs2), 19 | mapTo(obs2), 20 | concatAll() 21 | ) 22 | .subscribe(console.log) 23 | -------------------------------------------------------------------------------- /CH 15 Observables AJAX/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "observables", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "parcel index.html" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@babel/core": "^7.10.2", 14 | "@babel/node": "^7.10.1", 15 | "@babel/preset-env": "^7.10.2", 16 | "rxjs": "^6.5.5" 17 | }, 18 | "devDependencies": { 19 | "eslint": "^7.1.0", 20 | "eslint-plugin-functional": "^3.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CH 15 Observables AJAX/race.js: -------------------------------------------------------------------------------- 1 | import { ajax } from 'rxjs/ajax'; 2 | import { race } from 'rxjs'; 3 | 4 | const dictionariesObservable = ajax.getJSON('http://localhost:3000/dictionaries'); 5 | const todosObservable = ajax.getJSON('http://localhost:3000/todos'); 6 | 7 | //take the first observable to emit 8 | const observable = race( 9 | dictionariesObservable, 10 | todosObservable 11 | ); 12 | 13 | observable.subscribe(console.log); 14 | 15 | -------------------------------------------------------------------------------- /CH 15 Observables AJAX/run.bat: -------------------------------------------------------------------------------- 1 | npm run dev -------------------------------------------------------------------------------- /CH 15 Observables/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /CH 15 Observables/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "globals": { 7 | }, 8 | "parserOptions": { 9 | "ecmaVersion": 11, 10 | "sourceType": "module" 11 | }, 12 | "plugins": [ 13 | "functional" 14 | ], 15 | "rules": { 16 | "functional/no-this-expression": "error", 17 | "functional/immutable-data": "error", 18 | "no-var": "error", 19 | "functional/no-conditional-statement": ["error", { 20 | "allowReturningBranches": "ifExhaustive" 21 | }], 22 | "functional/no-loop-statement": "error", 23 | "functional/no-throw-statement": "error" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CH 15 Observables/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | pnpm-debug.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /CH 15 Observables/combine.js: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { map, filter } from 'rxjs/operators'; 3 | import { timer, combineLatest } from 'rxjs'; 4 | 5 | const dataSource1 = of(1, 2, 3, 4, 5); 6 | const dataSource2 = of('A', 'B', 'C'); 7 | 8 | combineLatest(dataSource1, dataSource2) 9 | .subscribe( 10 | ([data1, data2]) => { 11 | console.log(data1) 12 | console.log(data2) 13 | } 14 | ) -------------------------------------------------------------------------------- /CH 15 Observables/create.js: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | 3 | const observable = Observable.create(observer => { 4 | observer.next(1); 5 | observer.next(2); 6 | observer.next(3); 7 | observer.complete(); 8 | }); 9 | 10 | observable.subscribe(console.log); 11 | 12 | setTimeout(()=>{ 13 | observable.subscribe(console.log); 14 | //1 15 | //2 16 | //3 17 | }, 3000); -------------------------------------------------------------------------------- /CH 15 Observables/filter.js: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { map, filter } from 'rxjs/operators'; 3 | 4 | const dataSource = of(1, 2, 3, 4, 5); 5 | 6 | function isEven(n){ 7 | return n % 2 === 0; 8 | } 9 | 10 | dataSource 11 | .pipe( 12 | filter(isEven) 13 | ) 14 | .subscribe(console.log); -------------------------------------------------------------------------------- /CH 15 Observables/from.js: -------------------------------------------------------------------------------- 1 | import { from } from 'rxjs'; 2 | 3 | const observable = from([1, 2, 3]); 4 | observable.subscribe(console.log); 5 | // 1 6 | // 2 7 | // 3 -------------------------------------------------------------------------------- /CH 15 Observables/interval.js: -------------------------------------------------------------------------------- 1 | import { interval } from 'rxjs'; 2 | 3 | /* number of milliseconds */ 4 | const observable = interval(1000); 5 | observable.subscribe(console.log); 6 | //0 7 | //1 8 | //2 9 | //3 10 | //4 11 | //5 12 | -------------------------------------------------------------------------------- /CH 15 Observables/laws1.js: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { map } from 'rxjs/operators'; 3 | 4 | //Identity law 5 | function identity(n){ 6 | return n; 7 | } 8 | 9 | of(1, 2, 3) 10 | .pipe( 11 | map(identity) 12 | ) 13 | .subscribe(console.log); 14 | //Observable 1,2,3 15 | 16 | //Composition law 17 | //observable.map(f).map(g) === observable.map(x => g(f(x))) 18 | 19 | function increment(n){ 20 | return n + 1; 21 | } 22 | 23 | function double(n){ 24 | return n * 2 25 | } 26 | 27 | of(1, 2, 3) 28 | .pipe( 29 | map(increment), 30 | map(double) 31 | ) 32 | .subscribe(console.log); 33 | //Observable 4,6,8 34 | 35 | of(1, 2, 3) 36 | .pipe( 37 | map(x => double(increment(x))) 38 | ) 39 | .subscribe(console.log); 40 | //Observable 4,6,8 -------------------------------------------------------------------------------- /CH 15 Observables/laws2.js: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { mergeMap } from 'rxjs/operators'; 3 | 4 | //Left identity 5 | //M(x).flatMap(f) === f(x) 6 | 7 | function f(n){ 8 | return of(n + 1); 9 | } 10 | 11 | of(1) 12 | .pipe( 13 | mergeMap(f) 14 | ) 15 | .subscribe(console.log); 16 | //Observable 2 17 | 18 | f(1) 19 | .subscribe(console.log); 20 | //Observable 2 21 | 22 | //Right identity 23 | //monad.flatMap(M) === monad 24 | 25 | function unit(value){ 26 | return of(value) 27 | } 28 | 29 | of(1) 30 | .pipe( 31 | mergeMap(unit) 32 | ) 33 | .subscribe(console.log); 34 | //Observable 1 35 | 36 | //Associativity 37 | //monad.flatMap(f).flatMap(g) 38 | // === monad.flatMap(x => f(x).flatMap(g))) 39 | 40 | function g(n){ 41 | return of(n * 2); 42 | } 43 | 44 | of(1) 45 | .pipe( 46 | mergeMap(f), 47 | mergeMap(g) 48 | ) 49 | .subscribe(console.log); 50 | //Observable 4 51 | 52 | of(1) 53 | .pipe( 54 | mergeMap( x => f(x).pipe(mergeMap(g))) 55 | ) 56 | .subscribe(console.log); 57 | //Observable 4 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /CH 15 Observables/map.js: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { map } from 'rxjs/operators'; 3 | 4 | const dataSource = of(1, 2, 3); 5 | 6 | function double(n){ 7 | return n * 2; 8 | } 9 | 10 | dataSource 11 | .pipe( 12 | map(double) 13 | ) 14 | .subscribe(console.log); -------------------------------------------------------------------------------- /CH 15 Observables/mergeAll.js: -------------------------------------------------------------------------------- 1 | import { from } from 'rxjs'; 2 | import { ajax } from 'rxjs/ajax'; 3 | import { map, mergeAll } from 'rxjs/operators'; 4 | 5 | const observable = from([ 6 | 'http://localhost:3000/dictionaries', 7 | 'http://localhost:3000/todos' 8 | ]); 9 | 10 | observable.pipe( 11 | map(ajax.getJSON), 12 | mergeAll() 13 | ).subscribe(console.log); 14 | //[{name: 'Dictionary'}] 15 | //[{title: 'To Do'}] 16 | -------------------------------------------------------------------------------- /CH 15 Observables/mergeMap.js: -------------------------------------------------------------------------------- 1 | import { of, from } from 'rxjs'; 2 | import { map, mergeMap, mergeAll } from 'rxjs/operators'; 3 | 4 | function createGetNTerm(c){ 5 | return function(n){ 6 | return n * c + 1 7 | } 8 | } 9 | 10 | function toSequence(x) { 11 | const getNTerm = createGetNTerm(x); 12 | return of(1, 2, 3).pipe( 13 | map(getNTerm) 14 | ) 15 | } 16 | 17 | toSequence(2) 18 | //.subscribe(console.log); 19 | 20 | toSequence(4) 21 | //.subscribe(console.log); 22 | 23 | // using map and mergeAll 24 | from([2, 4]).pipe( 25 | map(toSequence), 26 | mergeAll() 27 | ) 28 | //.subscribe(console.log); 29 | 30 | // using mergeMap 31 | from([2, 4]).pipe( 32 | mergeMap(toSequence) 33 | ) 34 | .subscribe(console.log); -------------------------------------------------------------------------------- /CH 15 Observables/observers.js: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs'; 2 | 3 | const observer = { 4 | next(value){ 5 | console.log(`Next: ${value}`) 6 | }, 7 | error(msg){ 8 | console.error(`Error: ${msg}`) 9 | }, 10 | complete(value){ 11 | console.error(`Complete`) 12 | } 13 | }; 14 | 15 | const observable = Observable.create(observer => { 16 | observer.next(1); 17 | observer.next(2); 18 | observer.complete('Done'); 19 | }); 20 | observable.subscribe(observer); 21 | 22 | const observable1 = Observable.create(observer => { 23 | observer.next(1); 24 | observer.next(2); 25 | observer.error('Failed'); 26 | }); 27 | observable1.subscribe(observer); 28 | 29 | observable.subscribe(value => console.log(`Next: ${value}`)); 30 | 31 | observable.subscribe( 32 | value => console.log(`Next: ${value}`), 33 | msg => console.error(`Error: ${msg}`), 34 | () => console.error(`Complete`) 35 | ); -------------------------------------------------------------------------------- /CH 15 Observables/of.js: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | 3 | const source = of(1, 2, 3, 4, 5); 4 | source 5 | .subscribe(console.log); -------------------------------------------------------------------------------- /CH 15 Observables/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "observables", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@babel/core": "^7.10.2", 13 | "@babel/node": "^7.10.1", 14 | "@babel/preset-env": "^7.10.2", 15 | "rxjs": "^6.5.5" 16 | }, 17 | "devDependencies": { 18 | "eslint": "^7.1.0", 19 | "eslint-plugin-functional": "^3.0.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CH 15 Observables/pipe.js: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { map, filter } from 'rxjs/operators'; 3 | 4 | const dataSource = of(1, 2, 3, 4, 5); 5 | 6 | function isEven(n){ 7 | return n % 2 === 0; 8 | } 9 | 10 | function double(n){ 11 | return n * 2; 12 | } 13 | 14 | dataSource 15 | .pipe( 16 | filter(isEven), 17 | map(double) 18 | ) 19 | .subscribe(console.log); -------------------------------------------------------------------------------- /CH 15 Observables/run.bat: -------------------------------------------------------------------------------- 1 | npx babel-node create.js -------------------------------------------------------------------------------- /CH 15 Observables/subscription.js: -------------------------------------------------------------------------------- 1 | import { interval } from 'rxjs'; 2 | 3 | const observable = interval(1000); 4 | const subscription = observable.subscribe(console.log); 5 | 6 | // Later 7 | setTimeout(() => { 8 | subscription.unsubscribe(); 9 | }, 3500); 10 | -------------------------------------------------------------------------------- /CH 15 Observables/take.js: -------------------------------------------------------------------------------- 1 | import { of } from 'rxjs'; 2 | import { take } from 'rxjs/operators'; 3 | 4 | const source = of(1, 2, 3, 4, 5); 5 | source 6 | .pipe( 7 | take(3) 8 | ) 9 | .subscribe(console.log); -------------------------------------------------------------------------------- /CH 16 Elm Architecture/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | elm-stuff -------------------------------------------------------------------------------- /CH 16 Elm Architecture/elm.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "application", 3 | "source-directories": [ 4 | "src" 5 | ], 6 | "elm-version": "0.19.0", 7 | "dependencies": { 8 | "direct": { 9 | "elm/browser": "1.0.2", 10 | "elm/core": "1.0.5", 11 | "elm/html": "1.0.0", 12 | "elm/random": "1.0.0" 13 | }, 14 | "indirect": { 15 | "elm/json": "1.1.3", 16 | "elm/time": "1.0.0", 17 | "elm/url": "1.0.0", 18 | "elm/virtual-dom": "1.0.2" 19 | } 20 | }, 21 | "test-dependencies": { 22 | "direct": {}, 23 | "indirect": {} 24 | } 25 | } -------------------------------------------------------------------------------- /CH 16 Elm Architecture/run.bat: -------------------------------------------------------------------------------- 1 | elm make src/Counter.elm --output=dist/counter.html -------------------------------------------------------------------------------- /CH 16 Elm Architecture/src/Counter.elm: -------------------------------------------------------------------------------- 1 | import Browser 2 | import Html exposing (Html, button, div, text) 3 | import Html.Events exposing (onClick) 4 | 5 | -- MODEL 6 | 7 | type alias Model = { 8 | number : Int 9 | } 10 | 11 | initialModel : Model 12 | initialModel = { number = 0 } 13 | 14 | -- MESSAGES 15 | 16 | type Msg = Increment | Decrement 17 | 18 | -- UPDATE 19 | 20 | update : Msg -> Model -> Model 21 | update msg model = 22 | case msg of 23 | Increment -> 24 | { model | number = model.number + 1 } 25 | 26 | Decrement -> 27 | { model | number = model.number - 1 } 28 | 29 | -- VIEW 30 | 31 | view : Model -> Html Msg 32 | view model = 33 | div [] 34 | [ 35 | div [] [ text (String.fromInt model.number) ] 36 | , button [ onClick Decrement ] [ text "-" ] 37 | , button [ onClick Increment ] [ text "+" ] 38 | ] 39 | 40 | 41 | main = 42 | Browser.sandbox { init = initialModel, update = update, view = view } -------------------------------------------------------------------------------- /CH 16 Elm Architecture/src/RandomGenerator-update.js: -------------------------------------------------------------------------------- 1 | function update(model, action){ 2 | switch(action.type){ 3 | case 'GenerateNumber' : { 4 | const command = { name: 'Random.generate', resultMessage : 'SetNumber' }; 5 | return [model, command]; 6 | } 7 | case 'SetNumber' : { 8 | const newModel = { ...model, number: action.payload }; 9 | const command = { name: 'Cmd.none' }; 10 | return [newModel, command]; 11 | } 12 | default : { 13 | const command = { name: 'Cmd.none' }; 14 | return [model, command]; 15 | } 16 | } 17 | } 18 | 19 | const [model, command] = update({}, 'GenerateNumber'); 20 | console.log(model); 21 | console.log(command); 22 | //{} 23 | //{name: "Cmd.none"} -------------------------------------------------------------------------------- /CH 16 Elm Architecture/src/RandomGenerator.elm: -------------------------------------------------------------------------------- 1 | import Browser 2 | import Html exposing (Html, button, div, text) 3 | import Html.Events exposing (onClick) 4 | import Random 5 | 6 | -- MODEL 7 | 8 | type alias Model = { 9 | number : Int 10 | } 11 | 12 | 13 | initModel : () -> (Model, Cmd Msg) 14 | initModel _ = 15 | ( Model 0 16 | , Cmd.none 17 | ) 18 | 19 | -- MESSAGES & COMMANDS 20 | 21 | type Msg = GenerateNumber | SetNumber Int 22 | 23 | -- UPDATE 24 | 25 | update : Msg -> Model -> (Model, Cmd Msg) 26 | update msg model = 27 | case msg of 28 | GenerateNumber -> 29 | ( model 30 | , Random.generate SetNumber (Random.int 1 100) 31 | ) 32 | 33 | SetNumber number -> 34 | ( Model number 35 | , Cmd.none 36 | ) 37 | 38 | -- VIEW 39 | 40 | view : Model -> Html Msg 41 | view model = 42 | div [] 43 | [ 44 | div [] [ text (String.fromInt model.number) ] 45 | , button [ onClick GenerateNumber ] [ text "Generate" ] 46 | ] 47 | 48 | 49 | -- SUBSCRIPTIONS 50 | 51 | 52 | subscriptions : Model -> Sub Msg 53 | subscriptions model = 54 | Sub.none 55 | 56 | main = 57 | Browser.element { init = initModel, update = update, view = view, subscriptions = subscriptions } -------------------------------------------------------------------------------- /CH 16 Redux Commands/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app"], 3 | "plugins": [ 4 | "immutable" 5 | ], 6 | "rules": { 7 | "immutable/no-this": "error", 8 | "immutable/no-mutation": "error" 9 | } 10 | } -------------------------------------------------------------------------------- /CH 16 Redux Commands/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /CH 16 Redux Commands/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /CH 16 Redux Commands/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-redux": "^7.2.0", 12 | "react-scripts": "3.4.1", 13 | "redux": "^4.0.5", 14 | "redux-thunk": "^2.3.0" 15 | }, 16 | "scripts": { 17 | "start": "npx eslint src && react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test", 20 | "eject": "react-scripts eject" 21 | }, 22 | "eslintConfig": { 23 | "extends": "react-app" 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.2%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | }, 37 | "devDependencies": { 38 | "eslint-plugin-immutable": "^1.0.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CH 16 Redux Commands/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /CH 16 Redux Commands/src/Counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { GenerateNumber } from './store'; 4 | 5 | function Counter({number, GenerateNumber}){ 6 | return( 7 |
8 |
{number}
9 | 12 |
13 | ) 14 | } 15 | 16 | export default connect( 17 | state => state, 18 | { GenerateNumber } 19 | )(Counter) -------------------------------------------------------------------------------- /CH 16 Redux Commands/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { createStore, applyMiddleware } from 'redux'; 5 | import thunk from 'redux-thunk'; 6 | import { reducer } from './store'; 7 | import Counter from "./Counter"; 8 | 9 | const store = createStore(reducer, applyMiddleware(thunk)); 10 | 11 | ReactDOM.render( 12 | 13 | 14 | 15 | 16 | , 17 | document.getElementById('root') 18 | ); 19 | -------------------------------------------------------------------------------- /CH 16 Redux Commands/src/store.js: -------------------------------------------------------------------------------- 1 | //MODEL 2 | const initialModel = { 3 | number: 0 4 | }; 5 | 6 | //COMMANDS 7 | function GenerateNumber(){ 8 | return function(dispatch){ 9 | const number = Math.floor(Math.random() * 100); 10 | dispatch(SetNumber(number)); 11 | } 12 | } 13 | 14 | //MESSAGES 15 | function SetNumber(number){ 16 | return { 17 | type : SetNumber.name, 18 | number 19 | } 20 | } 21 | 22 | //UPDATE 23 | function reducer(model = initialModel, action){ 24 | switch(action.type){ 25 | case SetNumber.name: 26 | return { ...model, number: action.number }; 27 | default: 28 | return model; 29 | } 30 | } 31 | 32 | export { reducer, GenerateNumber }; -------------------------------------------------------------------------------- /CH 16 Redux/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app"], 3 | "plugins": [ 4 | "immutable" 5 | ], 6 | "rules": { 7 | "immutable/no-this": "error", 8 | "immutable/no-mutation": "error" 9 | } 10 | } -------------------------------------------------------------------------------- /CH 16 Redux/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /CH 16 Redux/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /CH 16 Redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-redux": "^7.2.0", 12 | "react-scripts": "3.4.1", 13 | "redux": "^4.0.5" 14 | }, 15 | "scripts": { 16 | "start": "npx eslint src && react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": { 25 | "production": [ 26 | ">0.2%", 27 | "not dead", 28 | "not op_mini all" 29 | ], 30 | "development": [ 31 | "last 1 chrome version", 32 | "last 1 firefox version", 33 | "last 1 safari version" 34 | ] 35 | }, 36 | "devDependencies": { 37 | "eslint-plugin-immutable": "^1.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CH 16 Redux/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /CH 16 Redux/src/Counter.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Increment, Decrement } from './store'; 4 | 5 | function Counter({number, Increment, Decrement}){ 6 | return( 7 |
8 |
{number}
9 | 12 | 15 |
16 | ) 17 | } 18 | 19 | export default connect( 20 | state => state, 21 | {Increment, Decrement} 22 | )(Counter); -------------------------------------------------------------------------------- /CH 16 Redux/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { createStore } from 'redux'; 4 | import { Provider } from 'react-redux'; 5 | import Counter from './Counter'; 6 | import { reducer } from './store'; 7 | 8 | const store = createStore(reducer); 9 | 10 | ReactDOM.render( 11 | 12 | 13 | 14 | 15 | , 16 | document.getElementById('root') 17 | ); 18 | -------------------------------------------------------------------------------- /CH 16 Redux/src/store.js: -------------------------------------------------------------------------------- 1 | //MODEL 2 | const initialModel = { 3 | number: 0 4 | }; 5 | 6 | //MESSAGES 7 | function Increment(){ 8 | return { 9 | type : Increment.name 10 | } 11 | } 12 | 13 | function Decrement(){ 14 | return { 15 | type : Decrement.name 16 | } 17 | } 18 | 19 | //UPDATE 20 | function reducer(model = initialModel, action){ 21 | switch(action.type){ 22 | case Increment.name: 23 | return { ...model, number: model.number + 1 }; 24 | case Decrement.name: 25 | return { ...model, number: model.number - 1 }; 26 | default: 27 | return model; 28 | } 29 | } 30 | 31 | export { reducer, Increment, Decrement }; -------------------------------------------------------------------------------- /CH 17 Redux Axios/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["react-app"], 3 | "plugins": [ 4 | "immutable" 5 | ], 6 | "rules": { 7 | "immutable/no-this": "error", 8 | "immutable/no-mutation": "error" 9 | } 10 | } -------------------------------------------------------------------------------- /CH 17 Redux Axios/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /CH 17 Redux Axios/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /CH 17 Redux Axios/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "axios": "^0.19.2", 10 | "react": "^16.13.1", 11 | "react-dom": "^16.13.1", 12 | "react-redux": "^7.2.0", 13 | "react-scripts": "3.4.1", 14 | "redux": "^4.0.5", 15 | "redux-axios-middleware": "^4.0.1" 16 | }, 17 | "scripts": { 18 | "start": "npx eslint src && react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": "react-app" 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | }, 38 | "devDependencies": { 39 | "eslint-plugin-immutable": "^1.0.0", 40 | "redux-devtools-extension": "^2.13.8" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /CH 17 Redux Axios/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /CH 17 Redux Axios/src/List.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { LoadTodos } from './store'; 4 | 5 | function List({todos, LoadTodos}){ 6 | return( 7 |
8 |
9 | 14 |
15 |
16 | { todos.map(todo => 17 | {todo.title} )} 18 |
19 |
20 | ) 21 | } 22 | 23 | export default connect( 24 | state => state, 25 | { LoadTodos } 26 | )(List) -------------------------------------------------------------------------------- /CH 17 Redux Axios/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { createStore, applyMiddleware } from 'redux'; 5 | import axios from 'axios'; 6 | import axiosMiddleware from 'redux-axios-middleware'; 7 | import { composeWithDevTools } from 'redux-devtools-extension'; 8 | import { reducer } from './store'; 9 | import List from "./List"; 10 | 11 | const client = axios.create({ 12 | baseURL:'http://localhost:3000/', 13 | responseType: 'json' 14 | }); 15 | 16 | const store = createStore(reducer, composeWithDevTools( 17 | applyMiddleware( 18 | axiosMiddleware(client) 19 | ) 20 | )); 21 | 22 | ReactDOM.render( 23 | 24 | 25 | 26 | 27 | , 28 | document.getElementById('root') 29 | ); -------------------------------------------------------------------------------- /CH 17 Redux Axios/src/store.js: -------------------------------------------------------------------------------- 1 | //MODEL 2 | const initialModel = { 3 | todos: [] 4 | }; 5 | 6 | //MESSAGES 7 | function LoadTodos(){ 8 | return { 9 | type: LoadTodos.name, 10 | payload: { 11 | request:{ 12 | url:'/todos' 13 | } 14 | } 15 | } 16 | } 17 | 18 | //UPDATE 19 | function reducer(model = initialModel, action){ 20 | switch(action.type){ 21 | case `${LoadTodos.name}_SUCCESS`: 22 | return { 23 | ...model, 24 | todos: action.payload.data 25 | }; 26 | default: 27 | return model; 28 | } 29 | } 30 | 31 | export { reducer, LoadTodos }; -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### Functional Programming in JavaScript 2 | #### Source Code 3 | 4 | In this book, you will find how to use JavaScript as a functional programming language. 5 | 6 | It turns out that JavaScript has everything it needs to be used as a functional language. We just have to remove features from the language starting with the 'this' keyword. 7 | 8 | Functions are values. Functions can operate on other functions. 9 | Inner functions can access variables from the outer functions even after the outer functions have executed. 10 | 11 | Functional programming makes code easier to read, understand, test, and debug. 12 | 13 | * Here are some of the things you will learn: 14 | 15 | * How to disable 'this' and enable immutable data objects using a linter 16 | 17 | * How to work with immutable objects and collections 18 | 19 | * How to do data transformations using core operations like filter, map, sort, or reduce 20 | 21 | * How to use statements like if and switch in a functional way 22 | 23 | * How to create pipelines and use currying to pass additional data 24 | 25 | * How to create and use functors and monads 26 | 27 | * How to work with promises and observables 28 | 29 | * Understand the Elm Architecture 30 | 31 | [https://www.amazon.com/dp/B08CZZ4FQQ](https://www.amazon.com/dp/B08CZZ4FQQ) 32 | 33 | [https://www.amazon.co.uk/dp/B08CZZ4FQQ](https://www.amazon.co.uk/dp/B08CZZ4FQQ) 34 | 35 | [https://www.amazon.es/dp/B08CZZ4FQQ](https://www.amazon.de/dp/B08CZZ4FQQ) 36 | 37 | [https://www.amazon.fr/dp/B08CZZ4FQQ](https://www.amazon.de/dp/B08CZZ4FQQ) --------------------------------------------------------------------------------