diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..05b1cf3 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +**/node_modules/* +**/vendor/* +**/*.min.js +**/coverage/* +**/build/* diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..840d336 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,26 @@ +{ + "env": { + "browser": true, + "node": true, + "commonjs": true, + "jest": true, + "es6": true + }, + "globals": { + "err": true, + "req": true, + "res": true, + "next": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "sourceType": "module" + }, + "rules": { + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": ["error", "single", { "allowTemplateLiterals": true }], + "comma-dangle": ["error", "always-multiline"], + "semi": [ "error", "always" ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c116a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,80 @@ + +# Created by https://www.gitignore.io/api/node,linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +# End of https://www.gitignore.io/api/node,linux diff --git a/DIRECTIONS.md b/DIRECTIONS.md new file mode 100644 index 0000000..70bd927 --- /dev/null +++ b/DIRECTIONS.md @@ -0,0 +1,48 @@ +![cf](https://i.imgur.com/7v5ASc8.png) 02: Tools and Context +====== + +## Submission Instructions +* Work in a fork of this repository +* Work in a branch on your fork +* Write all of your code in a directory named `lab-` + `` **e.g.** `lab-susan` +* Open a pull request to this repository +* Submit on canvas a question and observation, how long you spent, and a link to your pull request + +## Configuration +Configure the root of your repository with the following files and directories. Thoughtfully name and organize any additional configuration or module files. +* **README.md** - contains documentation +* **.gitignore** - contains a [robust](http://gitignore.io) `.gitignore` file +* **.eslintrc** - contains the course linter configuration +* **.eslintignore** - contains the course linter ignore configuration +* **package.json** - contains npm package config + * create a `lint` script for running eslint + * create a `test` script for running tests +* **lib/** - contains module definitions +* **\_\_test\_\_/** - contains unit tests + +## Feature Tasks +#### fp Module +Create a NodeJS module in the lib/ directory named fp.js that exports an object. Create stand-alone `map`, `filter`, `reduce`, and `slice` functions using the `call` and `apply` function methods. Define each function using ES6 lexical arrow function syntax. + +In each function error-check each parameter and throw an Error with a meaningful message if the function is invoked with invalid arguments. Do not use any third party libraries in the FP module. + +* `fp.map` and `fp.filter` should have the function signature `(callback, collection) => Array` +* `fp.reduce` should have the function signature `(callback, initialState, collection) => data` +* `fp.slice` should have the function signature `(begin, end, collection) => Array` + +## Testing +#### FP Module Tests +Create a NodeJS module in the \_\_test\_\_/ named fp.test.js that asserts the correctness of the fp module. + +* Use TDD `describe` and `test` methods to define descriptive tests +* Each `test` callback should aim to test a small well defined feature of a function +* Write tests to ensure the fp module functions correctly error-check parameters + * Assert that the correct errors are thrown with invalid arguments +* Write tests to ensure the fp module functions returns the correct results when invoked with valid arguments + +## Documentation +In your README.md describe the exported values of each module you have defined. Every function description should include it's arity (expected number of parameters), the expected data for each parameters (data-type and limitations), and it's behavior (for both valid and invalid use). Feel free to write any additional information in your README.md. + +## Bonus 2pts +* Create a second module fp-curry.js that is a refactored version of fp.js, where each function has curried arguments +* Create a fp-curry.test.js that is a refactored version of fp.curry.js that tests fp-curry.js diff --git a/README.md b/README.md index 70bd927..0f68f75 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,14 @@ -![cf](https://i.imgur.com/7v5ASc8.png) 02: Tools and Context -====== - -## Submission Instructions -* Work in a fork of this repository -* Work in a branch on your fork -* Write all of your code in a directory named `lab-` + `` **e.g.** `lab-susan` -* Open a pull request to this repository -* Submit on canvas a question and observation, how long you spent, and a link to your pull request - -## Configuration -Configure the root of your repository with the following files and directories. Thoughtfully name and organize any additional configuration or module files. -* **README.md** - contains documentation -* **.gitignore** - contains a [robust](http://gitignore.io) `.gitignore` file -* **.eslintrc** - contains the course linter configuration -* **.eslintignore** - contains the course linter ignore configuration -* **package.json** - contains npm package config - * create a `lint` script for running eslint - * create a `test` script for running tests -* **lib/** - contains module definitions -* **\_\_test\_\_/** - contains unit tests - -## Feature Tasks -#### fp Module -Create a NodeJS module in the lib/ directory named fp.js that exports an object. Create stand-alone `map`, `filter`, `reduce`, and `slice` functions using the `call` and `apply` function methods. Define each function using ES6 lexical arrow function syntax. - -In each function error-check each parameter and throw an Error with a meaningful message if the function is invoked with invalid arguments. Do not use any third party libraries in the FP module. - -* `fp.map` and `fp.filter` should have the function signature `(callback, collection) => Array` -* `fp.reduce` should have the function signature `(callback, initialState, collection) => data` -* `fp.slice` should have the function signature `(begin, end, collection) => Array` - -## Testing -#### FP Module Tests -Create a NodeJS module in the \_\_test\_\_/ named fp.test.js that asserts the correctness of the fp module. - -* Use TDD `describe` and `test` methods to define descriptive tests -* Each `test` callback should aim to test a small well defined feature of a function -* Write tests to ensure the fp module functions correctly error-check parameters - * Assert that the correct errors are thrown with invalid arguments -* Write tests to ensure the fp module functions returns the correct results when invoked with valid arguments - -## Documentation -In your README.md describe the exported values of each module you have defined. Every function description should include it's arity (expected number of parameters), the expected data for each parameters (data-type and limitations), and it's behavior (for both valid and invalid use). Feel free to write any additional information in your README.md. - -## Bonus 2pts -* Create a second module fp-curry.js that is a refactored version of fp.js, where each function has curried arguments -* Create a fp-curry.test.js that is a refactored version of fp.curry.js that tests fp-curry.js +# Lab 02 + +## exports + +###fp +fp exports four customized functions that affect arrays +####fp.map +takes in a callback function, and an array or array like object and returns an array of modified values. it also catches errors and returns a descriptive error message +####fp.slice +takes in an array or array like object, a begin, and an optional end and returns an array containing a subset of the original array. it also catches errors and returns a descriptive error message +####fp.reduce +takes in a callback function, an array or array like object and an inital value and returns a value or string built out of the original array. it also catches errors and returns a descriptive error message +####fp.filter +takes in a callback function, and an array or array like object and returns an array of values that match the conditions. it also catches errors and returns a descriptive error message diff --git a/__test__/fp.test.js b/__test__/fp.test.js new file mode 100644 index 0000000..7f7f135 --- /dev/null +++ b/__test__/fp.test.js @@ -0,0 +1,185 @@ +'use strict'; + +const fp = require('../lib/fp.js'); + +describe('fp.js', () => { + describe('fp.reduce', () => { + // parameters are f(collection, callback, initialValue) + test('return value should be the sum of the collection', ()=>{ + expect(fp.reduce( + (x, y) => x+y, + [1,2,3], + 0) + ).toBe(6); + }); + test('return value should be the product of the collection', ()=>{ + expect(fp.reduce( + (x, y) => x*y, + [1,5,3], + 1) + ).toBe(15); + }); + test('an exception should be thrown if error', ()=>{ + expect( + () => { + fp.reduce( + (x, y) => x+y, + ['1',2, null], + 0); + }).toThrow(); + expect( + () => { + fp.reduce( + (x, y) => x+y, + 1, + 0); + }).toThrow(); + expect( + () => { + fp.reduce( + (x, y) => x+y, + [1,2,3], + 'a'); + }).toThrow(); + expect( + () => { + fp.reduce( + 'lets do this', + [1, 2, 3], + 0); + }).toThrow(); + }); + }); + describe('fp.map', () => { + // parameters are f(collection, callback) + test('return values should increment by two', ()=>{ + expect(fp.map( + (x) => x+2, + [1,2,3] + ).toString() + ).toBe('3,4,5'); + }); + test('return values should double', ()=>{ + expect(fp.map( + (x) => x*2, + [1,2,3] + ).toString() + ).toBe('2,4,6'); + }); + test('an exception should be thrown if error', ()=>{ + expect( + () => { + fp.map( + (x) => x+2, + ['1',2, null], + 0); + }).toThrow(); + expect( + () => { + fp.map( + (x) => x+2, + 1 + ); + }).toThrow(); + expect( + () => { + fp.map( + 'lets do this', + [1, 2, 3] + ); + }).toThrow(); + }); + }); + describe('fp.filter', () => { + // parameters are f(collection, callback) + test('return values should only have a length greater than 6', ()=>{ + expect(fp.filter( + (password) => password.length > 6, + ['password','pass','1234'] + ).toString() + ).toBe('password'); + }); + test('return values should be larger than 90', ()=>{ + expect(fp.filter( + (grades) => grades > 90, + [95, 97.5, 81, 99, 23] + ).toString() + ).toBe('95,97.5,99'); + }); + test('an exception should be thrown if error', ()=>{ + expect( + () => { + fp.filter( + (grades) => grades > 90, + ['kerry', 97.5, 'nicholas', 99, 23] + ); + }).toThrow(); + expect( + () => { + fp.filter( + (grades) => grades > 90, + 99 + ); + + }).toThrow(); + expect( + () => { + fp.filter( + 'greater than 90', + [1, 2, 3] + ); + }).toThrow(); + }); + }); + describe('fp.slice', () => { + // parameters are f(collection, begin, end) + test('return value should be the last three values', ()=>{ + expect(fp.slice( + [1, 2, 3, 4, 5], + 2 + ).toString() + ).toBe('3,4,5'); + }); + test('return value should be the middle 3 values', ()=>{ + expect(fp.slice( + [1, 2, 'three', 4, 5], + 1, + 4 + ).toString() + ).toBe('2,three,4'); + });2, 'three', 4; + test('an exception should be thrown if error', ()=>{ + expect( + () => { + fp.slice( + [1, 2, 3, 4, 5], + 5, + 1 + ); + }).toThrow(); + expect( + () => { + fp.slice( + [1, 2, 3, 4, 5], + 'start', + 1 + ); + }).toThrow(); + expect( + () => { + fp.slice( + [1, 2, 3, 4, 5], + 1, + 'five' + ); + }).toThrow(); + expect( + () => { + fp.slice( + 1, + 1, + 1); + }).toThrow(); + }); + }); +}); diff --git a/lib/fp.js b/lib/fp.js new file mode 100644 index 0000000..cdd352e --- /dev/null +++ b/lib/fp.js @@ -0,0 +1,60 @@ +'use strict'; + + +const fp = module.exports = {}; + +fp.reduce = (callback, collection, initialValue) => { + //assumes that collection is an array like object, we should test for this + if(typeof callback !== 'function' ){ + throw new TypeError(' must be a function'); + } + if(typeof collection !== 'object' ){ + throw new TypeError(' must be an array or array like object'); + } + collection.forEach((x) => {if(typeof x !== 'number'){throw new TypeError(' inputs must be numbers');}}); + if(typeof initialValue !== 'number'){ + console.log('callback'); + throw new TypeError(' must be a number'); + } + return Array.prototype.reduce.call(collection, callback, initialValue); +}; + +fp.map = (callback, collection) => { + if(typeof callback !== 'function' ){ + throw new TypeError(' must be a function'); + } + if(typeof collection !== 'object' ){ + throw new TypeError(' must be an array or array like object'); + } + collection.forEach((x) => {if(typeof x !== 'number'){throw new TypeError(' inputs must be numbers');}}); + return Array.prototype.map.call(collection, callback); +}; + +fp.filter = (callback, collection) => { + if(typeof callback !== 'function' ){ + throw new TypeError(' must be a function'); + } + if(typeof collection !== 'object' ){ + throw new TypeError(' must be an array or array like object'); + } + for(let i=0; i inputs must be of same type');} + } + return Array.prototype.filter.call(collection, callback); +}; + +fp.slice = (collection, begin, end) => { + if(!Number.isInteger(begin)){ + throw new TypeError('begin must be an integer'); + } + if(end && !Number.isInteger(end)){ + throw new TypeError('end must be an integer or null'); + } + if(typeof collection !== 'object' ){ + throw new TypeError(' must be an array or array like object'); + } + if(begin >= collection.length || end >= collection.length+1){ + throw new TypeError(' and must not be larger than the length of the '); + } + return Array.prototype.slice.call(collection, begin, end); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..967b293 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "call-bind-apply", + "version": "1.0.0", + "description": "##When to use exceptions", + "main": "index.js", + "dependencies": { + "uuid": "^3.1.0" + }, + "devDependencies": { + "eslint": "^4.12.0", + "jest": "^21.2.1" + }, + "scripts": { + "test": "jest -i --coverage", + "lint": "eslint ." + + }, + "author": "", + "license": "ISC" +}