diff --git a/config.json b/config.json index 80db48f451..de2c9a1dc0 100644 --- a/config.json +++ b/config.json @@ -1119,6 +1119,19 @@ "Pattern recognition", "Regular expressions" ] + }, + { + "uuid": "213ea396-0c25-e480-4e76-5876975bdd54ac4890c", + "slug": "isbn-verifier", + "core": false, + "unlocked_by": "bob", + "difficulty": 4, + "topics": [ + "control_flow_conditionals", + "control_flow_loops", + "pattern_recognition", + "regular_expressions" + ] } ], "foregone": [] diff --git a/exercises/isbn-verifier/README.md b/exercises/isbn-verifier/README.md new file mode 100644 index 0000000000..d7f31b6552 --- /dev/null +++ b/exercises/isbn-verifier/README.md @@ -0,0 +1,62 @@ +# ISBN Verifier + +Check if a given string is a valid ISBN-10 number. + +## Functionality + +Given an unknown string the program should check if the provided string is a valid ISBN-10. +Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN. + +The program should allow for ISBN-10 without the separating dashes to be verified as well. + +## ISBN + +Let's take a random ISBN-10 number, say `3-598-21508-8` for this. +The first digit block indicates the group where the ISBN belongs. Groups can consist of shared languages, geographic regions or countries. The leading '3' signals this ISBN is from a german speaking country. +The following number block is to identify the publisher. Since this is a three digit publisher number there is a 5 digit title number for this book. +The last digit in the ISBN is the check digit which is used to detect read errors. + +The first 9 digits in the ISBN have to be between 0 and 9. +The check digit can additionally be an 'X' to allow 10 to be a valid check digit as well. + +A valid ISBN-10 is calculated with this formula `(x1 * 10 + x2 * 9 + x3 * 8 + x4 * 7 + x5 * 6 + x6 * 5 + x7 * 4 + x8 * 3 + x9 * 2 + x10 * 1) mod 11 == 0` +So for our example ISBN this means: +(3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 = 0 + +Which proves that the ISBN is valid. + +## Setup + +Go through the setup instructions for ECMAScript to +install the necessary dependencies: + +http://exercism.io/languages/ecmascript + +## Requirements + +Install assignment dependencies: + +```bash +$ npm install +``` + +## Making the test suite pass + +Execute the tests with: + +```bash +$ npm test +``` + +In the test suites all tests but the first have been skipped. + +Once you get a test passing, you can enable the next one by +changing `xtest` to `test`. + + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation](https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/exercises/isbn-verifier/example.js b/exercises/isbn-verifier/example.js new file mode 100644 index 0000000000..508d9d3713 --- /dev/null +++ b/exercises/isbn-verifier/example.js @@ -0,0 +1,17 @@ +export default class ISBN { + + constructor(isbn) { + this.isbn = isbn.replace(/-/g, ''); + } + + isValid() { + if (!this.isbn.match(/^(\d{9}[\dxX])$/)) return false; + + const digits = [...this.isbn]; + if (digits[9].match(/[xX]/)) digits[9] = 10; + + const sum = digits.reduce((acc, value, index) => acc + ((10 - index) * parseInt(value, 10)), 0); + + return sum % 11 === 0; + } +} diff --git a/exercises/isbn-verifier/isbn-verifier.spec.js b/exercises/isbn-verifier/isbn-verifier.spec.js new file mode 100644 index 0000000000..cfdd40018a --- /dev/null +++ b/exercises/isbn-verifier/isbn-verifier.spec.js @@ -0,0 +1,81 @@ +import ISBN from './isbn-verifier.js'; + +describe('ISBN Verifier Test Suite', () => { + test('valid isbn number', () => { + const isbn = new ISBN('3-598-21508-8'); + + expect(isbn.isValid()).toEqual(true); + }); + + test('invalid isbn check digit', () => { + const isbn = new ISBN('3-598-21508-9'); + + expect(isbn.isValid()).toEqual(false); + }); + + xtest('valid isbn number with a check digit of 10', () => { + const isbn = new ISBN('3-598-21507-X'); + + expect(isbn.isValid()).toEqual(true); + }); + + xtest('check digit is a character other than X', () => { + const isbn = new ISBN('3-598-21507-A'); + + expect(isbn.isValid()).toEqual(false); + }); + + xtest('invalid character in isbn', () => { + const isbn = new ISBN('3-598-2K507-0'); + + expect(isbn.isValid()).toEqual(false); + }); + + xtest('X is only valid as a check digit', () => { + const isbn = new ISBN('3-598-2X507-0'); + + expect(isbn.isValid()).toEqual(false); + }); + + xtest('valid isbn without separating dashes', () => { + const isbn = new ISBN('3598215088'); + + expect(isbn.isValid()).toEqual(true); + }); + + xtest('isbn without separating dashes and X as check digit', () => { + const isbn = new ISBN('359821507X'); + + expect(isbn.isValid()).toEqual(true); + }); + + xtest('isbn without check digit and dashes', () => { + const isbn = new ISBN('359821507'); + + expect(isbn.isValid()).toEqual(false); + }); + + xtest('too long isbn and no dashes', () => { + const isbn = new ISBN('3598215078X'); + + expect(isbn.isValid()).toEqual(false); + }); + + xtest('isbn without check digit', () => { + const isbn = new ISBN('3-598-21507'); + + expect(isbn.isValid()).toEqual(false); + }); + + xtest('too long isbn', () => { + const isbn = new ISBN('3-598-21507-XA'); + + expect(isbn.isValid()).toEqual(false); + }); + + xtest('check digit of X should not be used for 0', () => { + const isbn = new ISBN('3-598-21515-X'); + + expect(isbn.isValid()).toEqual(false); + }); +}); diff --git a/exercises/isbn-verifier/package.json b/exercises/isbn-verifier/package.json new file mode 100644 index 0000000000..3e31078838 --- /dev/null +++ b/exercises/isbn-verifier/package.json @@ -0,0 +1,69 @@ +{ + "name": "xecmascript", + "version": "0.0.0", + "description": "Exercism exercises in ECMAScript 6.", + "author": "Katrina Owen", + "private": true, + "repository": { + "type": "git", + "url": "https://github.com/exercism/xecmascript" + }, + "devDependencies": { + "babel-jest": "^20.0.3", + "babel-plugin-transform-builtin-extend": "^1.1.2", + "babel-preset-env": "^1.4.0", + "eslint": "^3.19.0", + "eslint-config-airbnb": "^15.0.1", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-jsx-a11y": "^5.0.1", + "eslint-plugin-react": "^7.0.1", + "jest": "^20.0.4" + }, + "jest": { + "modulePathIgnorePatterns": [ + "package.json" + ] + }, + "babel": { + "presets": [ + "env" + ], + "plugins": [ + [ + "babel-plugin-transform-builtin-extend", + { + "globals": [ + "Error" + ] + } + ], + ["transform-regenerator"] + ] + }, + "scripts": { + "test": "jest --no-cache ./*", + "watch": "jest --no-cache --watch ./*", + "lint": "eslint .", + "lint-test": "eslint . && jest --no-cache ./* " + }, + "eslintConfig": { + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "env": { + "es6": true, + "node": true, + "jest": true + }, + "extends": "airbnb", + "rules": { + "import/no-unresolved": "off", + "import/extensions": "off" + } + }, + "licenses": [ + "MIT" + ], + "dependencies": {} +}