diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..5b254bfc7 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,4 +1,4 @@ -// Predict and explain first... +// Predict and explain first.../* there will be output of undefined as we are trying to access a property that is not yet defined // This code should log out the houseNumber from the address object // but it isn't working... @@ -12,4 +12,4 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +console.log(`My house number is ${address["houseNumber"]}`); diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..115358174 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,4 +1,5 @@ // Predict and explain first... +// My prediction is that there will be a TypeError as for ..of loop that is meant for array like object is being used on an object literals to access it's properties // 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 +12,6 @@ const author = { alive: true, }; -for (const value of author) { - console.log(value); -} +for (const value in author) { + console.log(author[value]); +} \ No newline at end of file diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..f989dc708 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,4 +1,5 @@ // Predict and explain first... +/* my prediction is that this program will print the title and how many it services but not the ingrientes as there is no dot notation in the recipe expression so the properties are never accessed */ // This program should log out the title, how many it serves and the ingredients. // Each ingredient should be logged on a new line @@ -10,6 +11,10 @@ const recipe = { ingredients: ["olive oil", "tomatoes", "salt", "pepper"], }; -console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +console.log(`${recipe.title} serves ${recipe.serves}`); + console.log("ingredients:"); + // use for of to print the value of the ingredients array element +for (const ingredients of recipe.ingredients){ +console.log(ingredients); +}; + diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..94b1264be 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,10 @@ -function contains() {} + +function contains(obj, target) { + for (const key in obj) { + if (key === target) return true; + } + return false; + } + module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..ec12ac711 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -16,20 +16,44 @@ 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 - +//Case 1 +test(" Case 1: returns true when the property exists", () => { + expect(contains({ a: 1, b: 2 }, "a")).toBe(true); +}); + +test("Case 2: returns false when the property does not exist", () => { + expect(contains({ a: 1, b: 2 }, "c")).toBe(false); +}); // Given an empty object // When passed to contains // Then it should return false -test.todo("contains on empty object returns false"); +test("contains on empty object returns false", () => { + expect(contains({ }, "c")).toBe(false);}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test(" Case 1: returns true when the property exists", () => { + expect(contains({ a: 1, b: 2 }, "a")).toBe(true); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false - +test("Case 2: returns false when the property does not exist", () => { + expect(contains({ a: 1, b: 2 }, "c")).toBe(false); +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error + +test(" returns false when invalid parameters like arrays are passed ", () => { + expect(contains([1,"a","NaN","b","3"], "a")).toBe(false); +}); +test(" returns false when invalid parameters like String is passed ", () => { + expect(contains("Toby", "Toby")).toBe(false); +}); + +test(" returns false when invalid parameters like number is passed ", () => { + expect(contains(1, 1)).toBe(false); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..f685e6b79 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,4 +1,11 @@ -function createLookup() { +function createLookup(pairs) { + const lookup = {}; + + for (let [country, currency] of pairs) { + lookup[country] = currency; + } + + return lookup; // implementation here } diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..954e510a7 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,6 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); +//test.todo("creates a country currency code lookup for multiple codes"); /* @@ -33,3 +33,28 @@ It should return: 'CA': 'CAD' } */ +describe("createLookup", () => { + test("creates a country currency code lookup for multiple codes", () => { + const input = [ + ["US", "USD"], + ["CA", "CAD"], + ["GB", "GBP"], + ]; + + const result = createLookup(input); + + expect(result).toEqual({ + US: "USD", + CA: "CAD", + GB: "GBP", + }); + }); + + test("returns an empty object when given an empty array", () => { + expect(createLookup([])).toEqual({}); + }); + + test("handles a single pair correctly", () => { + expect(createLookup([["JP", "JPY"]])).toEqual({ JP: "JPY" }); + }); +}); \ No newline at end of file diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..97772b86b 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,13 +1,36 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { - return queryParams; - } - const keyValuePairs = queryString.split("&"); + if (!queryString) return queryParams; + + const keyValuePairs = queryString.split("&").filter((item) => item !== ""); + + for (let pair of keyValuePairs) { + // Replace '+' with spaces + pair = pair.replace(/\+/g, " "); + + const eqIdx = pair.indexOf("="); + + let keyPair, valuePair; + + if (eqIdx === -1) { + keyPair = pair; + valuePair = ""; + } else { + keyPair = pair.slice(0, eqIdx); + valuePair = pair.slice(eqIdx + 1); + } + + const key = decodeURIComponent(keyPair); + const value = decodeURIComponent(valuePair); - for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + // Handle duplicates + if (!(key in queryParams)) { + queryParams[key] = value; + } else if (Array.isArray(queryParams[key])) { + queryParams[key].push(value); + } else { + queryParams[key] = [queryParams[key], value]; + } } return queryParams; diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..33923613d 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,20 @@ -function tally() {} +function tally(items) { + if(!Array.isArray(items)){ + throw new Error("Input must me and array"); + // validate and throw error if input is not an array . + } + const frequency = {}; + for (let item of items){ + if (item in frequency){ + frequency[item] +=1; + }else{ + frequency[item] = 1 + } + + } + + + return frequency; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..814db124d 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -32,3 +32,27 @@ test.todo("tally on an empty array returns an empty object"); // Given an invalid input like a string // When passed to tally // Then it should throw an error + + + +describe("tally()", () => { + test("counts frequency of each unique item", () => { + const result = tally(["a", "a", "b", "c"]); + expect(result).toEqual({ a: 2, b: 1, c: 1 }); + }); + test("returns an empty object when given an empty array", () => { + expect(tally([])).toEqual({}); + }); + + test("counts a single item correctly", () => { + expect(tally(["x"])).toEqual({ x: 1 }); + }); + + test("throws an error when input is not an array", () => { + expect(() => tally("hello")).toThrow(Error); + expect(() => tally(123)).toThrow(Error); + expect(() => tally({})).toThrow(Error); + expect(() => tally(null)).toThrow(Error); + }); +}); + diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..c8d2c060b 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,21 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; } return invertedObj; } +module.exports = invert; -// a) What is the current return value when invert is called with { a : 1 } +// a) What is the current return value when invert is called with { a : 1 }// the current return value is {key: 1} -// b) What is the current return value when invert is called with { a: 1, b: 2 } +// b) What is the current return value when invert is called with { a: 1, b: 2 }// the current returned value is {key:2} -// c) What is the target return value when invert is called with {a : 1, b: 2} +// c) What is the target return value when invert is called with {a : 1, b: 2}// the target output is { '1': 'a', '2': 'b' } -// c) What does Object.entries return? Why is it needed in this program? +// c) What does Object.entries return? Why is it needed in this program?// Object.entries converts the object into an array of [key, value] pairs. -// d) Explain why the current return value is different from the target output +// d) Explain why the current return value is different from the target output// Because after iterations the "invertedObj.key=value" is assigning same value to the new variable name "key" with the dot notation. // 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..ab3d0103f --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,9 @@ + + +const invert = require("./invert.js"); + + test("swaps keys and values for a simple object", () => { + const input = { x: 10, y: 20 }; + const output = invert(input); + expect(output).toEqual({ 10: "x", 20: "y" }); + }); \ No newline at end of file diff --git a/Sprint-2/stretch/count-words.js b/Sprint-2/stretch/count-words.js index 8e85d19d7..bcd992b36 100644 --- a/Sprint-2/stretch/count-words.js +++ b/Sprint-2/stretch/count-words.js @@ -26,3 +26,21 @@ 3. Order the results to find out which word is the most common in the input */ + function countWords(str){ + if(typeof str !=="string"){ + throw new Error("Input must be a string"); + } + const cleanedStr = str.replace(/[.,!?;:]/g, "").toLowerCase(); + const freq = {}; + const words = cleanedStr.split(" ").filter((w) => w !== ""); + + for (const word of words) { + freq[word] = (freq[word] || 0) + 1; + } + const sorted = Object.entries(freq).sort((a, b) => b[1] - a[1]); + + return sorted; + } + + + \ No newline at end of file diff --git a/Sprint-2/stretch/mode.js b/Sprint-2/stretch/mode.js index 3f7609d79..01a50483a 100644 --- a/Sprint-2/stretch/mode.js +++ b/Sprint-2/stretch/mode.js @@ -8,22 +8,24 @@ // refactor calculateMode by splitting up the code // into smaller functions using the stages above -function calculateMode(list) { - // track frequency of each value - let freqs = new Map(); +function buildFrequencyMap(list) { + const freqs = new Map(); - for (let num of list) { + for (const num of list) { if (typeof num !== "number") { - continue; + continue; // ignore non-numbers } - freqs.set(num, (freqs.get(num) || 0) + 1); } - // Find the value with the highest frequency + return freqs; +} + +function findModeFromFreqs(freqs) { let maxFreq = 0; let mode; - for (let [num, freq] of freqs) { + + for (const [num, freq] of freqs) { if (freq > maxFreq) { mode = num; maxFreq = freq; @@ -33,4 +35,10 @@ function calculateMode(list) { return maxFreq === 0 ? NaN : mode; } +function calculateMode(list) { + const freqs = buildFrequencyMap(list); // Stage 1 + const mode = findModeFromFreqs(freqs); // Stage 2 + return mode; +} + module.exports = calculateMode; diff --git a/Sprint-2/stretch/till.js b/Sprint-2/stretch/till.js index 6a08532e7..ec3cec837 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); + total += coinValue * quantity; } - return `£${total / 100}`; + return `£${(total / 100).toFixed(2)}`; } const till = { @@ -22,10 +23,19 @@ const till = { }; const totalAmount = totalTill(till); -// a) What is the target output when totalTill is called with the till object +const result = totalAmount; +const expectedResult = "£4.40"; -// b) Why do we need to use Object.entries inside the for...of loop in this function? +if (result === expectedResult) { + console.log("✅ Test Passed!"); +} else { + console.log(`❌ Test Failed. Expected ${expectedResult} but got ${result}`); +} + +// a) What is the target output when totalTill is called with the till object/* the target output is (1p*10 + 5p*6 +50p*4 +20p*10) =440/100 = £4.40*/ + +// b) Why do we need to use Object.entries inside the for...of loop in this function?// we use Object.entries to convert Object into an arrays of array to enable iteration as javaScript object is not iterable*/ -// c) What does coin * quantity evaluate to inside the for...of loop? +// c) What does coin * quantity evaluate to inside the for...of loop?/* The expression coin * quantity evaluated to NaN ,as Object.entries converted the object properties to string and after looping through , when the name Variable which is a string multiples a number they will concatenate to form NaN*/ // d) Write a test for this function to check it works and then fix the implementation of totalTill