From 13acaf43082c3d044294dff35436daf43c254c7c Mon Sep 17 00:00:00 2001 From: UbunMen Date: Tue, 25 Nov 2025 00:22:32 +0000 Subject: [PATCH 01/26] Address.js log function corrected --- Sprint-2/debug/address.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..36baf9306 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,5 +1,8 @@ // Predict and explain first... + // address is an object, so we can't access it by address[0] like in array we need to use a notation +// address.houseNumber, address.street, address.city etc ... + // This code should log out the houseNumber from the address object // but it isn't working... // Fix anything that isn't working @@ -12,4 +15,9 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); + +console.log(`My house number is ${address.houseNumber}`); + + + +// Address.js log function corrected From 7f8b2248946fcaeb005460b04945ec419b181366 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Tue, 25 Nov 2025 00:25:50 +0000 Subject: [PATCH 02/26] Author.js corrected. --- Sprint-2/debug/author.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..e7187d820 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,5 +1,9 @@ // Predict and explain first... +// I thought the value in the log needed to get attached with the author like value.author +// But the error was author is not iterable as it is an object, so we need to modify the for ... of loop as +// for(const value of Object.values(author)) + // 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 +15,8 @@ const author = { alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { console.log(value); } + +// Author.js corrected. \ No newline at end of file From ba4ef984c49cf57d44d7aefa986dba8d5197e70a Mon Sep 17 00:00:00 2001 From: UbunMen Date: Tue, 25 Nov 2025 00:26:56 +0000 Subject: [PATCH 03/26] recipe.js log function corrected. --- Sprint-2/debug/recipe.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..e998ed74f 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -12,4 +12,10 @@ const recipe = { console.log(`${recipe.title} serves ${recipe.serves} ingredients: -${recipe}`); +${recipe.ingredients.join(", ")}`); + + +// I guess, because the values of ingredients are given as array, +// the way ingredients is passed to the log is not right. + +// recipe.js log function corrected. \ No newline at end of file From 6453fe492cc7eb3a600cb6e86498aee72013935f Mon Sep 17 00:00:00 2001 From: UbunMen Date: Tue, 25 Nov 2025 00:28:11 +0000 Subject: [PATCH 04/26] contains.js function implemented and tested. --- Sprint-2/implement/contains.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..2bf9a9e2a 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,11 @@ -function contains() {} +function contains(obj, key) { + if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { + return false; +} +return key in obj; + +} module.exports = contains; + +// In contains.js function implemented and tested. From cb4642b11f6493e22df3b06fe6d5cdf8ca654b2f Mon Sep 17 00:00:00 2001 From: UbunMen Date: Tue, 25 Nov 2025 00:29:39 +0000 Subject: [PATCH 05/26] In contains.test.js tests for contains.test.js passed --- Sprint-2/implement/contains.test.js | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..48c56f425 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -17,19 +17,46 @@ as the object doesn't contains a key of 'c' // When passed an object and a property name // Then it should return true if the object contains the property, false otherwise +test("Should return true when key exists in object", () => { + const obj = { a: 1, b: 2 }; + expect(contains(obj, "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.todo("contains on empty object returns false"); + +test("Should return false when empty object is passed", () => { + const obj = {}; // empty object + expect(contains(obj, "a")).toEqual(false); +}) // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test("Should return true when object has the property", () => { + const obj = { a: 5, c: 6, r: 5 }; + expect(contains(obj, "c")).toEqual(true); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test("Should return false when object does not have the property", () => { + const obj = { a: 5, c: 6, r: 5 }; + expect(contains(obj, "z")).toEqual(false); // non-existent property name +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error + +test("Should return false when invalid parameters are used", () => { + expect(contains([], "a")).toEqual(false); // array + expect(contains(null, "a")).toEqual(false); // null + expect(contains(5, "a")).toEqual(false); // number + expect(contains("hello", "a")).toEqual(false); // string +}) + +// In contains.test.js tests for contains.test.js passed \ No newline at end of file From 9dd1769d03dff7c644fc7a5c132823e515711db1 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 26 Nov 2025 19:32:32 +0000 Subject: [PATCH 06/26] In lookup.js function createLookup implemented. --- Sprint-2/implement/lookup.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..03176b1c8 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,27 @@ -function createLookup() { - // implementation here +function createLookup(input) { + if (!Array.isArray(input)) { + throw new Error("Input must be an array of [country, currency] pairs"); + } + + const lookup = {}; + + for (const pair of input) { + if (!Array.isArray(pair) || pair.length !== 2) { + throw new Error("Each item must be an array of [country, currency]"); + } + + const [country, currency] = pair; + + if (typeof country !== "string" || typeof currency !== "string") { + throw new Error("Each item must be an array of [country, currency]"); + } + + lookup[country] = currency; + } + + return lookup; } module.exports = createLookup; + +// In lookup.js function createLookup implemented. \ No newline at end of file From 8bdd6c52ea25bea9394464da8e4335a9d181fce4 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 26 Nov 2025 19:33:31 +0000 Subject: [PATCH 07/26] In lookup.test.js test cases added and passed. --- Sprint-2/implement/lookup.test.js | 49 ++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..a59ab3a3b 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,50 @@ It should return: 'CA': 'CAD' } */ +// Case 1: Valid input + +test("returns an object with country codes as keys and currency codes as values", () => { + const input = [ + ["US", "USD"], + ["CA", "CAD"], + ]; + const expected = { US: "USD", CA: "CAD" }; + + expect(createLookup(input)).toEqual(expected); +}); + +// Case 2: Empty array + +test("returns an empty object when an empty array is passed", () => { + expect(createLookup([])).toEqual({}); +}); + +// Case 3: Input not an array + +test("throws an error when input is not an array", () => { + expect(() => createLookup("not an array")).toThrow( + "Input must be an array of [country, currency] pairs" + ); +}); + +// Case 4: Input null or undefined + +test("throws an error when input is null or undefined", () => { + expect(() => createLookup(null)).toThrow( + "Input must be an array of [country, currency] pairs" + ); + expect(() => createLookup(undefined)).toThrow( + "Input must be an array of [country, currency] pairs" + ); +}); + +// Case 5: type of Input elements are not strings + + test("throws an error if inner array elements are not valid strings", () => { + const invalidInput = [['US', 'USD'], ['CA', 123], ['JP', null]]; + expect(() => createLookup(invalidInput)).toThrow( + "Each item must be an array of [country, currency]" + ); + }); + +// In lookup.test.js test cases added and passed. \ No newline at end of file From 2c0305694ff438705ec4b1d58999a9a2c218d10d Mon Sep 17 00:00:00 2001 From: UbunMen Date: Thu, 27 Nov 2025 11:57:15 +0000 Subject: [PATCH 08/26] In querystring.js function implemented. --- Sprint-2/implement/querystring.js | 43 ++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..14edc8912 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,16 +1,45 @@ function parseQueryString(queryString) { - const queryParams = {}; + const parsedParams = {}; // Gets the final key-value pairs + + if (!queryString) return parsedParams; + + // Removes leading '?' if present + + if (queryString.startsWith("?")) { + queryString = queryString.slice(1); + } + if (queryString.length === 0) { - return queryParams; + return parsedParams; } - const keyValuePairs = queryString.split("&"); - for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + // Split the string into individual key-value pairs + const pairs = queryString.split("&"); + + for (const pair of pairs) { + if (!pair) continue; // skip empty segments (like from && or trailing &) eg "name=John&&age=30" + + const equalSignIndex = pair.indexOf("="); + + let paramKey, paramValue; + + if (equalSignIndex === -1) { + + // If '=' not found we have a key exists but value is empty + + paramKey = pair; + paramValue = ""; + } else { + paramKey = decodeURIComponent(pair.slice(0, equalSignIndex)); + paramValue = decodeURIComponent(pair.slice(equalSignIndex + 1)); + } + + parsedParams[paramKey] = paramValue; // overwrite previous value if key repeats } - return queryParams; + return parsedParams; } module.exports = parseQueryString; + +// In querystring.js function implemented. From 1101ce6f47d1d594a3b10e75ddda4bad7ef9ec44 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Thu, 27 Nov 2025 11:58:05 +0000 Subject: [PATCH 09/26] In querystring.test.js test edge cases added. --- Sprint-2/implement/querystring.test.js | 67 ++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..33ccab265 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -10,3 +10,70 @@ test("parses querystring values containing =", () => { "equation": "x=y+1", }); }); + +// Given an empty query string + +test("Empty querystring returns empty object", () => { + expect(parseQueryString("")).toEqual({}); +}); + +// A single key with value not given + +test("Single Key with No Value", () => { + expect(parseQueryString("key=")).toEqual({ key: "" }); +}); + +// A key without"=" sign + +test("Key Without = Sign", () => { + expect(parseQueryString("KeyAlone")).toEqual({ KeyAlone: "" }); +}); + +// Multiple parameters given + +test("Multiple Parameters", () => { + expect(parseQueryString("name=Smith&age=40&job=Teacher")).toEqual({ + name: "Smith", + age: "40", + job: "Teacher", + }); +}); + +// If values contain special characters + +test("Values Containing Special Characters", () => { + expect(parseQueryString("query=a%20b%26c%3Dd")).toEqual({ query: "a b&c=d" }); +}); + +// Query starting with "?" + +test("Starting with ?", () => { + expect(parseQueryString("?foo=bar")).toEqual({ foo: "bar" }); +}); + +// Given encoded key eg, %20 to " " + +test("Encoded keys, decoded correctly", () => { + expect(parseQueryString("first%20name=John")).toEqual({ + "first name": "John", + }); +}); + +// Mixed encoded + +test("Mixed encoded and plain parts", () => { + expect(parseQueryString("message=Hello%20World%21&Ans=Hello")).toEqual({ + message: "Hello World!", + Ans: "Hello", + }); +}); + +// Ignore repeated symbols --- && +test("Skips any empty parts that appear because of repeated symbols", () => { + expect(parseQueryString("a=1&&b=2")).toEqual({ + a: "1", + b: "2", + }); +}); + +// In querystring.test.js test edge cases added. \ No newline at end of file From d0665bd3f01f1e139d2911010cb4b23d12ed4e53 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Thu, 27 Nov 2025 13:28:22 +0000 Subject: [PATCH 10/26] In tally.js function tally() implemented. --- Sprint-2/implement/tally.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..0cd1ce842 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,24 @@ -function tally() {} +function tally(items) { + if (!Array.isArray(items)) { + throw new Error("Input must be an array"); + } + + if (items.length === 0) { + return {}; + } + + const counts = {}; + + for (const item of items) { + + // Convert objects and arrays to JSON string + const key = (typeof item === "object" && item !== null) ? JSON.stringify(item) : item; + counts[key] = (counts[key] || 0) + 1; + } + + return counts; +} module.exports = tally; + +// In tally.js function tally() implemented. From d471bcab93feda51bcd9df7cea0182097bd42651 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Thu, 27 Nov 2025 13:28:51 +0000 Subject: [PATCH 11/26] In tally.test.js test cases added and passed. --- Sprint-2/implement/tally.test.js | 81 +++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..fbf13122a 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -23,7 +23,7 @@ const tally = require("./tally.js"); // 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"); // Given an array with duplicate items // When passed to tally @@ -32,3 +32,82 @@ 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 + +// Case 1: Empty array + +test("tally on an empty array returns an empty object", () => { + expect(tally([])).toEqual({}); +}); + +// Case 2: Array with duplicate items + +test("tally with duplicate items", () => { + expect(tally(["a", "a", "a"])).toEqual({ a: 3 }); + expect(tally(["a", "a", "b", "c"])).toEqual({ a: 2, b: 1, c: 1 }); +}); + +// Cas 3: Array of arrays + +test("tally handles arrays as items", () => { + const input = [ + [1, 2], + [1, 2], + [3, 4], + ]; + const output = { + "[1,2]": 2, + "[3,4]": 1, + }; + expect(tally(input)).toEqual(output); +}); + +// case 4: Array of objects + +test("tally handles objects as items", () => { + const input = [{ a: 1 }, { a: 1 }, { b: 2 }]; + const output = { + '{"a":1}': 2, + '{"b":2}': 1, + }; + expect(tally(input)).toEqual(output); +}); + +// Case 5: Mixed array of arrays and objects + +test("tally handles mixed arrays and objects", () => { + const input = [[1, 2], { a: 1 }, [1, 2], { a: 1 }, { b: 2 }]; + const output = { + "[1,2]": 2, + '{"a":1}': 2, + '{"b":2}': 1, + }; + expect(tally(input)).toEqual(output); +}); + +// Case 6: Mixed types (numbers, strings, booleans) + +test("tally handles numbers, strings, and booleans", () => { + const input = [1, "1", true, true, false]; + const output = { 1: 2, true: 2, false: 1 }; + expect(tally(input)).toEqual(output); +}); + +// Case 7: Array with null and undefined + +test("tally handles null and undefined values", () => { + const input = [null, null, undefined]; + const output = { null: 2, undefined: 1 }; + expect(tally(input)).toEqual(output); +}); + +// Case 8: Invalid input: non-array + +test("tally throws error if input is not an array", () => { + expect(() => tally("not an array")).toThrow("Input must be an array"); + expect(() => tally(123)).toThrow("Input must be an array"); + expect(() => tally({ a: 1 })).toThrow("Input must be an array"); + expect(() => tally(null)).toThrow("Input must be an array"); + expect(() => tally(undefined)).toThrow("Input must be an array"); +}); + +// In tally.test.js test cases added and passed. From e550d17e9f0ccde52b8c978b127981d68338901b Mon Sep 17 00:00:00 2001 From: UbunMen Date: Thu, 4 Dec 2025 19:09:01 +0000 Subject: [PATCH 12/26] In invert.js invert function implemented. --- Sprint-2/interpret/invert.js | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..2c31d5cff 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -7,23 +7,46 @@ // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} function invert(obj) { + if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { + throw new Error("Input must be an object"); + } + const invertedObj = {}; - for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + for (let [key, value] of Object.entries(obj)) { + if (typeof key === "object") { + key = JSON.stringify(key); + } + if (typeof value === "object") { + value = JSON.stringify(value); + } + invertedObj[value] = key; } return invertedObj; } +module.exports = invert; // a) What is the current return value when invert is called with { a : 1 } +// --> current return value i {key: 1} + // b) What is the current return value when invert is called with { a: 1, b: 2 } +// --> current return value i {key: 2} // c) What is the target return value when invert is called with {a : 1, b: 2} +// --> Target return value is {"1": "a", "2": "b" } // c) What does Object.entries return? Why is it needed in this program? +// --> Object.entries returns an array of key and value ['key', 'value'] pairs + // Given { a: 1, b: 2 } returns [['a', '1'],['b', '2']] // d) Explain why the current return value is different from the target output +// --> invertedObj.key = value is creating a property named key. As the loop + //goes through the object each value gets overwritten and we get the last + // argument as a value. // e) Fix the implementation of invert (and write tests to prove it's fixed!) + + +// In invert.js invert function implemented. \ No newline at end of file From 218caa693d4bf5ada46e16b3ecab9bda1a04e869 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Thu, 4 Dec 2025 19:09:34 +0000 Subject: [PATCH 13/26] // In invert.test.js test cases tested. --- Sprint-2/interpret/invert.test.js | 45 +++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 Sprint-2/interpret/invert.test.js diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..449adf966 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,45 @@ +const invert = require("./invert.js"); + +// Case 1: Normal object inversion +test("invert single-level object", () => { + expect(invert({ a: 1, b: 2 })).toEqual({ "1": "a", "2": "b" }); +}); + +// Case 2: object with string values +test("invert object with string values", () => { + expect(invert({ x: "CYF", y: "abc" })).toEqual({ "CYF": "x", "abc": "y" }); +}); + +// Case 3: object with boolean values +test("invert object with boolean values", () => { + expect(invert({ a: true, b: false })).toEqual({ "true": "a", "false": "b" }); +}); + +// Case 4: object with nested object as value +test("invert object with nested object as value", () => { + const input = { a: { x: 1 }, b: 2 }; + const expected = { '{"x":1}': "a", "2": "b" }; + expect(invert(input)).toEqual(expected); +}); + +// Case 5: object with duplicate values +test("invert object with duplicate values", () => { + const input = { a: 1, b: 1 }; + const expected = { "1": "b" }; + expect(invert(input)).toEqual(expected); +}); + +// Case 6: empty object +test("invert empty object", () => { + expect(invert({})).toEqual({}); +}); + +// Case 7: invalid inputs (non-object) +test("throws error for non-object input", () => { + expect(() => invert(null)).toThrow("Input must be an object"); + expect(() => invert(42)).toThrow("Input must be an object"); + expect(() => invert("foo")).toThrow("Input must be an object"); + expect(() => invert([1,2,3])).toThrow("Input must be an object"); +}); + +// In invert.test.js test cases tested. \ No newline at end of file From d2985e477596d45d40992969b0c90388bd5a0a52 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Thu, 4 Dec 2025 20:00:57 +0000 Subject: [PATCH 14/26] In count-words.js function countWords implemented. --- Sprint-2/stretch/count-words.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Sprint-2/stretch/count-words.js b/Sprint-2/stretch/count-words.js index 8e85d19d7..e6bcc235e 100644 --- a/Sprint-2/stretch/count-words.js +++ b/Sprint-2/stretch/count-words.js @@ -25,4 +25,32 @@ 2. Ignore the case of the words to find more unique words. e.g. (A === a, Hello === hello) 3. Order the results to find out which word is the most common in the input + */ +function countWords(string) { + // validation of input + if (typeof string !== "string") { + throw new Error("Input should be a string"); + } + if (string === "") return {}; + const counts = {}; + + // Remove punctuation and lowercase everything + + const cleanText = string.replace(/[.,!?]/g, "").toLowerCase(); + const words = cleanText.split(" "); + + // Count word frequencies + for (const word of words) { + counts[word] = (counts[word] || 0) + 1; + } + + // Sort by frequency (most common first) + const sorted = Object.entries(counts).sort((a, b) => b[1] - a[1]); + + return Object.fromEntries(sorted); +} + +console.log(countWords("Hello!, my friend, You and me, and you.")); + +// In count-words.js function countWords implemented. From 6dff1386e70235e265125d2f7c67669930f558dd Mon Sep 17 00:00:00 2001 From: UbunMen Date: Thu, 4 Dec 2025 20:01:52 +0000 Subject: [PATCH 15/26] In mode.js function calculateMode refactored. --- Sprint-2/stretch/mode.js | 62 +++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/Sprint-2/stretch/mode.js b/Sprint-2/stretch/mode.js index 3f7609d79..13e72112b 100644 --- a/Sprint-2/stretch/mode.js +++ b/Sprint-2/stretch/mode.js @@ -8,29 +8,69 @@ // 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 calculateMode(list) { +// // track frequency of each value +// let freqs = new Map(); - for (let num of list) { - if (typeof num !== "number") { - continue; - } +// for (let num of list) { +// if (typeof num !== "number") { +// continue; +// } + +// freqs.set(num, (freqs.get(num) || 0) + 1); +// } + +// // Find the value with the highest frequency +// let maxFreq = 0; +// let mode; +// for (let [num, freq] of freqs) { +// if (freq > maxFreq) { +// mode = num; +// maxFreq = freq; +// } +// } + +// return maxFreq === 0 ? NaN : mode; +//} + + +// -------------- refactored ------------- // + +// Phase 1: Count the frequency of each number + +function countFrequency(list) { + const frequency = new Map(); - freqs.set(num, (freqs.get(num) || 0) + 1); + for (const num of list) { + if (typeof num !== "number") continue; // skip non-numbers + frequency.set(num, (frequency.get(num) || 0) + 1); } - // Find the value with the highest frequency + return frequency; +} + +// Stage 2: Find the number with the highest frequency +function findMode(frequency) { let maxFreq = 0; let mode; - for (let [num, freq] of freqs) { + + for (const [num, freq] of frequency) { if (freq > maxFreq) { - mode = num; maxFreq = freq; + mode = num; } } return maxFreq === 0 ? NaN : mode; } +// Main function +function calculateMode(list) { + const frequency = countFrequency(list); + return findMode(frequency); +} +console.log(calculateMode([1, 2, 2, 3, 3, 3, 4])); + module.exports = calculateMode; + +// In mode.js function calculateMode refactored. From 0088e751e9d5ef949eeb1ab19c7515ed920b8c3f Mon Sep 17 00:00:00 2001 From: UbunMen Date: Thu, 4 Dec 2025 20:09:37 +0000 Subject: [PATCH 16/26] In mode.test.js test cases passed --- Sprint-2/stretch/mode.test.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Sprint-2/stretch/mode.test.js b/Sprint-2/stretch/mode.test.js index ca33c28a3..241ebe8de 100644 --- a/Sprint-2/stretch/mode.test.js +++ b/Sprint-2/stretch/mode.test.js @@ -13,20 +13,29 @@ const calculateMode = require("./mode.js"); describe("calculateMode()", () => { test("returns the most frequent number in an array", () => { - const nums = [2, 4, 1, 2, 3, 2, 1]; + const arr = [2, 4, 1, 2, 3, 2, 1]; - expect(calculateMode(nums)).toEqual(2); + expect(calculateMode(arr)).toEqual(2); }); test("returns the first mode in case of multiple modes", () => { - const nums = [1, 2, 2, 3, 3]; + const arr = [1, 2, 2, 3, 3]; - expect(calculateMode(nums)).toEqual(2); + expect(calculateMode(arr)).toEqual(2); }); test("ignores non-number values", () => { - const nums = [1, 3, "2", 2, 3, null]; + const arr = [1, 3, "2", 2, 3, null]; - expect(calculateMode(nums)).toEqual(3); + expect(calculateMode(arr)).toEqual(3); }); + + test("returns NaN for an empty array", () => { + expect(calculateMode([])).toEqual(); +}); + }); + +// In mode.test.js test cases passed + + From bd20fa115b6d3fb94d35542ff1362f86b6cebc94 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Sat, 6 Dec 2025 14:16:32 +0000 Subject: [PATCH 17/26] In till.js function implemented. --- Sprint-2/stretch/till.js | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/Sprint-2/stretch/till.js b/Sprint-2/stretch/till.js index 6a08532e7..f95a5bd14 100644 --- a/Sprint-2/stretch/till.js +++ b/Sprint-2/stretch/till.js @@ -5,27 +5,35 @@ // Then it should return the total amount in pounds function totalTill(till) { + if (typeof till !== "object" || till === null || Array.isArray(till)) { + throw new Error("Input should be an object"); + } + let total = 0; for (const [coin, quantity] of Object.entries(till)) { - total += coin * quantity; + const value = parseInt(coin); // extracts the numeric part + + if (isNaN(value)) continue; + + total += value * quantity; } - return `£${total / 100}`; + const pounds = Math.floor(total / 100); + const pence = String(total % 100).padStart(2, "0"); + + return `£${pounds}.${pence}`; } -const till = { - "1p": 10, - "5p": 6, - "50p": 4, - "20p": 10, -}; -const totalAmount = totalTill(till); +module.exports = totalTill; // a) What is the target output when totalTill is called with the till object - +// --> The target output should be £4.40 // b) Why do we need to use Object.entries inside the for...of loop in this function? - +// --> To get both the key and value of the till object inside the loop. // c) What does coin * quantity evaluate to inside the for...of loop? - +// --> It is supposed to calculate the total number of pence // d) Write a test for this function to check it works and then fix the implementation of totalTill + + +// In till.js function implemented. \ No newline at end of file From 3735e91b9c9e5cb64f63fc889620d4db28b070d3 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Sat, 6 Dec 2025 14:17:01 +0000 Subject: [PATCH 18/26] In till.test.js test cases written and tested. --- Sprint-2/stretch/till.test.js | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Sprint-2/stretch/till.test.js diff --git a/Sprint-2/stretch/till.test.js b/Sprint-2/stretch/till.test.js new file mode 100644 index 000000000..08abbe8d7 --- /dev/null +++ b/Sprint-2/stretch/till.test.js @@ -0,0 +1,49 @@ +const totalTill = require("./till.js"); + +describe("totalTill()", () => { + test("correctly totals a simple till", () => { + const till = { + "1p": 10, // 10p + "5p": 6, // 30p + "20p": 10, // 200p + "50p": 4, // 200p + }; + + expect(totalTill(till)).toBe("£4.40"); + }); + + test("ignores invalid coin types", () => { + const till = { + "1p": 2, // 2p + "abc": 10, // ignore + "£1": 5, + }; + + expect(totalTill(till)).toEqual("£0.02"); + }); + + test("handles empty till", () => { + expect(totalTill({})).toEqual("£0.00"); + }); + + test("throws an error for non-object input", () => { + expect(() => totalTill(null)).toThrow("Input should be an object"); + expect(() => totalTill(5)).toThrow("Input should be an object"); + expect(() => totalTill("hello")).toThrow("Input should be an object"); + expect(() => totalTill([])).toThrow("Input should be an object"); + }); + + test("correct formatting for values under 10p", () => { + const till = { "1p": 3 }; // 3p + expect(totalTill(till)).toEqual("£0.03"); + }); + + test("correct formatting when total is exactly whole pounds", () => { + const till = { "50p": 4, "20p": 5 }; + // 50p*4 = 200p, 20p*5 = 100p → total = 300p = £3.00 + + expect(totalTill(till)).toEqual("£3.00"); + }); +}); + +// In till.test.js test cases written and tested. \ No newline at end of file From 2f7f0e2f4402fc678c26aa615e0387b24dba938e Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 10 Dec 2025 11:56:16 +0000 Subject: [PATCH 19/26] recipe.js modified "${recipe.ingredients.join("\n")}`) to make ingredients appear on new line. --- Sprint-2/debug/recipe.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index e998ed74f..1940376c4 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -12,10 +12,11 @@ const recipe = { console.log(`${recipe.title} serves ${recipe.serves} ingredients: -${recipe.ingredients.join(", ")}`); +${recipe.ingredients.join("\n")}`); // I guess, because the values of ingredients are given as array, // the way ingredients is passed to the log is not right. -// recipe.js log function corrected. \ No newline at end of file +// recipe.js log function corrected. +// recipe.js modified "${recipe.ingredients.join("\n")}`) to make ingredients appear on new line. \ No newline at end of file From 161b8710a9ef72210f47c266276748bf585f9f13 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 10 Dec 2025 12:42:24 +0000 Subject: [PATCH 20/26] In contains.js modified return function as return Object.hasOwn(obj, key); --- Sprint-2/implement/contains.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index 2bf9a9e2a..8c8b8f98d 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -2,10 +2,17 @@ function contains(obj, key) { if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { return false; } -return key in obj; +//return key in obj; +return Object.hasOwn(obj, key); } module.exports = contains; // In contains.js function implemented and tested. + + let obj = {}, propertyName = "toString"; + console.log( propertyName in obj ); // true + console.log( Object.hasOwn(obj, propertyName) ); // false + + // In contains.js modified return function as return Object.hasOwn(obj, key); \ No newline at end of file From 5cddc238ba9bc24a9e9bdfeff1150d7e4dc0321f Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 10 Dec 2025 13:01:44 +0000 Subject: [PATCH 21/26] contains.test.js re-tested. --- Sprint-2/implement/contains.test.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 48c56f425..9e8743d8c 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -53,10 +53,14 @@ test("Should return false when object does not have the property", () => { // Then it should return false or throw an error test("Should return false when invalid parameters are used", () => { - expect(contains([], "a")).toEqual(false); // array + expect(contains([1, 2, 3], "1")).toEqual(true); // "1" is a key + expect(contains([1, 2, 3], "3")).toEqual(false); // "3" is not a key + //expect(contains([], "a")).toEqual(false); // array expect(contains(null, "a")).toEqual(false); // null expect(contains(5, "a")).toEqual(false); // number expect(contains("hello", "a")).toEqual(false); // string }) -// In contains.test.js tests for contains.test.js passed \ No newline at end of file +// In contains.test.js tests for contains.test.js passed + +// contains.test.js re-tested. \ No newline at end of file From aab642f09e10b79779398e0d2bef77fc4cb602a9 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 10 Dec 2025 15:19:24 +0000 Subject: [PATCH 22/26] Decoding of paramKey = decodeURIComponent(pair); added. --- Sprint-2/implement/querystring.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 14edc8912..de4a8eb70 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -27,7 +27,7 @@ function parseQueryString(queryString) { // If '=' not found we have a key exists but value is empty - paramKey = pair; + paramKey = decodeURIComponent(pair); paramValue = ""; } else { paramKey = decodeURIComponent(pair.slice(0, equalSignIndex)); @@ -43,3 +43,4 @@ function parseQueryString(queryString) { module.exports = parseQueryString; // In querystring.js function implemented. +// Decoding of paramKey = decodeURIComponent(pair); added. \ No newline at end of file From 7ef1f9c60891b8373cc1140e0e676d02f835ecd6 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 10 Dec 2025 15:42:20 +0000 Subject: [PATCH 23/26] In tally.js modification made. --- Sprint-2/implement/tally.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index 0cd1ce842..cad5ee856 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -7,7 +7,7 @@ function tally(items) { return {}; } - const counts = {}; + const counts = Object.create(null); for (const item of items) { @@ -19,6 +19,11 @@ function tally(items) { return counts; } +console.log(tally(["toStrin", "toStrin"])); +console.log(tally(["toString", "toString"])); + module.exports = tally; // In tally.js function tally() implemented. + +// modification made. From ba1bc62c5b825f27578348d162bf976d9e9df6e4 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 10 Dec 2025 15:45:47 +0000 Subject: [PATCH 24/26] Modified to handle inherent property cases and to remove empty spaces. --- Sprint-2/stretch/count-words.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Sprint-2/stretch/count-words.js b/Sprint-2/stretch/count-words.js index e6bcc235e..7ed1433d9 100644 --- a/Sprint-2/stretch/count-words.js +++ b/Sprint-2/stretch/count-words.js @@ -33,12 +33,12 @@ function countWords(string) { throw new Error("Input should be a string"); } if (string === "") return {}; - const counts = {}; + const counts = Object.create(null) // prevents inherent properties. // Remove punctuation and lowercase everything const cleanText = string.replace(/[.,!?]/g, "").toLowerCase(); - const words = cleanText.split(" "); + const words = cleanText.split(" ").filter(Boolean); // removes empty strings // Count word frequencies for (const word of words) { @@ -52,5 +52,9 @@ function countWords(string) { } console.log(countWords("Hello!, my friend, You and me, and you.")); +console.log(countWords("constructor constructor")); +console.log(countWords(" Hello World ")); // In count-words.js function countWords implemented. + +// Modified to handle inherent property cases and to remove empty spaces. From 2528d55cb57f0a619cacfb485f9b072b2d906bb6 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 10 Dec 2025 16:59:00 +0000 Subject: [PATCH 25/26] The "if (typeof obj !== "object" || obj === null)" modified. --- Sprint-2/implement/contains.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index 8c8b8f98d..f3c8c0c33 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,7 +1,7 @@ function contains(obj, key) { - if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { + if (typeof obj !== "object" || obj === null) { return false; -} + } //return key in obj; return Object.hasOwn(obj, key); @@ -15,4 +15,5 @@ module.exports = contains; console.log( propertyName in obj ); // true console.log( Object.hasOwn(obj, propertyName) ); // false - // In contains.js modified return function as return Object.hasOwn(obj, key); \ No newline at end of file + // In contains.js modified return function as return Object.hasOwn(obj, key); + // The "if (typeof obj !== "object" || obj === null)" modified. \ No newline at end of file From 8e4807e51132b7742bad2dbea0229e77c918f665 Mon Sep 17 00:00:00 2001 From: UbunMen Date: Wed, 10 Dec 2025 17:29:54 +0000 Subject: [PATCH 26/26] Test case description updated --- Sprint-2/implement/contains.test.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 9e8743d8c..5bd2c50e2 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -52,15 +52,19 @@ test("Should return false when object does not have the property", () => { // When passed to contains // Then it should return false or throw an error -test("Should return false when invalid parameters are used", () => { +test("Should correctly detect keys in arrays", () => { expect(contains([1, 2, 3], "1")).toEqual(true); // "1" is a key expect(contains([1, 2, 3], "3")).toEqual(false); // "3" is not a key - //expect(contains([], "a")).toEqual(false); // array - expect(contains(null, "a")).toEqual(false); // null +}); + +test("Should return false when invalid parameters are used", () => { + expect(contains(null, "a")).toEqual(false); // null expect(contains(5, "a")).toEqual(false); // number expect(contains("hello", "a")).toEqual(false); // string }) // In contains.test.js tests for contains.test.js passed -// contains.test.js re-tested. \ No newline at end of file +// contains.test.js re-tested. + +// test case description differentiated. \ No newline at end of file