diff --git a/.travis.yml b/.travis.yml index 008cbc167..a9c0a7fc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: node_js node_js: - - "5.10.0" + - "12.16.1" diff --git a/README.md b/README.md index a81b5c08a..775a6a210 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Brest Rolling Scopes](http://brest.rollingscopes.com/images/logo_rs_text.svg)](http://brest.rollingscopes.com/) #Brest Rolling Scopes School -## Javascript Assignments [![Build Status](https://travis-ci.org/rolling-scopes-school/js-assignments.svg?branch=master)](https://travis-ci.org/rolling-scopes-school/js-assignments) +## Javascript Assignments [![Build Status](https://travis-ci.org/AisBrestEDU/js-assignments.svg?branch=master)](https://travis-ci.org/AisBrestEDU/js-assignments) Yet another javascript assignments. There are a lot of interactive javascript resources for beginners, but most of them are online and do not cover the modern programming workflow. There are some excellent training resources on github (https://github.com/rmurphey/js-assessment, https://github.com/mrdavidlaing/javascript-koans, https://github.com/vasanthk/js-bits etc) but they are not exactly simulate the everyday programming process. So the motivation of this project is to show TDD process in the wild to the beginners. Assingment tests are implemented in various ways to feel a difference and gain the experience what manner is good, what is bad and what is ugly. @@ -22,14 +22,14 @@ To start javascript assignments please follow the next steps: ### How to setup travis-ci * Open [https://travis-ci.org/](https://travis-ci.org/) and sign in with your github account. * Activate your forked repo **js-assignments**. -* Edit local README.md file and update all links (just replace all occurrences of `'rolling-scopes-school'` with your account name). +* Edit local README.md file and update all links (just replace all occurrences of `'TapeGhad'` with your account name). * Commit and push updated README.md to github: ```bash git add README.md git commit -m "Update the links" git push origin master ``` -* Open https://github.com/rolling-scopes-school/js-assignments and test the build icon. Now it will run all tests and update status once you push changes to github. Keep this icon green! +* Open https://github.com/TapeGhad/js-assignments and test the build icon. Now it will run all tests and update status once you push changes to github. Keep this icon green! ### How to setup work environment @@ -73,7 +73,7 @@ and run the unit tests again. Find one test failed (red). Now it's time to fix i * Implement the function by any way and verify your solution by running tests until the failed test become passed (green). * Your solution work, but now time to refactor it. Try to make your code as pretty and simple as possible keeping up the test green. * Once you can't improve your code and tests are passed you can commit your solution. -* Push your updates to github server and check if tests passed on [travis-ci](https://travis-ci.org/rolling-scopes-school/js-assignments/builds). +* Push your updates to github server and check if tests passed on [travis-ci](https://travis-ci.org/TapeGhad/js-assignments/builds). * If everything is OK you can try to resolve the next task. ### How to debug tasks diff --git a/extensions/it-optional.js b/extensions/it-optional.js index 380b80918..0af4d7756 100644 --- a/extensions/it-optional.js +++ b/extensions/it-optional.js @@ -3,16 +3,23 @@ exports = module.exports = testOptional; function testOptional(title, fn) { - - it(title, function() { - try { - fn.call(this); - } catch (err) { - if (err.message=="Not implemented") { + + it(title, function () { + let errHandler = (err) => { + if (err.message == "Not implemented") { this.test.skip(); } else { throw err; } + }; + + try { + let promise = fn.call(this); + if (promise && promise.catch) { + return promise.catch(errHandler); + } + } catch (err) { + errHandler(err); } }); diff --git a/launch.json b/launch.json new file mode 100644 index 000000000..f72fbfcf7 --- /dev/null +++ b/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", + } + ] + } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..9c3bb9263 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,154 @@ +{ + "name": "js-training", + "version": "0.9.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "commander": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz", + "integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=", + "dev": true + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "diff": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", + "integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz", + "integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=", + "dev": true + }, + "glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", + "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", + "dev": true, + "requires": { + "inherits": "2", + "minimatch": "0.3" + } + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", + "dev": true + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "jade": { + "version": "0.26.3", + "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", + "integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=", + "dev": true, + "requires": { + "commander": "0.6.1", + "mkdirp": "0.3.0" + }, + "dependencies": { + "commander": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", + "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=", + "dev": true + }, + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=", + "dev": true + } + } + }, + "lru-cache": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", + "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=", + "dev": true + }, + "minimatch": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", + "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", + "dev": true, + "requires": { + "lru-cache": "2", + "sigmund": "~1.0.0" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz", + "integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=", + "dev": true, + "requires": { + "commander": "2.3.0", + "debug": "2.2.0", + "diff": "1.4.0", + "escape-string-regexp": "1.0.2", + "glob": "3.2.11", + "growl": "1.9.2", + "jade": "0.26.3", + "mkdirp": "0.5.1", + "supports-color": "1.2.0", + "to-iso-string": "0.0.2" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + }, + "sigmund": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", + "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=", + "dev": true + }, + "supports-color": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz", + "integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=", + "dev": true + }, + "to-iso-string": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz", + "integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 712b264fa..38276dce3 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,10 @@ "author": "aorgish", "license": "MIT", "devDependencies": { - "mocha": "^2.3.4" + "mocha": "^2.5.3" }, - "repository" : { - "type" : "git", - "url" : "https://github.com/rolling-scopes-school/js-assignments.git" + "repository": { + "type": "git", + "url": "https://github.com/rolling-scopes-school/js-assignments.git" } } diff --git a/task/01-strings-tasks.js b/task/01-strings-tasks.js index e28054657..fea07bc03 100644 --- a/task/01-strings-tasks.js +++ b/task/01-strings-tasks.js @@ -22,7 +22,7 @@ * '', 'bb' => 'bb' */ function concatenateStrings(value1, value2) { - throw new Error('Not implemented'); + return value1 + value2; } @@ -38,7 +38,7 @@ function concatenateStrings(value1, value2) { * '' => 0 */ function getStringLength(value) { - throw new Error('Not implemented'); + return value.length; } /** @@ -55,7 +55,7 @@ function getStringLength(value) { * 'Chuck','Norris' => 'Hello, Chuck Norris!' */ function getStringFromTemplate(firstName, lastName) { - throw new Error('Not implemented'); + return `Hello, ${firstName} ${lastName}!`; } /** @@ -69,7 +69,7 @@ function getStringFromTemplate(firstName, lastName) { * 'Hello, Chuck Norris!' => 'Chuck Norris' */ function extractNameFromTemplate(value) { - throw new Error('Not implemented'); + return value.slice(7, -1); } @@ -84,7 +84,7 @@ function extractNameFromTemplate(value) { * 'cat' => 'c' */ function getFirstChar(value) { - throw new Error('Not implemented'); + return value[0]; } /** @@ -99,7 +99,7 @@ function getFirstChar(value) { * '\tHello, World! ' => 'Hello, World!' */ function removeLeadingAndTrailingWhitespaces(value) { - throw new Error('Not implemented'); + return value.trim( /\t| |/g,'' ); } /** @@ -114,7 +114,7 @@ function removeLeadingAndTrailingWhitespaces(value) { * 'cat', 3 => 'catcatcat' */ function repeatString(value, count) { - throw new Error('Not implemented'); + return value.repeat(count); } /** @@ -130,7 +130,7 @@ function repeatString(value, count) { * 'ABABAB','BA' => 'ABAB' */ function removeFirstOccurrences(str, value) { - throw new Error('Not implemented'); + return str.replace(value, ''); } /** @@ -145,7 +145,7 @@ function removeFirstOccurrences(str, value) { * '' => 'a' */ function unbracketTag(str) { - throw new Error('Not implemented'); + return str.replace( /<|>|/g,'' ); } @@ -160,7 +160,7 @@ function unbracketTag(str) { * 'abcdefghijklmnopqrstuvwxyz' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' */ function convertToUpperCase(str) { - throw new Error('Not implemented'); + return str.toUpperCase(); } /** @@ -174,7 +174,7 @@ function convertToUpperCase(str) { * 'info@gmail.com' => ['info@gmail.com'] */ function extractEmails(str) { - throw new Error('Not implemented'); + return str.split(";"); } /** @@ -201,7 +201,12 @@ function extractEmails(str) { * */ function getRectangleString(width, height) { - throw new Error('Not implemented'); + let str = "┌"+"─".repeat(width-2)+"┐"+'\n'; + + for (let countHeight = height - 2; countHeight > 0; countHeight -- ){ + str = str.concat("│" + " ".repeat(width-2)) + "│\n"; + } + return str.concat("└" + "─".repeat(width - 2)+"┘\n"); } @@ -221,9 +226,20 @@ function getRectangleString(width, height) { * */ function encodeToRot13(str) { - throw new Error('Not implemented'); -} + let finalArr = []; + for (let count = 0; count < str.length; count ++ ){ + if ((str.charCodeAt(count) >= 78 & str.charCodeAt(count) <= 90) | (str.charCodeAt(count) >= 110 & str.charCodeAt(count) <= 122)) + { + finalArr.push(str.charCodeAt(count) - 13); + } else + if ((str.charCodeAt(count) >= 65 & str.charCodeAt(count) <= 77)| (str.charCodeAt(count) >= 97 & str.charCodeAt(count) <= 109)) + { + finalArr.push(str.charCodeAt(count) + 13); + } else finalArr.push(str.charCodeAt(count)); + }; + return String.fromCharCode(...finalArr); +} /** * Returns true if the value is string; otherwise false. * @param {string} value @@ -238,7 +254,7 @@ function encodeToRot13(str) { * isString(new String('test')) => true */ function isString(value) { - throw new Error('Not implemented'); + return (typeof value ==="string" || value instanceof String); } @@ -267,7 +283,11 @@ function isString(value) { * 'K♠' => 51 */ function getCardId(value) { - throw new Error('Not implemented'); + let arr = ['A♣','2♣','3♣','4♣','5♣','6♣','7♣','8♣','9♣','10♣','J♣','Q♣','K♣', + 'A♦','2♦','3♦','4♦','5♦','6♦','7♦','8♦','9♦','10♦','J♦','Q♦','K♦', + 'A♥','2♥','3♥','4♥','5♥','6♥','7♥','8♥','9♥','10♥','J♥','Q♥','K♥', + 'A♠','2♠','3♠','4♠','5♠','6♠','7♠','8♠','9♠','10♠','J♠','Q♠','K♠']; + return arr.indexOf(value); } diff --git a/task/02-numbers-tasks.js b/task/02-numbers-tasks.js index c9ed20208..3002586f9 100644 --- a/task/02-numbers-tasks.js +++ b/task/02-numbers-tasks.js @@ -1,5 +1,7 @@ 'use strict'; + + /******************************************************************************************** * * * Plese read the following tutorial before implementing tasks: * @@ -22,7 +24,7 @@ * 5, 5 => 25 */ function getRectangleArea(width, height) { - throw new Error('Not implemented'); + return width * height; } @@ -38,7 +40,7 @@ function getRectangleArea(width, height) { * 0 => 0 */ function getCicleCircumference(radius) { - throw new Error('Not implemented'); + return 2 * radius * Math.PI; } /** @@ -54,7 +56,7 @@ function getCicleCircumference(radius) { * -3, 3 => 0 */ function getAverage(value1, value2) { - throw new Error('Not implemented'); + return value1 / 2 + value2 / 2; } /** @@ -73,7 +75,7 @@ function getAverage(value1, value2) { * (-5,0) (10,-10) => 18.027756377319946 */ function getDistanceBetweenPoints(x1, y1, x2, y2) { - throw new Error('Not implemented'); + return Math.hypot(x1 - x2, y1 - y2); } /** @@ -89,7 +91,7 @@ function getDistanceBetweenPoints(x1, y1, x2, y2) { * 5*x = 0 => 0 */ function getLinearEquationRoot(a, b) { - throw new Error('Not implemented'); + return -b / a; } @@ -111,7 +113,8 @@ function getLinearEquationRoot(a, b) { * (0,1) (1,2) => 0 */ function getAngleBetweenVectors(x1, y1, x2, y2) { - throw new Error('Not implemented'); + return Math.acos((x1 * x2 + y1 * y2) / Math.hypot(x1, y1) * + Math.sqrt( Math.hypot(x2, y2))); } /** @@ -127,7 +130,7 @@ function getAngleBetweenVectors(x1, y1, x2, y2) { * 0 => 0 */ function getLastDigit(value) { - throw new Error('Not implemented'); + return value % 10; } @@ -143,7 +146,7 @@ function getLastDigit(value) { * '-525.5' => -525.5 */ function parseNumberFromString(value) { - throw new Error('Not implemented'); + return parseFloat(value); } /** @@ -160,7 +163,7 @@ function parseNumberFromString(value) { * 1,2,3 => 3.741657386773941 */ function getParallelipidedDiagonal(a,b,c) { - throw new Error('Not implemented'); + return Math.hypot(a, b, c); } /** @@ -181,7 +184,8 @@ function getParallelipidedDiagonal(a,b,c) { * 1678, 3 => 2000 */ function roundToPowerOfTen(num, pow) { - throw new Error('Not implemented'); + return Math.round( num / 10 ** pow) * 10 ** pow; + } /** @@ -202,7 +206,12 @@ function roundToPowerOfTen(num, pow) { * 17 => true */ function isPrime(n) { - throw new Error('Not implemented'); + for ( let count = 2; count <= n / 2; count ++ ){ + if (n % count === 0) { + return false; + } + } + return true; } /** @@ -221,7 +230,8 @@ function isPrime(n) { * toNumber(new Number(42), 0) => 42 */ function toNumber(value, def) { - throw new Error('Not implemented'); + if ( parseInt(value) ) return parseInt(value); + return def; } module.exports = { diff --git a/task/03-date-tasks.js b/task/03-date-tasks.js index 83c6266bc..db8612bd2 100644 --- a/task/03-date-tasks.js +++ b/task/03-date-tasks.js @@ -22,7 +22,7 @@ * 'Sun, 17 May 1998 03:00:00 GMT+01' => Date() */ function parseDataFromRfc2822(value) { - throw new Error('Not implemented'); + return Date.parse(value); } /** @@ -37,7 +37,7 @@ function parseDataFromRfc2822(value) { * '2016-01-19T08:07:37Z' => Date() */ function parseDataFromIso8601(value) { - throw new Error('Not implemented'); + return Date.parse(value); } @@ -56,7 +56,10 @@ function parseDataFromIso8601(value) { * Date(2015,1,1) => false */ function isLeapYear(date) { - throw new Error('Not implemented'); + if (date.getFullYear() % 4 != 0) return false; + else if (date.getFullYear() % 100 != 0) return true; + else if (date.getFullYear() % 400 != 0) return false; + else return true; } @@ -76,7 +79,17 @@ function isLeapYear(date) { * Date(2000,1,1,10,0,0), Date(2000,1,1,15,20,10,453) => "05:20:10.453" */ function timeSpanToString(startDate, endDate) { - throw new Error('Not implemented'); + let timePeriod = endDate-startDate; + let milliseconds = parseInt((timePeriod % 1000)), seconds = Math.floor((timePeriod / 1000) % 60), + minutes = Math.floor((timePeriod / (1000 * 60)) % 60), + hours = Math.floor((timePeriod / (1000 * 60 * 60)) % 24); + hours = (startDate.getDay()!== endDate.getDay()) ? hours+24 : hours; + hours = (hours < 10) ? "0" + hours : hours; + minutes = (minutes < 10) ? "0" + minutes : minutes; + seconds = (seconds < 10) ? "0" + seconds : seconds; + milliseconds = (milliseconds < 100) ? "0" + milliseconds : milliseconds; + milliseconds = (milliseconds < 10) ? "0" + milliseconds : milliseconds; + return hours + ":" + minutes + ":" + seconds + "." + milliseconds; } @@ -94,7 +107,11 @@ function timeSpanToString(startDate, endDate) { * Date.UTC(2016,3,5,21, 0) => Math.PI/2 */ function angleBetweenClockHands(date) { - throw new Error('Not implemented'); + let hours = date.getHours(); + let minutes = date.getMinutes(); + let timeZone = date.getTimezoneOffset(); + let angleTime = Math.abs((hours * 60 + minutes + timeZone - minutes * 12) / 2) % 360; + return (angleTime <= 180 ? angleTime : 360 - angleTime ) / 180 * Math.PI; } diff --git a/task/04-arrays-tasks.js b/task/04-arrays-tasks.js index ff3a4c019..cf745812f 100644 --- a/task/04-arrays-tasks.js +++ b/task/04-arrays-tasks.js @@ -23,7 +23,7 @@ * [0, 1, 2, 3, 4, 5], 5 => 5 */ function findElement(arr, value) { - throw new Error('Not implemented'); + return arr.indexOf(value); } /** @@ -38,7 +38,12 @@ function findElement(arr, value) { * 5 => [ 1, 3, 5, 7, 9 ] */ function generateOdds(len) { - throw new Error('Not implemented'); + let arr = new Array(len).fill(undefined); + let count = -1; + return arr.map( elem=> { + count += 2; + return count; + }); } @@ -54,7 +59,7 @@ function generateOdds(len) { * [] => [] */ function doubleArray(arr) { - throw new Error('Not implemented'); + return arr.concat(arr); } @@ -70,7 +75,9 @@ function doubleArray(arr) { * [] => [] */ function getArrayOfPositives(arr) { - throw new Error('Not implemented'); + return arr.filter( elem => { + return elem > 0; + }); } /** @@ -85,7 +92,9 @@ function getArrayOfPositives(arr) { * [ 'cat, 'dog', 'raccon' ] => [ 'cat', 'dog', 'racoon' ] */ function getArrayOfStrings(arr) { - throw new Error('Not implemented'); + return arr.filter( elem =>{ + return typeof elem === "string"; + }); } /** @@ -102,7 +111,9 @@ function getArrayOfStrings(arr) { * [ false, 0, NaN, '', undefined ] => [ ] */ function removeFalsyValues(arr) { - throw new Error('Not implemented'); + return arr.filter( elem => { + if (elem) return elem; + }); } /** @@ -116,7 +127,7 @@ function removeFalsyValues(arr) { * [ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ] => [ 'A', 'B', 'C', 'D', 'E', 'F', 'G' ] */ function getUpperCaseStrings(arr) { - throw new Error('Not implemented'); + return arr.map( elem => elem.toUpperCase()); } @@ -131,7 +142,9 @@ function getUpperCaseStrings(arr) { * [ 'angular', 'react', 'ember' ] => [ 7, 5, 5 ] */ function getStringsLength(arr) { - throw new Error('Not implemented'); + return arr.map( elem => { + return elem.length; + }); } /** @@ -146,7 +159,7 @@ function getStringsLength(arr) { * [ 1, 'b', 'c'], 0, 'x' => [ 'x', 1, 'b', 'c' ] */ function insertItem(arr, item, index) { - throw new Error('Not implemented'); + return arr.splice(index, 0, item); } /** @@ -160,7 +173,7 @@ function insertItem(arr, item, index) { * [ 'a', 'b', 'c', 'd'], 3 => [ 'a', 'b', 'c' ] */ function getHead(arr, n) { - throw new Error('Not implemented'); + return arr.slice(0, n); } @@ -175,7 +188,7 @@ function getHead(arr, n) { * [ 'a', 'b', 'c', 'd'], 3 => [ 'b', 'c', 'd' ] */ function getTail(arr, n) { - throw new Error('Not implemented'); + return arr.slice(-n); } @@ -200,7 +213,7 @@ function getTail(arr, n) { * +'30,31,32,33,34' */ function toCsvText(arr) { - throw new Error('Not implemented'); + return arr.join( "\n" ); } /** @@ -215,7 +228,9 @@ function toCsvText(arr) { * [ 10, 100, -1 ] => [ 100, 10000, 1 ] */ function toArrayOfSquares(arr) { - throw new Error('Not implemented'); + return arr.map( elem => { + return Math.pow(elem, 2); + }); } @@ -234,7 +249,12 @@ function toArrayOfSquares(arr) { * [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] => [ 1, 3, 6, 10, 15, 21, 28, 36, 45, 55 ] */ function getMovingSum(arr) { - throw new Error('Not implemented'); + let newArr = []; + arr.reduce(function(sum, current) { + newArr.push(sum + current); + return sum + current; + }, 0); + return newArr; } /** @@ -249,7 +269,9 @@ function getMovingSum(arr) { * [ "a" ] => [] */ function getSecondItems(arr) { - throw new Error('Not implemented'); + return arr.filter( elem => { + return arr.indexOf(elem) % 2 === 1; + }); } @@ -268,7 +290,13 @@ function getSecondItems(arr) { * [ 1,2,3,4,5 ] => [ 1, 2,2, 3,3,3, 4,4,4,4, 5,5,5,5,5 ] */ function propagateItemsByPositionIndex(arr) { - throw new Error('Not implemented'); + let newArr = []; + arr.map(elem => { + let position = arr.indexOf(elem) + 1; + let helpArr = new Array(position).fill(elem); + newArr = newArr.concat(helpArr); + }); + return newArr; } @@ -286,7 +314,9 @@ function propagateItemsByPositionIndex(arr) { * [ 10, 10, 10, 10 ] => [ 10, 10, 10 ] */ function get3TopItems(arr) { - throw new Error('Not implemented'); + return arr.sort( (a, b) => { + return b-a; + }).slice(0,3); } @@ -304,7 +334,14 @@ function get3TopItems(arr) { * [ 1, '2' ] => 1 */ function getPositivesCount(arr) { - throw new Error('Not implemented'); + let countPositive = 0; + arr.map( elem => { + if (typeof elem === "number"){ + if (elem > 0) countPositive ++; + } + return elem; + }) + return countPositive; } /** @@ -321,7 +358,10 @@ function getPositivesCount(arr) { * [ 'one','one','one','zero' ] => [ 'zero','one','one','one' ] */ function sortDigitNamesByNumericOrder(arr) { - throw new Error('Not implemented'); + let numbersArr = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]; + return arr.sort((a, b)=>{ + return numbersArr.indexOf(a) - numbersArr.indexOf(b); + }); } /** @@ -337,8 +377,10 @@ function sortDigitNamesByNumericOrder(arr) { * [ 1, 10, 100, 1000 ] => 1111 */ function getItemsSum(arr) { - throw new Error('Not implemented'); -} + return arr.reduce((sum, cur)=>{ + return sum+cur; + }, 0); +}; /** * Returns the number of all falsy value in the specified array @@ -353,8 +395,11 @@ function getItemsSum(arr) { * [ null, undefined, NaN, false, 0, '' ] => 6 */ function getFalsyValuesCount(arr) { - throw new Error('Not implemented'); -} + return arr.reduce((sum, cur)=>{ + if (!cur) return sum += 1; + else return sum; + }, 0); +}; /** * Returns a number of all occurences of the specified item in an array @@ -371,8 +416,11 @@ function getFalsyValuesCount(arr) { * [ true, 0, 1, 'true' ], true => 1 */ function findAllOccurences(arr, item) { - throw new Error('Not implemented'); -} + return arr.reduce((sum, cur)=>{ + if (cur === item) return sum += 1; + else return sum; + }, 0); +}; /** * Concatenates all elements from specified array into single string with ',' delimeter @@ -386,7 +434,7 @@ function findAllOccurences(arr, item) { * ['rock', 'paper', 'scissors'] => 'rock,paper,scissors' */ function toStringList(arr) { - throw new Error('Not implemented'); + return arr.join(","); } @@ -415,7 +463,11 @@ function toStringList(arr) { * { country: 'Russia', city: 'Saint Petersburg' } */ function sortCitiesArray(arr) { - throw new Error('Not implemented'); + return arr.sort((a, b)=>{ + if(a.country < b.country) { return -1; } + if(a.country > b.country) { return 1; } + if(a.city < b.city) { return -1; } + }); } /** @@ -437,7 +489,15 @@ function sortCitiesArray(arr) { * [0,0,0,0,1]] */ function getIdentityMatrix(n) { - throw new Error('Not implemented'); + let finalMatrix = new Array(n).fill(undefined); + let countRow = 0; + finalMatrix = finalMatrix.map( elem => { + let newArr = new Array(n).fill(0); + newArr[countRow] = 1; + countRow++; + return newArr; + }); + return finalMatrix; } /** @@ -454,7 +514,12 @@ function getIdentityMatrix(n) { * 3, 3 => [ 3 ] */ function getIntervalArray(start, end) { - throw new Error('Not implemented'); + let newArr = new Array(end - start + 1).fill(undefined); + let count = start - 1; + return newArr.map( elem => { + count++; + return count; + }); } /** @@ -469,9 +534,15 @@ function getIntervalArray(start, end) { * [ 1, 1, 2, 2, 3, 3, 4, 4] => [ 1, 2, 3, 4] */ function distinct(arr) { - throw new Error('Not implemented'); + let newArr = []; + arr.reduce((sum, elem) => { + if (!newArr.includes(elem)) { + newArr.push(elem); + }}, 0); + return newArr; } + /** * Groups elements of the specified array by key. * Returns multimap of keys extracted from array elements via keySelector callback @@ -503,7 +574,17 @@ function distinct(arr) { * } */ function group(array, keySelector, valueSelector) { - throw new Error('Not implemented'); + const map = new Map(); + array.map( (item) => { + const key = keySelector(item); + const value = valueSelector(item); + const collection = map.get(key); + if (!collection) { + map.set(key, [value]); + } else { + collection.push(value); + }}); + return map; } @@ -519,7 +600,7 @@ function group(array, keySelector, valueSelector) { * ['one','two','three'], x=>x.split('') => ['o','n','e','t','w','o','t','h','r','e','e'] */ function selectMany(arr, childrenSelector) { - throw new Error('Not implemented'); + return arr.flatMap(childrenSelector); } @@ -536,7 +617,10 @@ function selectMany(arr, childrenSelector) { * [[[ 1, 2, 3]]], [ 0, 0, 1 ] => 2 (arr[0][0][1]) */ function getElementByIndexes(arr, indexes) { - throw new Error('Not implemented'); + indexes.map(index => { + arr = arr[index]; + }); + return arr; } @@ -559,7 +643,21 @@ function getElementByIndexes(arr, indexes) { * */ function swapHeadAndTail(arr) { - throw new Error('Not implemented'); + const arrLength = arr.length; + if (arrLength === 1) return arr; + if (arrLength % 2 != 0){ + let newArr = []; + newArr=newArr.concat(arr.slice(Math.round(arrLength / 2))); + newArr.push(arr[Math.floor(arrLength / 2)]); + newArr=newArr.concat(arr.slice(0, Math.floor(arrLength / 2))); + return newArr; + } + else { + let newArr = []; + newArr= newArr.concat(arr.slice(arrLength / 2)); + newArr=newArr.concat(arr.slice(0, arrLength / 2)); + return newArr; + } } diff --git a/task/07-yield-tasks.js b/task/07-yield-tasks.js index a2369790a..9e88bf9b7 100644 --- a/task/07-yield-tasks.js +++ b/task/07-yield-tasks.js @@ -129,11 +129,31 @@ function* mergeSortedSequences(source1, source2) { throw new Error('Not implemented'); } +/** + * Resolve Promises and take values step by step. + * + * @params {Iterable.} generator + * @return {Promise} Promise with value returned via return + * + * @example + * async((function*() { + * var a = yield new Promise((resolve)=> setTimeout(()=>resolve(5))); + * var b = yield Promise.resolve(6); + * return a + b; + * }).then(value=>console.log(value)) => 11 + * + * Most popular implementation of the logic in npm https://www.npmjs.com/package/co + */ +function async(generator) { + throw new Error('Not implemented'); +} + module.exports = { get99BottlesOfBeer: get99BottlesOfBeer, getFibonacciSequence: getFibonacciSequence, depthTraversalTree: depthTraversalTree, breadthTraversalTree: breadthTraversalTree, - mergeSortedSequences: mergeSortedSequences + mergeSortedSequences: mergeSortedSequences, + async : async }; diff --git a/test/03-date-tests.js b/test/03-date-tests.js index 8423865b0..62932f922 100644 --- a/test/03-date-tests.js +++ b/test/03-date-tests.js @@ -83,6 +83,10 @@ describe('03-date-tasks', function() { startDate: new Date(2000,1,1,10,0,0), endDate: new Date(2000,1,1,15,20,10,453), expected: '05:20:10.453' + }, { + startDate: new Date(2000,1,1,10,0,0), + endDate: new Date(2000,1,2,15,20,10,453), + expected: '29:20:10.453' } ].forEach(data => { assert.equal( diff --git a/test/06-conditions-n-loops-tests.js b/test/06-conditions-n-loops-tests.js index 93ffa3eee..4d9ffe701 100644 --- a/test/06-conditions-n-loops-tests.js +++ b/test/06-conditions-n-loops-tests.js @@ -63,7 +63,8 @@ describe('06-conditions-n-loops-tasks', function() { [ { n: 1, expected: 1 }, { n: 5, expected: 120 }, - { n: 10, expected: 3628800 } + { n: 10, expected: 3628800 }, + { n: 41758, expected: Number.POSITIVE_INFINITY } ].forEach(data => { var actual = tasks.getFactorial(data.n); assert.equal( diff --git a/test/07-yield-tests.js b/test/07-yield-tests.js index a38c53e3d..62005cfac 100644 --- a/test/07-yield-tests.js +++ b/test/07-yield-tests.js @@ -316,6 +316,7 @@ describe('07-yield-tasks', function() { }); it.optional('depthTraversalTree should process a wide tree', () => { + this.timeout(30000); var root = createWideTree(); var index = 1; for(let node of tasks.depthTraversalTree(root)) { @@ -446,4 +447,62 @@ describe('07-yield-tasks', function() { assert.equal(count, ITEMS_COUNT); }); + + it.optional('async should resolve Promises and take values step by step', () => { + return new Promise((resolve, reject)=> { + tasks.async(function*() { + let a = yield new Promise((resolve)=> setTimeout(()=>resolve(5), 100)), + b = yield Promise.resolve(6); + assert.equal(a, 5); + assert.equal(b, 6); + + return yield new Promise((resolve)=> resolve(a + b)); + }).then(value=> { + try { + assert.equal(value, 11); + resolve() + } catch (err) { + reject(err); + } + }, (err)=> { + reject(err); + }); + }); + }); + + it.optional('async should resolve Promises and take values step by step', () => { + return new Promise((resolve, reject)=> { + tasks.async(function*() { + let a = yield new Promise((resolve)=> setTimeout(()=>resolve(5), 100)), + b = yield Promise.resolve(6), + c = yield Promise.resolve(6); + assert.equal(a, 5); + assert.equal(b, 6); + assert.equal(c, 6); + return yield new Promise((resolve)=> resolve(a + b + c)); + }).then(value=> { + try { + assert.equal(value, 17); + resolve() + } catch (err) { + reject(err); + } + }, (err)=> { + reject(err); + }); + }); + }); + + it.optional('async should handle exception during generator work', () => { + return new Promise((resolve, reject)=> { + tasks.async(function*() { + yield new Promise(()=> {throw new Error("test error");}); + }).then(()=> { + reject(); + }, (err)=> { + if (err.message === 'test error') resolve(); + else reject(err); + }); + }); + }); }); diff --git a/test/09-functions-n-closures-tests.js b/test/09-functions-n-closures-tests.js index 595713a90..7b5a0dbf8 100644 --- a/test/09-functions-n-closures-tests.js +++ b/test/09-functions-n-closures-tests.js @@ -88,6 +88,20 @@ describe('09-functions-n-closures-tasks', function() { }); + it.optional('retry method should throw an error when attemps are end', () => { + var maxAttemps = 3; + + var fn = function() { + throw new Error("fulyError"); + } + + assert.throws(tasks.retry(fn, maxAttemps), function (err) { + assert.equal(err.message, "fulyError", "retry method should throw an initial error"); + return true; + }); + }); + + it.optional('logger method should log start and end of call of the standard js function', () => { var log = ''; diff --git a/test/10-katas-1-tests.js b/test/10-katas-1-tests.js index 0d9023da6..bf420882e 100644 --- a/test/10-katas-1-tests.js +++ b/test/10-katas-1-tests.js @@ -162,6 +162,8 @@ describe('10-katas-1-tasks', function() { [1,3], [2,3], [1,4], [2,4], [1,5], [2,5] ],[ [1,1], [1,2], [2,3], [2,5], [2,6], [3,6], [5,6], [6,6] + ],[ + [1,2], [2,1], [2,2] ] ].forEach(data => { var actual = tasks.canDominoesMakeRow(data);