diff --git a/Sprint-1/fix/median.js b/Sprint-1/fix/median.js index b22590bc6..b91f22644 100644 --- a/Sprint-1/fix/median.js +++ b/Sprint-1/fix/median.js @@ -6,9 +6,28 @@ // or 'list' has mixed values (the function is expected to sort only numbers). function calculateMedian(list) { - const middleIndex = Math.floor(list.length / 2); - const median = list.splice(middleIndex, 1)[0]; - return median; + // check if its array and if has content + if (!Array.isArray(list) || list.length === 0) return null; + + // filter for valid numbers + const numbers = list.filter( + (item) => typeof item === "number" && !isNaN(item) + ); + + // if there's no numbers returns null + if (numbers.length === 0) return null; + + // sort without modifying the original + const sorted = [...numbers].sort((a, b) => a - b); + + // calculates median + const middle = Math.floor(sorted.length / 2); + + if (sorted.length % 2 === 0) { + return (sorted[middle - 1] + sorted[middle]) / 2; + } else { + return sorted[middle]; + } } module.exports = calculateMedian; diff --git a/Sprint-1/fix/package.json b/Sprint-1/fix/package.json new file mode 100644 index 000000000..bdef60590 --- /dev/null +++ b/Sprint-1/fix/package.json @@ -0,0 +1,18 @@ +{ + "name": "fix", + "version": "1.0.0", + "description": "", + "main": "median.js", + "scripts": { + "test": "jest" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "test": "^3.3.0" + }, + "devDependencies": { + "jest": "^30.2.0" + } +} diff --git a/Sprint-1/implement/dedupe.js b/Sprint-1/implement/dedupe.js index 781e8718a..4143d984f 100644 --- a/Sprint-1/implement/dedupe.js +++ b/Sprint-1/implement/dedupe.js @@ -1 +1,27 @@ -function dedupe() {} +function dedupe(arr) { + if (!Array.isArray(arr) || arr.length === 0){ + return []; + } + const unique = [...new Set(arr)]; + return unique; +} + +console.log(dedupe([])); // [] +console.log(dedupe([1, 2, 3])); // [1, 2, 3] +console.log(dedupe([1, 2, 2, 3, 1])); // [1, 2, 3] +console.log(dedupe(["apple", "banana", "apple", "orange"])); // ["apple", "banana", "orange"] +console.log(dedupe(["apple", 3, 5, 5, 9, 7, "banana", "orange", "orange"])); // ["apple", 3, 5, 9, 7, "banana", "orange"] + +/*Given an empty array +When passed to the dedupe function +Then it should return an empty array*/ + +/*Given an array with no duplicates +When passed to the dedupe function +Then it should return a copy of the original array*/ + +/*Given an array with strings or numbers +When passed to the dedupe function +Then it should remove the duplicate values, preserving the first occurence of each element*/ + +module.exports = dedupe; diff --git a/Sprint-1/implement/dedupe.test.js b/Sprint-1/implement/dedupe.test.js index 23e0f8638..e456e1b28 100644 --- a/Sprint-1/implement/dedupe.test.js +++ b/Sprint-1/implement/dedupe.test.js @@ -1,4 +1,5 @@ const dedupe = require("./dedupe.js"); + /* Dedupe Array @@ -16,12 +17,21 @@ E.g. dedupe([1, 2, 1]) target output: [1, 2] // Given an empty array // When passed to the dedupe function // Then it should return an empty array -test.todo("given an empty array, it returns an empty array"); + +//test.todo("given an empty array, it returns an empty array"); +test("given an empty array, it returns an empty array", () => { + expect(dedupe([])).toEqual([]); +}) // Given an array with no duplicates // When passed to the dedupe function // Then it should return a copy of the original array - +test("given an array without duplicates, return the copy of original", () => { + expect(dedupe([1, 2, 5, 7, 9])).toEqual([1, 2, 5, 7, 9]); +}) // Given an array with strings or numbers // When passed to the dedupe function -// Then it should remove the duplicate values, preserving the first occurence of each element +// Then it should remove the duplicate values, preserving the first occurrence of each element +test("given an array with strings or numbers, return the original without the repeated values", () => { + expect(dedupe(["apple", "apple", "pear", 2, 6, 6, 10, 11, 11, "banana"])).toEqual(["apple", "pear", 2, 6, 10, 11, "banana"]); +}) \ No newline at end of file diff --git a/Sprint-1/implement/max.js b/Sprint-1/implement/max.js index 6dd76378e..35c1b084a 100644 --- a/Sprint-1/implement/max.js +++ b/Sprint-1/implement/max.js @@ -1,4 +1,22 @@ function findMax(elements) { + const onlyNumbers = elements.filter(x => typeof x === "number" && !isNaN(x)); + // empty array + if ( onlyNumbers.length === 0){ + return -Infinity; + // array with only one number + } if ( onlyNumbers.length === 1) { + return onlyNumbers[0]; + } + return Math.max(...onlyNumbers); } +console.log(findMax(["banana", 3, 20, -5, "apple", 3.5])); +console.log(findMax([10, -5, 20, -3])); // 20 (mixed positive/negative) +console.log(findMax([-10, -5, -3, -8])); // -3 (all negative) +console.log(findMax([1.5, 2.8, 1.2, 3.1])); // 3.1 (decimals) +console.log(findMax([233])); // 233 (single number) +console.log(findMax([])); // -Infinity (empty array) +console.log(findMax(["banana"])); // -Infinity (no valid numbers) + + module.exports = findMax; diff --git a/Sprint-1/implement/max.test.js b/Sprint-1/implement/max.test.js index 82f18fd88..4f590e28a 100644 --- a/Sprint-1/implement/max.test.js +++ b/Sprint-1/implement/max.test.js @@ -16,28 +16,49 @@ const findMax = require("./max.js"); // When passed to the max function // Then it should return -Infinity // Delete this test.todo and replace it with a test. -test.todo("given an empty array, returns -Infinity"); +// test.todo("given an empty array, returns -Infinity"); +test("given an empty array, returns -Infitiny", () => { + expect(findMax([])).toEqual(-Infinity); +}) // Given an array with one number // When passed to the max function // Then it should return that number +test("given an array with one number, returns that number", () => { + expect(findMax([1])).toEqual(1); +}) // Given an array with both positive and negative numbers // When passed to the max function // Then it should return the largest number overall +test("given an array with both positive and negative numbers, return the largest number overall", () => { + expect(findMax([-10, 3, 6, -1])).toEqual(6); +}) // Given an array with just negative numbers // When passed to the max function // Then it should return the closest one to zero +test("given an array with just negative numbers, return the closest one to zero", () => { + expect(findMax([-10, -1, -4, -15])).toEqual(-1); +}) // Given an array with decimal numbers // When passed to the max function // Then it should return the largest decimal number +test("given an array with decimal numbers, return the largest decimal number", () => { + expect(findMax([3.2, 4.9, 6.1, 2.9])).toEqual(6.1); +}) // Given an array with non-number values // When passed to the max function // Then it should return the max and ignore non-numeric values +test("given an array with non-number values, return the max and ignore non-numeric values", () => { + expect(findMax(["banana", 20, 5, "apple"])).toEqual(20); +}) // Given an array with only non-number values // When passed to the max function // Then it should return the least surprising value given how it behaves for all other inputs +test("given an array with only non-number values, return -Infinity", () => { + expect(findMax(["banana", "apple", "orange"])).toEqual(-Infinity); +}) \ No newline at end of file diff --git a/Sprint-1/implement/package.json b/Sprint-1/implement/package.json new file mode 100644 index 000000000..ff6f1e295 --- /dev/null +++ b/Sprint-1/implement/package.json @@ -0,0 +1,15 @@ +{ + "name": "implement", + "version": "1.0.0", + "description": "", + "main": "dedupe.js", + "scripts": { + "test": "jest" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "jest": "^30.2.0" + } +} diff --git a/Sprint-1/implement/sum.js b/Sprint-1/implement/sum.js index 9062aafe3..ef1565503 100644 --- a/Sprint-1/implement/sum.js +++ b/Sprint-1/implement/sum.js @@ -1,4 +1,23 @@ function sum(elements) { + // filter out only valid numbers from the input array + const onlyNumbers = elements.filter(x => typeof x === "number" && !isNaN(x)); + // empty array + if ( onlyNumbers.length === 0) { + return 0; + // array with only one number + } if ( onlyNumbers.length === 1) { + return onlyNumbers[0]; + } // take first element + sum of the rest untill it's just a number + return onlyNumbers[0] + sum(onlyNumbers.slice(1)); } +console.log(sum([-1, 5, 10, -1])); +console.log(sum(["banana", -1, 5, 10, -1, "pear"])); +console.log(sum([-1, -5, -10, -1])); +console.log(sum([1, 5, 10, 1])); +console.log(sum([])); +console.log(sum([10])); +console.log(sum(["banana"])); +console.log(sum([-1, 5.9, 10.4, -1.2])); + module.exports = sum; diff --git a/Sprint-1/implement/sum.test.js b/Sprint-1/implement/sum.test.js index dd0a090ca..c59680dc7 100644 --- a/Sprint-1/implement/sum.test.js +++ b/Sprint-1/implement/sum.test.js @@ -13,24 +13,40 @@ const sum = require("./sum.js"); // Given an empty array // When passed to the sum function // Then it should return 0 -test.todo("given an empty array, returns 0") - +//test.todo("given an empty array, returns 0") +test("given an empty array, return 0", () => { + expect(sum([])).toEqual(0); +}) // Given an array with just one number // When passed to the sum function // Then it should return that number +test("given an array with just one number, return that number", () => { + expect(sum([5])).toEqual(5); +}) // Given an array containing negative numbers // When passed to the sum function // Then it should still return the correct total sum - +test("given an array containing negative numbers, return the correct total sum", () => { + expect(sum([5, -10])).toEqual(-5); +}) // Given an array with decimal/float numbers // When passed to the sum function // Then it should return the correct total sum +test("given an array with decimal/float numbers, return the correct total sum", () => { + expect(sum([10.1, 10.2, 6.3])).toEqual(26.6); +}) // Given an array containing non-number values // When passed to the sum function // Then it should ignore the non-numerical values and return the sum of the numerical elements +test("given an array containing non-number values, return the sum of numerical elements", () => { + expect(sum(["banana", 5, "apple", 10])).toEqual(15); +}) // Given an array with only non-number values // When passed to the sum function // Then it should return the least surprising value given how it behaves for all other inputs +test("given an array with only non-number values, return lest surprising value, so 0", () => { + expect(sum(["banana", "apple"])).toEqual(0); +}) diff --git a/Sprint-1/refactor/includes.js b/Sprint-1/refactor/includes.js index 29dad81f0..034cc9a6c 100644 --- a/Sprint-1/refactor/includes.js +++ b/Sprint-1/refactor/includes.js @@ -1,8 +1,7 @@ // Refactor the implementation of includes to use a for...of loop function includes(list, target) { - for (let index = 0; index < list.length; index++) { - const element = list[index]; + for (const element of list) { if (element === target) { return true; } @@ -10,4 +9,15 @@ function includes(list, target) { return false; } +console.log(includes([1, 10, 15, 20], 6)); module.exports = includes; + +/*function includes(list, target) { + for (let index = 0; index < list.length; index++) { + const element = list[index]; + if (element === target) { + return true; + } + } + return false; +}*/ \ No newline at end of file diff --git a/Sprint-1/refactor/package.json b/Sprint-1/refactor/package.json new file mode 100644 index 000000000..a4b346ee3 --- /dev/null +++ b/Sprint-1/refactor/package.json @@ -0,0 +1,15 @@ +{ + "name": "refactor", + "version": "1.0.0", + "description": "", + "main": "includes.js", + "scripts": { + "test": "jest" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "jest": "^30.2.0" + } +} diff --git a/Sprint-1/stretch/aoc-2018-day1/solution.js b/Sprint-1/stretch/aoc-2018-day1/solution.js index e69de29bb..d3efa9a7f 100644 --- a/Sprint-1/stretch/aoc-2018-day1/solution.js +++ b/Sprint-1/stretch/aoc-2018-day1/solution.js @@ -0,0 +1,1026 @@ +const inputString = `+13 ++4 +-8 ++11 ++13 +-12 ++19 ++6 +-4 +-7 ++3 ++16 +-18 ++11 +-10 ++11 ++13 +-4 +-16 ++19 +-1 ++12 +-20 ++17 ++9 ++5 +-1 +-18 +-1 ++5 ++18 ++12 ++16 +-8 +-3 ++16 ++9 ++9 ++6 ++11 +-19 ++6 +-19 +-9 ++5 ++9 ++4 ++17 +-12 +-20 ++9 +-15 +-11 +-2 +-1 +-5 +-12 +-4 +-2 +-7 +-1 ++9 ++3 +-1 ++5 +-1 ++13 ++18 +-17 ++15 +-1 +-6 ++15 ++3 ++9 ++16 ++18 +-3 ++18 +-14 ++17 +-11 ++15 ++14 ++12 ++5 ++9 +-13 ++3 +-15 +-18 +-13 ++17 +-15 ++16 ++9 +-6 +-13 +-10 +-16 +-9 ++4 +-12 +-2 +-9 +-8 ++6 ++12 ++20 +-9 ++8 ++7 ++13 +-11 ++19 ++9 +-13 +-1 +-7 ++6 +-19 +-17 ++12 +-3 +-8 ++17 +-16 ++17 ++1 ++21 ++12 ++4 ++3 ++16 ++19 ++11 ++12 ++15 ++14 ++17 +-5 ++18 ++15 ++15 ++5 ++6 +-16 +-20 ++17 +-10 +-1 ++18 ++17 ++7 ++13 +-8 ++3 ++2 ++2 ++19 +-5 ++11 +-5 ++8 +-5 ++19 ++12 +-2 ++7 ++6 +-16 +-17 ++4 ++9 ++1 +-9 +-8 ++5 +-13 +-12 ++17 +-4 +-12 +-16 ++17 +-8 +-12 +-12 +-8 ++7 ++9 +-7 ++3 ++2 ++4 ++19 +-13 ++12 +-8 ++6 ++13 +-3 ++9 ++15 +-1 ++13 +-3 +-15 ++8 ++19 ++16 ++14 +-18 +-6 ++1 ++11 +-17 +-14 ++3 ++14 ++11 +-20 ++1 +-16 +-9 ++4 ++18 ++16 ++13 +-5 ++14 +-7 +-16 ++15 ++15 ++4 ++5 +-14 ++8 ++17 ++4 ++19 +-3 ++14 ++8 +-1 ++7 +-2 ++1 ++4 +-1 +-1 ++7 ++10 ++13 +-1 +-10 ++5 +-3 +-17 +-17 +-17 ++16 +-7 +-10 +-5 +-19 ++5 +-4 ++6 ++14 +-5 ++19 +-15 ++7 +-14 ++5 ++15 ++10 ++15 ++8 ++3 +-6 +-12 ++17 ++14 +-11 ++7 ++15 +-4 +-16 ++2 ++5 ++17 ++2 +-9 ++5 +-7 ++21 +-15 ++14 ++2 +-12 ++7 ++6 ++16 ++14 +-5 +-8 +-15 ++4 ++15 ++20 ++1 +-15 ++1 ++10 +-14 +-2 +-7 +-11 +-14 +-24 +-16 ++9 +-24 ++3 +-4 +-15 ++14 +-22 +-13 +-17 ++1 +-13 +-6 ++2 ++11 +-19 +-10 ++3 ++12 ++24 +-1 +-13 +-1 ++3 ++19 +-13 ++7 ++16 +-2 ++5 ++14 +-3 ++4 +-24 +-14 ++8 +-3 +-15 +-17 +-16 ++26 +-15 +-17 ++5 ++9 ++52 +-8 +-15 +-13 +-6 +-37 +-14 +-3 +-10 +-1 +-12 +-14 +-8 ++10 ++10 +-5 ++15 +-37 ++9 +-11 +-11 ++12 +-15 ++2 +-8 +-19 +-2 ++19 +-6 +-6 ++16 +-12 ++1 ++19 +-15 ++19 ++2 +-17 +-3 +-13 +-5 +-11 +-17 +-14 ++19 ++2 ++15 +-9 +-18 +-16 +-16 ++5 +-9 ++7 ++16 ++3 +-18 ++4 ++3 +-9 +-14 +-13 +-13 +-16 +-19 ++11 +-1 ++14 ++10 ++17 ++17 +-5 ++16 +-17 +-17 ++11 ++10 ++16 ++11 +-13 ++17 +-10 ++16 +-18 +-15 ++16 +-14 ++18 ++16 ++7 ++12 +-16 ++12 +-10 ++17 +-14 ++8 ++15 +-18 +-18 ++7 ++8 +-17 ++1 +-12 ++16 +-2 ++16 ++13 ++2 +-7 ++10 +-2 +-14 ++20 ++5 ++16 +-13 +-18 ++14 ++19 ++16 +-6 +-8 +-30 ++14 ++26 ++9 ++9 ++20 ++52 +-45 +-165 +-15 ++7 ++2 +-13 +-6 ++16 +-17 +-9 ++1 ++7 ++5 ++11 ++23 +-2 +-7 ++6 +-7 +-4 ++21 +-15 +-5 +-17 +-6 +-42 ++3 ++40 ++18 ++41 ++3 ++13 +-92 +-22 +-17 +-37 +-17 ++10 ++19 +-24 +-10 +-2 +-3 +-1 ++8 ++12 +-17 ++14 ++6 ++19 ++4 +-41 +-64 +-5 +-16 ++5 +-4 +-20 +-7 ++4 ++1 +-15 +-11 +-3 +-13 ++58 ++33 ++8 +-10 +-6 +-69 ++1 +-107 ++84 +-79266 +-8 ++13 ++3 +-18 ++11 ++12 +-17 ++10 ++9 ++15 ++13 ++16 +-11 ++8 ++5 +-6 +-4 +-1 +-10 ++6 +-13 +-7 ++8 +-14 +-17 ++1 ++13 ++4 ++20 +-2 +-4 ++3 +-14 ++3 ++4 +-15 ++3 ++18 +-12 +-16 +-16 +-15 ++11 +-17 +-12 ++13 +-2 ++6 ++5 ++16 +-7 ++4 +-16 +-18 ++8 ++18 +-3 +-8 ++18 +-6 ++11 +-2 +-16 +-16 ++2 +-9 ++16 ++9 +-35 +-19 +-2 +-17 ++4 ++10 ++17 ++12 ++2 +-18 +-3 ++12 ++4 +-12 ++2 +-18 ++6 ++8 ++8 ++8 ++5 +-12 +-14 +-11 ++1 +-16 ++4 +-7 +-10 +-7 +-16 +-15 +-17 ++11 +-12 ++17 +-1 ++18 ++4 ++2 +-12 ++7 +-6 +-3 ++5 ++19 +-2 +-11 +-12 +-10 +-1 +-17 +-15 +-11 +-12 +-7 +-12 +-3 ++11 +-12 +-14 +-3 ++2 ++9 +-2 ++6 ++4 ++19 +-15 +-1 +-15 +-17 +-20 ++9 ++13 ++13 ++20 ++7 +-8 +-8 +-15 +-8 ++15 ++12 +-16 +-15 ++17 +-9 ++1 +-22 +-4 ++11 ++3 +-1 +-17 ++10 ++3 +-4 +-3 +-11 +-17 ++2 ++4 +-18 ++7 ++1 +-9 +-12 +-1 ++3 +-10 +-18 ++1 +-14 ++7 +-10 +-13 +-4 ++13 ++16 +-19 +-2 +-15 +-8 ++1 ++9 ++11 ++1 ++18 +-13 ++18 ++6 ++7 ++10 ++1 ++12 ++17 +-9 ++8 +-14 +-4 +-14 +-11 ++10 +-3 +-8 +-16 ++20 ++10 ++4 ++6 ++1 ++13 ++11 +-10 +-8 ++20 ++14 +-6 ++10 +-11 ++2 +-6 +-19 ++15 +-10 +-13 +-10 +-10 +-10 ++2 +-5 +-6 +-3 +-18 ++11 +-7 +-17 +-18 +-8 ++13 ++2 ++18 ++25 +-9 ++16 +-10 +-10 +-14 +-1 ++18 ++14 ++12 ++27 +-10 ++3 +-12 +-30 ++23 ++6 ++44 ++13 +-19 +-15 ++5 ++18 ++9 +-7 ++5 +-6 ++17 ++15 +-21 ++4 ++13 +-23 ++21 ++12 +-19 ++5 ++16 ++32 ++8 ++8 ++15 +-6 ++11 +-14 ++17 ++4 ++2 ++4 ++3 ++8 ++13 +-14 ++13 +-6 +-18 +-18 +-15 ++13 +-2 +-17 ++4 +-15 ++20 ++5 ++15 +-9 +-9 +-14 ++5 ++8 +-2 ++10 ++20 ++10 +-15 +-8 +-20 ++18 ++12 ++19 ++1 +-9 ++19 +-12 ++18 ++9 ++8 ++10 ++3 ++13 +-5 +-3 ++14 +-9 +-12 +-4 +-12 ++8 +-15 ++21 +-11 ++1 ++17 ++17 +-18 ++4 ++20 ++11 +-12 ++21 +-4 ++14 +-16 +-7 ++6 +-15 +-6 +-7 +-7 +-16 ++7 ++20 ++8 ++9 ++18 ++8 ++17 +-6 +-3 +-12 ++9 ++10 +-12 ++9 +-21 +-19 +-29 +-12 ++33 +-8 +-7 +-41 ++7 ++11 +-33 +-10 ++7 ++17 +-19 +-15 ++5 +-6 +-19 ++16 +-4 +-30 +-19 ++1 ++14 +-28 +-12 ++23 +-67 ++48 +-110 +-18 +-12 ++2 +-7 +-12 +-12 +-8 ++80339`; + + +// transform input to array +const changes = inputString + .split('\n') + .filter(line =>line.trim()) + .map(Number); + +//Calculate total frequency +const part1Result = changes.reduce((sum, change) => sum + change, 0); +console.log(`Part 1 - Resulting frequency: ${part1Result}`); +function findRepeatingFrequency(changes) { + let currentFrequency = 0; + const seen = new Set ([0]); + + while (true) { + for ( const change of changes) { + currentFrequency += change; + if (seen.has(currentFrequency)) { + return currentFrequency; + } + seen.add(currentFrequency) + } + } +} +const part2Result = findRepeatingFrequency (changes); +console.log(`Part 2 - First repeating frequency: ${part2Result}`); diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..3c23476cb 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,15 +1,26 @@ // Predict and explain first... + // I think it will show a syntax error due to inside const address we have more data than just the house number as requested. + // My house number is undefined - not a syntax + // This code should log out the houseNumber from the address object // but it isn't working... // Fix anything that isn't working -const address = { - houseNumber: 42, - street: "Imaginary Road", - city: "Manchester", - country: "England", - postcode: "XYZ 123", -}; +const address = [ + houseNumber = 56, + street = "Imaginary Road", + city = "Manchester", + country = "England", + postcode = "XYZ 123", +]; +// changed from {} to [] and from : to = +// could just change the call in console.log with the original code +// with this code, to console.log, I have to call by the index 0,1,2,3,4,5 console.log(`My house number is ${address[0]}`); +console.log(`My street name is ${address[1]}`); +console.log(`My city is ${address[2]}`); +console.log(`My country is ${address[3]}`); +console.log(`My postcode is ${address[4]}`); + diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..1a58d6847 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,5 +1,8 @@ // Predict and explain first... + // I believe is not working because is not an array, it will give me an syntax error + // real error: TypeError: author is not iterable + // This program attempts to log out all the property values in the object. // But it isn't working. Explain why first and then fix the problem @@ -11,6 +14,9 @@ const author = { alive: true, }; -for (const value of author) { - console.log(value); +// used Object.keys in order to iterate over the properties or entries of an object; +// “Give me all the keys from the author object, then for each one, show me the key and its value.” +for (const value of Object.keys(author)) { + const info = author[value]; + console.log(value, info); } diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..c3e46892a 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,5 +1,8 @@ // Predict and explain first... + // Is missing the instruction to log the ingredients in different lines, so it will not show the ingredients + // and also not in a different line + // This program should log out the title, how many it serves and the ingredients. // Each ingredient should be logged on a new line // How can you fix it? @@ -10,6 +13,6 @@ const recipe = { ingredients: ["olive oil", "tomatoes", "salt", "pepper"], }; -console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +console.log(`${recipe.title} serves ${recipe.serves} + ingredients: + ${recipe.ingredients.join('\n')}`); // .join('\n') to make a new line for each value on the string diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..cc27850a8 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,34 @@ -function contains() {} +function contains(obj, prop) { // define parameters + if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { + return false; + } + return Object.prototype.hasOwnProperty.call(obj, prop); +} + + +// Given a contains function +// When passed an object and a property name +// Then it should return true if the object contains the property, false otherwise +console.log(contains({a: 1, b: 2}, 'a')); // true + +// Given an empty object +// When passed to contains +// Then it should return false +console.log(contains({}, 'a')); // false + +// Given an object with properties +// When passed to contains with an existing property name +// Then it should return true +console.log(contains({a: 1, b: 2}, 'a')); // true + +// Given an object with properties +// When passed to contains with a non-existent property name +// Then it should return false +console.log(contains({a: 1, b: 2}, 'c')); // false + +// Given invalid parameters like an array +// When passed to contains +// Then it should return false or throw an error +console.log(contains([], 'a')); // false module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..036e800f2 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -16,20 +16,34 @@ as the object doesn't contains a key of 'c' // Given a contains function // When passed an object and a property name // Then it should return true if the object contains the property, false otherwise +test("object contains property", () => { + expect(contains({a: 1, b: 2}, 'a')).toEqual(true); +}) // Given an empty object // When passed to contains // Then it should return false -test.todo("contains on empty object returns false"); +test("empty object", () => { + expect(contains({}, 'a')).toEqual(false); +}) // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test("object contains existing property", () => { + expect(contains({a: 1, b: 2}, 'a')).toEqual(true); +}) // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test("object does not contain property", () => { + expect(contains({a: 1, b: 2}, 'c')).toEqual(false); +}) // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test("invalid parameters", () => { + expect(contains([], 'a')).toEqual(false); +}) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..0499dd3ec 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,11 @@ -function createLookup() { - // implementation here +function createLookup(countryCurrencyPairs) { + const lookup = {}; + + for (const [country, currency] of countryCurrencyPairs) { + lookup[country] = currency; + } + return lookup; } +console.log(createLookup([['PT', 'EUR'], ['CA', 'CAD']])); module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..ccee22e0a 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -2,6 +2,13 @@ const createLookup = require("./lookup.js"); test.todo("creates a country currency code lookup for multiple codes"); +test("creates lookup object from country-currency pairs", () => { + const input = [['US', 'USD'], ['CA', 'CAD']]; + const expected = { US: 'USD', CA: 'CAD' }; + + expect(createLookup(input)).toEqual(expected); +}); + /* Create a lookup object of key value pairs from an array of code pairs diff --git a/Sprint-2/implement/package.json b/Sprint-2/implement/package.json new file mode 100644 index 000000000..df5c00c2b --- /dev/null +++ b/Sprint-2/implement/package.json @@ -0,0 +1,15 @@ +{ + "name": "implement", + "version": "1.0.0", + "description": "", + "main": "contains.js", + "scripts": { + "test": "jest" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "jest": "^30.2.0" + } +} diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..e7de1bf7b 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,16 +1,31 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { - return queryParams; + + if (!queryString) return queryParams; +// remove leading "?" + if (queryString.startsWith("?")) { + queryString = queryString.slice(1); } + const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + if (!pair) continue; + + const [key, ...rest] = pair.split("="); + const value = rest.join("=") // handles "=" inside values + + const decodedKey = decodeURIComponent(key); // used to make the URL readable. e.g.: from { name: "John%20Doe" } to { name: "John Doe" } + const decodedValue = value ? decodeURIComponent(value) : ""; + + queryParams[decodedKey] = decodedValue; } return queryParams; } +//console.log(parseQueryString("name=oliveira&age=28")); +//console.log(parseQueryString("name=Beatriz%20Oliveira&city=Brussels")); + + module.exports = parseQueryString; diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..ecfac0853 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -6,7 +6,9 @@ const parseQueryString = require("./querystring.js") test("parses querystring values containing =", () => { - expect(parseQueryString("equation=x=y+1")).toEqual({ - "equation": "x=y+1", - }); + expect(parseQueryString("equation=x=y+1")).toEqual({"equation": "x=y+1",}); }); + +test("parses querystring values containing %20", () => { + expect(parseQueryString("name=Beatriz%20Oliveira&city=Brussels")).toEqual({name: 'Beatriz Oliveira', city: 'Brussels'}); +}); \ No newline at end of file diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..4b891fab0 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,28 @@ -function tally() {} +function tally(arr) { + if (!Array.isArray(arr)) { + return "THIS IS NOT AN ARRAY!"; + } + if (arr.length === 0) { + return {}; + } + /* ANOTHER WAY OF DOING THE DUPLICATE ITEMS +result[item] = (result[item] || 0) + 1; + */ + const result = {}; + + for (const item of arr) { + if (result[item] === undefined) { + result [item] = 1; + } else { + result[item] = result[item] + 1; + } + } + return result; +} + +//console.log(tally("dasda")); +//console.log(tally([])); +//console.log(tally(["a", "b", "c", "b", "a", "a"])); + module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..6605552d4 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -19,16 +19,28 @@ const tally = require("./tally.js"); // Given a function called tally // When passed an array of items // Then it should return an object containing the count for each unique item +test("tally on an array of items returns the count for each unique item", () => { + expect(tally(['a', 'a', 'b', 'c'])).toEqual({ a : 2, b: 1, c: 1 }); +}) // Given an empty array // When passed to tally // Then it should return an empty object -test.todo("tally on an empty array returns an empty object"); - +//test.todo("tally on an empty array returns an empty object"); +test("tally on an empty array returns an empty object", () => { + expect(tally([])).toEqual({}); +}) // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item +test("tally on an array with duplicate items", () => { + expect(tally(['a', 'a', 'b', 'c'])).toEqual({ a : 2, b: 1, c: 1 }); +}) + // Given an invalid input like a string // When passed to tally // Then it should throw an error +test("tally on an invalid input returns an error", () => { + expect(tally("fasdas")).toEqual("THIS IS NOT AN ARRAY!"); +}) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..c0f74ea23 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,30 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; // use the original value as the new key and the original key as the new value } return invertedObj; } +console.log(invert({ a: 1, b: 2 })); +console.log(invert({x : 10, y : 20})); + +module.exports = invert; + // a) What is the current return value when invert is called with { a : 1 } + // { key: [ 1 ] } // b) What is the current return value when invert is called with { a: 1, b: 2 } + // { key: [ 2 ] } // c) What is the target return value when invert is called with {a : 1, b: 2} + // {1: a, 2: b} // c) What does Object.entries return? Why is it needed in this program? + // returns an array of key-value pairs // d) Explain why the current return value is different from the target output + // because we are only inverting the value and not the key and it's also overwriting the first object // e) Fix the implementation of invert (and write tests to prove it's fixed!) diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..5554c0d56 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,17 @@ +const invert = require ('./invert.js'); + +test("should invert simple key values", () => { + expect(invert({ a: 1, b: 2 })).toEqual({ '1': 'a', '2': 'b' }); +}) + +test("should invert mixed types", () =>{ + expect(invert({ name: 'John', age: 30 })).toEqual({ 'John': 'name', '30': 'age' }); +}) + +test("should return empty object when given an empty object", () => { + expect(invert({})).toEqual({}); +}) + +test("should invert single key-value pair", () =>{ + expect(invert({ a: 1 })).toEqual({ '1': 'a' }); +}) \ No newline at end of file diff --git a/Sprint-2/interpret/package.json b/Sprint-2/interpret/package.json new file mode 100644 index 000000000..6cb72b3a4 --- /dev/null +++ b/Sprint-2/interpret/package.json @@ -0,0 +1,15 @@ +{ + "name": "interpret", + "version": "1.0.0", + "description": "", + "main": "invert.js", + "scripts": { + "test": "jest" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "jest": "^30.2.0" + } +} diff --git a/Sprint-2/stretch/count-words.js b/Sprint-2/stretch/count-words.js index 8e85d19d7..7c909d212 100644 --- a/Sprint-2/stretch/count-words.js +++ b/Sprint-2/stretch/count-words.js @@ -26,3 +26,38 @@ 3. Order the results to find out which word is the most common in the input */ + +function countWords(string) { + const totalWords = {}; + if (string.length === 0) { + return {}; + } + + const cleanedString = string + .replace(/[.,!?]/g, '') + .toLowerCase(); + + const words = cleanedString.split(' ').filter(word => word.length > 0); + + for(let i=0; i < words.length; i++){ + const word = words[i]; + + if (totalWords[word]) { + totalWords[word] += 1; + } else { + totalWords[word] = 1; + } + } + const sortedWords = Object.entries(totalWords) + .sort((a,b) => b[1] - a[1]) + .reduce((sortedObj, [word, count]) => { + sortedObj[word] = count; + return sortedObj; + }, {}); + return sortedWords; +} + +console.log(countWords("you and me and you")); +console.log(countWords("you and me and me and you and your friend Steve")); +console.log(countWords("")); +console.log(countWords("A a B b A")); diff --git a/Sprint-2/stretch/mode.js b/Sprint-2/stretch/mode.js index 3f7609d79..36ea9e3c2 100644 --- a/Sprint-2/stretch/mode.js +++ b/Sprint-2/stretch/mode.js @@ -9,28 +9,42 @@ // into smaller functions using the stages above function calculateMode(list) { - // track frequency of each value + if (!Array.isArray(list)) { + return NaN; + } + const freqs = countFrequencies(list); + const mode = findMaxFrequency(freqs); + return mode; +} + +function countFrequencies(list) { let freqs = new Map(); - for (let num of list) { - if (typeof num !== "number") { + for(let num of list) { + if(typeof num !== "number") { continue; } - freqs.set(num, (freqs.get(num) || 0) + 1); } + return freqs; +} - // Find the value with the highest frequency +function findMaxFrequency(freqs) { let maxFreq = 0; let mode; + for (let [num, freq] of freqs) { if (freq > maxFreq) { mode = num; maxFreq = freq; } } - return maxFreq === 0 ? NaN : mode; } +console.log(calculateMode([1, 2, 2, 3, 3, 3])); +console.log(calculateMode("hello world")); +console.log(calculateMode([])); +console.log(calculateMode(["a", "b", "c"])); +console.log(calculateMode([1, 3, 5, "a", "b", "c"])); module.exports = calculateMode; diff --git a/Sprint-2/stretch/till.js b/Sprint-2/stretch/till.js index 6a08532e7..9dee31483 100644 --- a/Sprint-2/stretch/till.js +++ b/Sprint-2/stretch/till.js @@ -8,10 +8,11 @@ function totalTill(till) { let total = 0; for (const [coin, quantity] of Object.entries(till)) { - total += coin * quantity; + const coinValue = parseInt(coin.replace('p', '')); + total += coinValue * quantity; } - return `£${total / 100}`; + return `£${(total / 100).toFixed(2)}`; } const till = { @@ -22,10 +23,17 @@ const till = { }; const totalAmount = totalTill(till); +console.log(totalAmount); + +module.exports = totalTill; + // a) What is the target output when totalTill is called with the till object + // it should return £4.40 - so it counts the total in pence and then converts to pounds // b) Why do we need to use Object.entries inside the for...of loop in this function? + // used to get key and values as an array so we can iterate over them in the for..of loop // c) What does coin * quantity evaluate to inside the for...of loop? + // is multiplying the coins by quantity so we can have the total amount // d) Write a test for this function to check it works and then fix the implementation of totalTill diff --git a/Sprint-2/stretch/till.test.js b/Sprint-2/stretch/till.test.js new file mode 100644 index 000000000..50050ad97 --- /dev/null +++ b/Sprint-2/stretch/till.test.js @@ -0,0 +1,21 @@ +const totalTill = require("./till"); + +test("calculates total for original till object", () => { + const till = {"1p": 10, "5p": 6, "50p": 4, "20p": 10}; + expect(totalTill(till)).toEqual("£4.40"); +}); + +test("calculates total for single coin type", () => { + const till = {"1p": 100}; + expect(totalTill(till)).toEqual("£1.00"); +}); + +test("calculates total for mixed coins", () => { + const till = {"50p": 2, "10p": 5}; + expect(totalTill(till)).toEqual("£1.50"); +}); + +test("handles empty till", () => { + const till = {}; + expect(totalTill(till)).toEqual("£0.00"); +}); \ No newline at end of file diff --git a/Sprint-3/alarmclock/alarmclock.js b/Sprint-3/alarmclock/alarmclock.js index 6ca81cd3b..2e5f6a9dd 100644 --- a/Sprint-3/alarmclock/alarmclock.js +++ b/Sprint-3/alarmclock/alarmclock.js @@ -1,4 +1,38 @@ -function setAlarm() {} +let timer; +let isTenSeconds = false; +function setAlarm() { + const input = document.getElementById("alarmSet").value; + let timeRemaining = Number(input); + + const isTenSeconds = timeRemaining === 10; + clearInterval(timer); // clear any previous interval + updateDisplay(timeRemaining); // display in mm:ss + + timer = setInterval(() => { + timeRemaining--; + + updateDisplay(timeRemaining); + + // when timer reaches 0 + if (timeRemaining <= 0) { + clearInterval(timer); + + if(isTenSeconds) { + + document.body.style.backgroundColor = "yellow"; + } + playAlarm(); + } +}, 1000); // 1000ms = 1 second +} + +function updateDisplay(time) { + const minutes = String(Math.floor(time / 60)).padStart(2, "0"); + const seconds = String(time % 60).padStart(2, "0"); + + document.getElementById("timeRemaining").innerText = + `Time Remaining: ${minutes}:${seconds}`; +} // DO NOT EDIT BELOW HERE diff --git a/Sprint-3/alarmclock/index.html b/Sprint-3/alarmclock/index.html index 48e2e80d9..98409e6a6 100644 --- a/Sprint-3/alarmclock/index.html +++ b/Sprint-3/alarmclock/index.html @@ -4,12 +4,12 @@ -