diff --git a/index.html b/index.html new file mode 100644 index 000000000..abb838a81 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + + LAB | JS Functions & Arrays + + + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 000000000..7a6863d83 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "lab-javascript-functions-and-arrays", + "version": "1.0.0", + "license": "UNLICENSED", + "scripts": { + "test": "jest", + "test:watch": "jest --watchAll --verbose=false" + }, + "devDependencies": { + "jest": "^26.6.3", + "jest-html-reporter": "^3.3.0", + "jest-junit": "^12.0.0" + }, + "jest": { + "reporters": [ + "default", + [ + "./node_modules/jest-html-reporter", + { + "pageTitle": "Lab Solution", + "outputPath": "lab-solution.html", + "includeFailureMsg": true, + "includeConsoleLog": true + } + ] + ] + }, + "jest-junit": { + "suiteNameTemplate": "{filepath}", + "classNameTemplate": "{classname}", + "titleTemplate": "{title}" + }, + "prettier": { + "printWidth": 120, + "singleQuote": true, + "trailingComma": "none" + } + } \ No newline at end of file diff --git a/src/functions-and-arrays.js b/src/functions-and-arrays.js index 3a7dbec41..66604a746 100644 --- a/src/functions-and-arrays.js +++ b/src/functions-and-arrays.js @@ -1,41 +1,89 @@ // Iteration #1: Find the maximum -function maxOfTwoNumbers() {} - - +function maxOfTwoNumbers(num1, num2) { + return Math.max(num1, num2); +} // Iteration #2: Find longest word const words = ['mystery', 'brother', 'aviator', 'crocodile', 'pearl', 'orchard', 'crackpot']; -function findLongestWord() {} - - +function findLongestWord(words) { + if (words.length === 0) { + return null; + } + const lengths = words.map((word) => word.length); + const maxLength = Math.max(...lengths); + for (let i = 0; i < words.length; i++) { + if (words[i].length === maxLength) { + return words[i]; + } + } +} // Iteration #3: Calculate the sum const numbers = [6, 12, 1, 18, 13, 16, 2, 1, 8, 10]; -function sumNumbers() {} - - +function sumNumbers(numbers) { + let currentSum = 0; + for (let i = 0; i < numbers.length; i++) { + currentSum += numbers[i]; + } + return currentSum; +} // Iteration #3.1 Bonus: -function sum() {} - +function getNumericValue(element) { + const type = typeof element; + if (type === 'number') { + return element; + } else if (type === 'string') { + return element.length; + } else if (element === true) { + return 1; + } else if (element === false) { + return 0; + } else { + throw new Error("Unsupported data type sir or ma'am"); + } +} +function sum(mixedArray) { + const numbers = mixedArray.map(getNumericValue); + return sumNumbers(numbers); +} // Iteration #4: Calculate the average // Level 1: Array of numbers const numbersAvg = [2, 6, 9, 10, 7, 4, 1, 9]; -function averageNumbers() {} +function averageNumbers(numbers) { + const n = numbers.length; + if (n === 0) { + return null; + } + const sum = sumNumbers(numbers); + return sum / n; +} // Level 2: Array of strings const wordsArr = ['seat', 'correspond', 'linen', 'motif', 'hole', 'smell', 'smart', 'chaos', 'fuel', 'palace']; -function averageWordLength() { } +function averageWordLength(words) { + const lengths = words.map((word) => word.length); + const n = lengths.length; + if (n === 0) { + return null; + } + + const sum = sumNumbers(lengths); + return sum / n; +} // Bonus - Iteration #4.1 -function avg() {} +function avg(mixedArray) { + const numbers = mixedArray.map(getNumericValue); + return averageNumbers(numbers); +} // Iteration #5: Unique arrays const wordsUnique = [ @@ -52,16 +100,30 @@ const wordsUnique = [ 'bring' ]; -function uniquifyArray() {} - - +function uniquifyArray(words) { + if (words.length === 0) { + return null; + } + const uniqueArray = []; + words.forEach((word) => { + const isRepeated = uniqueArray.some((previousWord) => previousWord === word); + if (!isRepeated) { + uniqueArray.push(word); + } + }); + return uniqueArray; +} // Iteration #6: Find elements const wordsFind = ['machine', 'subset', 'trouble', 'starting', 'matter', 'eating', 'truth', 'disobedience']; -function doesWordExist() {} - - +// Declare una función llamada doesWordExist que recibirá un array de palabras como un argumento, y una palabra a buscar como el otro. Devuelve true si existe, en caso contrario, devuelve false. No utilice indexOf para esta función. +function doesWordExist(words, wordToFind) { + if (words.length === 0) { + return null; + } + return words.some((word) => word === wordToFind); +} // Iteration #7: Count repetition const wordsCount = [ @@ -78,9 +140,9 @@ const wordsCount = [ 'matter' ]; -function howManyTimes() {} - - +function howManyTimes(words, wordToFind) { + return words.filter((word) => word === wordToFind).length; +} // Iteration #8: Bonus const matrix = [ @@ -106,10 +168,100 @@ const matrix = [ [1, 70, 54, 71, 83, 51, 54, 69, 16, 92, 33, 48, 61, 43, 52, 1, 89, 19, 67, 48] ]; -function greatestProduct() {} +const ADJACENT_NUMBERS = 4; +function multiplyNums(nums) { + let currentProduct = 1; + for (let i = 0; i < nums.length; i++) { + currentProduct = currentProduct * nums[i]; + } + return currentProduct; +} +function getProductsOfLines(matrix, lineType) { + // Get a list of lines, each one represented by a list of coordinates: + const sideLength = matrix.length; + const getLineIndex = { + horizontal: (row, col) => row, + vertical: (row, col) => col, + diagonal_ltr: (row, col) => row + col, + diagonal_rtl: (row, col) => sideLength + col - row - 1 + }; + + const lines = []; + for (let row = 0; row < sideLength; row++) { + for (let col = 0; col < sideLength; col++) { + const lineIndex = getLineIndex[lineType](row, col); + lines[lineIndex] ??= []; + lines[lineIndex].push([row, col]); + } + } + + // Get products of the lines: + const products = []; + const lineToProduct = (lineCoordinates) => { + const valuesInLine = lineCoordinates.map(([row, col]) => matrix[row][col]); + const product = multiplyNums(valuesInLine); + products.push(product); + }; + + lines.forEach((lineCoordinates) => { + const lineLength = lineCoordinates.length; + + // If the line is short enough, we can just process it all (as in some diagonals): + if (lineLength <= ADJACENT_NUMBERS) { + lineToProduct(lineCoordinates); + return; + } + + // If the line is longer than ADJACENT_NUMBERS, we need to calculate every chunk separately: + const lastSliceIndex = lineLength - ADJACENT_NUMBERS; + for (let sliceIndex = 0; sliceIndex <= lastSliceIndex; sliceIndex++) { + const sliceEndIndex = sliceIndex + ADJACENT_NUMBERS; + const sliceCoordinates = lineCoordinates.slice(sliceIndex, sliceEndIndex); + lineToProduct(sliceCoordinates); + } + }); + + return products; +} + +function greatestProduct(matrix) { + const verticalProducts = getProductsOfLines(matrix, 'vertical'); + const horizontProducts = getProductsOfLines(matrix, 'horizontal'); + + return Math.max(...verticalProducts, ...horizontProducts); +} + +function greatestProductOfDiagonals(matrix) { + const diagonal1Products = getProductsOfLines(matrix, 'diagonal_ltr'); + const diagonal2Products = getProductsOfLines(matrix, 'diagonal_rtl'); + + return Math.max(...diagonal1Products, ...diagonal2Products); +} + +// Diagonal coordinates ltr: +// [ [0, 0] ], +// [ [1, 0], [0, 1] ], +// [ [2, 0], [1, 1], [0, 2] ], +// [ [3, 0], [2, 1], [1, 2], [0, 3] ], +// [ [4, 0], [3, 1], [2, 2], [1, 3], [0, 4] ], +// [ [4, 1], [3, 2], [2, 3], [1, 4] ], +// [ [4, 2], [3, 3], [2, 4] ], +// [ [4, 3], [3, 4] ], +// [ [4, 4] ], + +// Diagonal coordinates rtl: +// [ [0, 4] ], +// [ [0, 3], [1, 4] ], +// [ [0, 2], [1, 3], [2, 4] ], +// [ [0, 1], [1, 2], [2, 3], [3, 4] ], +// [ [0, 0], [1, 1], [2, 2], [3, 3], [4, 4] ], +// [ [1, 0], [2, 1], [3, 2], [4, 3] ], +// [ [2, 0], [3, 1], [4, 2] ], +// [ [3, 0], [4, 1] ], +// [ [4, 0] ], // The following is required to make unit tests work. /* Environment setup. Do not modify the below code. */ @@ -127,4 +279,4 @@ if (typeof module !== 'undefined') { howManyTimes, greatestProduct }; -} +} \ No newline at end of file diff --git a/tests/functions-and-arrays.spec.js b/tests/functions-and-arrays.spec.js index 21ec1130c..ca7fd9f08 100644 --- a/tests/functions-and-arrays.spec.js +++ b/tests/functions-and-arrays.spec.js @@ -1,3 +1,17 @@ +const { + maxOfTwoNumbers, + findLongestWord, + sumNumbers, + sum, + averageNumbers, + averageWordLength, + avg, + uniquifyArray, + doesWordExist, + howManyTimes, + greatestProduct +} = require('./../src/functions-and-arrays'); + const shuffle = (currentArray) => { const array = [...currentArray]; let counter = array.length; @@ -12,47 +26,45 @@ const shuffle = (currentArray) => { return array; }; - - describe('Find the maximum', () => { - it('should declare a function named maxOfTwoNumbers', () => { + test('should declare a function named maxOfTwoNumbers', () => { expect(typeof maxOfTwoNumbers).toBe('function'); }); - it('should return greater of two arguments - if the first argument greater', () => { + test('should return greater of two arguments - if the first argument greater', () => { expect(maxOfTwoNumbers(2, 1)).toBe(2); expect(maxOfTwoNumbers(5, -7)).toBe(5); }); - it('should return greater of two arguments - if the second argument greater', () => { + test('should return greater of two arguments - if the second argument greater', () => { expect(maxOfTwoNumbers(1, 3)).toBe(3); expect(maxOfTwoNumbers(-5, 3)).toBe(3); }); - it('should return either arguments - if both arguments are equal', () => { + test('should return either arguments - if both arguments are equal', () => { expect(maxOfTwoNumbers(4, 4)).toBe(4); }); }); describe('Find the longest word', () => { - it('should declare a function named findLongestWord', () => { + test('should declare a function named findLongestWord', () => { expect(typeof findLongestWord).toBe('function'); }); - it('should return null when called with an empty array', () => { + test('should return null when called with an empty array', () => { expect(findLongestWord([])).toBe(null); }); - it('should return the word when called with a single-word array', () => { + test('should return the word when called with a single-word array', () => { expect(findLongestWord(['foo'])).toBe('foo'); }); - it('should return the first occurrence of the word when longest have multiple occurrences ', () => { + test('should return the first occurrence of the word when longest have multiple occurrences ', () => { expect(findLongestWord(['foo', 'bar'])).toBe('foo'); expect(findLongestWord(['bar', 'foo'])).toBe('bar'); }); - it('should return the longest occurrence when it has multiple words', () => { + test('should return the longest occurrence when it has multiple words', () => { let words = ['a', 'zab', '12abc', '$$abcd', 'abcde', 'ironhack']; for (let i = 0; i < 10; i++) { words = shuffle(words); @@ -62,106 +74,104 @@ describe('Find the longest word', () => { }); describe('Calculate the sum of array of numbers', () => { - it('should declare a function named sumNumbers', () => { + test('should declare a function named sumNumbers', () => { expect(typeof sumNumbers).toBe('function'); }); - it('should return zero if receives an empty array when called', () => { + test('should return zero if receives an empty array when called', () => { expect(sumNumbers([])).toBe(0); }); - it('should return the sum with one number array', () => { + test('should return the sum with one number array', () => { expect(sumNumbers([4])).toBe(4); }); - it('should return zero if all elements are zero', () => { + test('should return zero if all elements are zero', () => { expect(sumNumbers([0, 0, 0, 0, 0])).toBe(0); }); - it('should return the sum when passed array of numbers', () => { + test('should return the sum when passed array of numbers', () => { expect(sumNumbers([10, 5, 4, 32, 8])).toBe(59); }); }); describe('Bonus: Calculate the sum', () => { - it('should declare a function named sum', () => { + test('should declare a function named sum', () => { expect(typeof sum).toBe('function'); }); - it('should return zero if receives an empty array when called', () => { + test('should return zero if receives an empty array when called', () => { expect(sum([])).toBe(0); }); - it('should return the sum with one number array', () => { + test('should return the sum with one number array', () => { expect(sum([4])).toBe(4); }); - it('should return zero if all elements are zero', () => { + test('should return zero if all elements are zero', () => { expect(sum([0, 0, 0, 0, 0])).toBe(0); }); - it('should return the sum when passed array of numbers', () => { + test('should return the sum when passed array of numbers', () => { expect(sum([10, 5, 4, 32, 8])).toBe(59); }); - it('should return the sum when passed array of strings', () => { + test('should return the sum when passed array of strings', () => { expect(sum(['ana', 'marco', 'nicolas', 'tania', 'ptwd'])).toBe(24); }); - it('should return the sum when passed array of mixed strings and numbers - ', () => { + test('should return the sum when passed array of mixed strings and numbers - ', () => { expect(sum([6, 12, 'miami', 1, 'barca', '200', 'lisboa', 8, 10])).toBe(56); }); - - it('should return the sum when passed array of mixed strings, numbers and booleans - ', () => { + test('should return the sum when passed array of mixed strings, numbers and booleans - ', () => { // false is counted as 0 expect(sum([6, 12, 'miami', 1, 'barca', '200', 'lisboa', 8, false])).toBe(46); // true is counted as 1 expect(sum([6, 12, 'miami', 1, 'barca', '200', 'lisboa', 8, true])).toBe(47); }); - - it('should throw an error when unsupported data type (object or array) present in the array', () => { - expect(() => sum([6, 12, 'miami', 1, 'barca', '200', 'lisboa', 8, [], {}])).toThrow(); + test('should throw an error when unsupported data type (object or array) present in the array', () => { + expect(() => sum([6, 12, 'miami', 1, 'barca', '200', 'lisboa', 8, [], {}])).toThrow( + new Error("Unsupported data type sir or ma'am") + ); }); - }); describe('Calculate the average of an array of numbers', () => { - it('should declare a function named averageNumbers', () => { + test('should declare a function named averageNumbers', () => { expect(typeof averageNumbers).toBe('function'); }); - it('should return null if receives an empty array when called', () => { + test('should return null if receives an empty array when called', () => { expect(averageNumbers([])).toBe(null); }); - it('should return the average of a one-element array', () => { + test('should return the average of a one-element array', () => { expect(averageNumbers([9])).toBe(9); }); - it('should return the average even with negative values', () => { + test('should return the average even with negative values', () => { expect(averageNumbers([9, -3, -4, 6])).toBe(2); }); - it('should return the average of the array', () => { + test('should return the average of the array', () => { expect(averageNumbers([9, 10, 82, 92, 32, 102, 58])).toBe(55); }); - }); describe('Calculate the average of an array of strings', () => { - it('should declare a function named averageWordLength', () => { + test('should declare a function named averageWordLength', () => { expect(typeof averageWordLength).toBe('function'); }); - it('should return null if receives an empty array when called', () => { + test('should return null if receives an empty array when called', () => { expect(averageWordLength([])).toBe(null); }); - it('should return the average of a one-element array', () => { + test('should return the average of a one-element array', () => { expect(averageWordLength(['ironhack'])).toBe(8); }); - it('should return the average of a the array', () => { + test('should return the average of a the array', () => { expect( averageWordLength(['Ironhack', 'Madrid', 'Barcelona', 'Paris', 'Miami', 'Mexico', 'Berlin', 'Programmers']) ).toBe(7); @@ -169,15 +179,15 @@ describe('Calculate the average of an array of strings', () => { }); describe('Bonus: Calculate the average of a mixed elements array', () => { - it('should declare a function named avg', () => { + test('should declare a function named avg', () => { expect(typeof avg).toBe('function'); }); - it('should return null if receives an empty array when called', () => { + test('should return null if receives an empty array when called', () => { expect(avg([])).toBe(null); }); - it('should return the average of the array', () => { + test('should return the average of the array', () => { // false is counted as 0 expect(avg([6, 12, 'miami', 1, 'barca', '200', 'lisboa', 8, false])).toBe(46/9); // true is counted as 1 @@ -186,23 +196,23 @@ describe('Bonus: Calculate the average of a mixed elements array', () => { }); describe('Unique array', () => { - it('should declare a function named uniquifyArray', () => { + test('should declare a function named uniquifyArray', () => { expect(typeof uniquifyArray).toBe('function'); }); - it('should return null if receives an empty array when called', () => { + test('should return null if receives an empty array when called', () => { expect(uniquifyArray([])).toEqual(null); }); - it('should return the correct uniqified array when an array of the same elements passed as argument', () => { + test('should return the correct uniqified array when an array of the same elements passed as argument', () => { expect(uniquifyArray(['Ironhack', 'Ironhack', 'Ironhack'])).toEqual(['Ironhack']); }); - it('should return the same array when no element is repeated', () => { + test('should return the same array when no element is repeated', () => { expect(uniquifyArray(['Cat', 'Dog', 'Cow'])).toEqual(['Cat', 'Dog', 'Cow']); }); - it('should return the uniquified array', () => { + test('should return the uniquified array', () => { expect( uniquifyArray(['iPhone', 'Samsung', 'Android', 'iOS', 'iPhone', 'Samsung', 'Nokia', 'Blackberry', 'Android']) ).toEqual(['iPhone', 'Samsung', 'Android', 'iOS', 'Nokia', 'Blackberry']); @@ -210,45 +220,45 @@ describe('Unique array', () => { }); describe('Find elements', () => { - it('should declare a function named doesWordExist', () => { + test('should declare a function named doesWordExist', () => { expect(typeof doesWordExist).toBe('function'); }); - it('should return null if receives an empty array when called', () => { + test('should return null if receives an empty array when called', () => { expect(doesWordExist([])).toBe(null); }); - it('should return true if the word we are looking for is the only one in the array', () => { + test('should return true if the word we are looking for is the only one in the array', () => { expect(doesWordExist(['machine'], 'machine')).toBe(true); }); - it('should return false if the word we are looking for is not in the array', () => { + test('should return false if the word we are looking for is not in the array', () => { expect(doesWordExist(['machine', 'poison', 'eat', 'apple', 'horse'], 'ratatouille')).toBe(false); }); - it('should return true if the word we are looking for is in the array', () => { + test('should return true if the word we are looking for is in the array', () => { expect(doesWordExist(['pizza', 'sandwich', 'snack', 'soda', 'book', 'computer'], 'book')).toBe(true); }); }); describe('Count repetition', () => { - it('should declare a function named howManyTimes', () => { + test('should declare a function named howManyTimes', () => { expect(typeof howManyTimes).toBe('function'); }); - it('should return 0 (zero) if receives an empty array when called', () => { + test('should return 0 (zero) if receives an empty array when called', () => { expect(howManyTimes([])).toBe(0); }); - it('should return 1 (one) when the word appears only one time in the array', () => { + test('should return 1 (one) when the word appears only one time in the array', () => { expect(howManyTimes(['basketball', 'football', 'tennis'], 'tennis')).toBe(1); }); - it("should return 0 (zero) when the word doesn't appear in the array", () => { + test("should return 0 (zero) when the word doesn't appear in the array", () => { expect(howManyTimes(['basketball', 'football', 'tennis'], 'rugby')).toBe(0); }); - it('should return 5 (five) when the word appears 5 times in the array', () => { + test('should return 5 (five) when the word appears 5 times in the array', () => { expect( howManyTimes( [ @@ -271,11 +281,11 @@ describe('Count repetition', () => { }); describe('Bonus Quest - greatestProduct', () => { - it('should declare a function named greatestProduct', () => { + test('should declare a function named greatestProduct', () => { expect(typeof greatestProduct).toBe('function'); }); - it('should return 1 (one) when all numbers of the arrays are 1', () => { + test('should return 1 (one) when all numbers of the arrays are 1', () => { let matrix = [ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], @@ -301,7 +311,7 @@ describe('Bonus Quest - greatestProduct', () => { expect(greatestProduct(matrix)).toBe(1); }); - it('should return 16 when all the numbers of the arrays are 2', () => { + test('should return 16 when all the numbers of the arrays are 2', () => { let matrix = [ [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], @@ -326,4 +336,4 @@ describe('Bonus Quest - greatestProduct', () => { ]; expect(greatestProduct(matrix)).toBe(16); }); -}); +}); \ No newline at end of file