From 15a213d2f6bae351483f326674980711d9083a72 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Thu, 12 Mar 2026 18:08:04 +0000 Subject: [PATCH 01/11] files have been debugged in debug folder of sprint-2 --- Sprint-2/debug/address.js | 2 +- Sprint-2/debug/author.js | 7 +++++-- Sprint-2/debug/recipe.js | 8 ++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..36d2f865d 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -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..4612b723d 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -3,6 +3,9 @@ // 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 +// ==> this code is looping through an object and only return the key not the value. +// ==> After testing I found that (for of) does not loop directly in objects and it was throwing error "author is not iterable". In this case we should use (for in) to iterate. + const author = { firstName: "Zadie", lastName: "Smith", @@ -11,6 +14,6 @@ const author = { alive: true, }; -for (const value of author) { - console.log(value); +for (const value in author) { + console.log(author[value]); } diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..abb87ece7 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,8 +1,10 @@ // Predict and explain first... +// ==> this code is not correct as the ingredients act as string not an object key therefore we wont have the value. // 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? +// ==> to fix the issue and print each ingredients in new line we need to loop through the array inside our recipe object. const recipe = { title: "bruschetta", @@ -11,5 +13,7 @@ const recipe = { }; console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +ingredients:`); +for (let fruits of recipe.ingredients) { + console.log(fruits); +} From ad212d7c7d4bd90e190f73b9c74378eee3522f95 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Mon, 16 Mar 2026 11:21:18 +0000 Subject: [PATCH 02/11] Jest test added and Functions were fixed --- Sprint-2/implement/contains.js | 17 ++++++++++++- Sprint-2/implement/contains.test.js | 34 ++++++++++++++++++++++++-- Sprint-2/implement/lookup.js | 16 +++++++++--- Sprint-2/implement/lookup.test.js | 18 +++++++++++--- Sprint-2/implement/querystring.js | 20 ++++++++++----- Sprint-2/implement/querystring.test.js | 4 +-- Sprint-2/implement/tally.js | 16 ++++++++++-- Sprint-2/implement/tally.test.js | 18 ++++++++++++-- 8 files changed, 122 insertions(+), 21 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..b509ba799 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,18 @@ -function contains() {} +// start your function + +function contains(object, key) { + if (Array.isArray(object) && Object.keys(object).length === 0) { + return false; + } else if (object.hasOwnProperty(key)) { + return true; + } else return false; +} + +let key = "name"; +const object = { + name: "Roman", + age: 33, + city: "Birmingham", +}; module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..2713519ca 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -15,21 +15,51 @@ 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 +// Then it should return true if the object contains the property, +test("It return true when the object contains the property", () => { + let object = { name: "Roman", age: 34 }; + let key = "name"; + expect(contains(object, key)).toEqual(true); +}); +// object does not contain the property +test("It return false when the object does not contains the property", () => { + let object = { name: "Roman", age: 34 }; + let key = "lastName"; + expect(contains(object, key)).toEqual(false); +}); // Given an empty object // When passed to contains // Then it should return false -test.todo("contains on empty object returns false"); +test("It return false when the object has not any property", () => { + let object = {}; + let key = "name"; + expect(contains(object, key)).toEqual(false); +}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test("It returns true when the object has the specified property", () => { + const object = { name: "Roman", age: 34, city: "Birmingham" }; + const key = "name"; + expect(contains(object, key)).toEqual(true); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test("It returns false when the object does NOT have the specified property", () => { + const object = { name: "Roman", age: 34, city: "Birmingham" }; + const key = "lastName"; + expect(contains(object, key)).toEqual(false); +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test("It return false when an array is passed instead of object", () => { + let object = [1, 2, 3]; + let key = "name"; + expect(contains(object, key)).toEqual(false); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..0a01c5652 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,15 @@ -function createLookup() { - // implementation here +// start your function; +function createLookup(input) { + let output = {}; + for (let pair of input) { + let key = pair[0]; + let value = pair[1]; + output[key] = value; + } + return output; } - +let input = [ + ["US", "USD"], + ["CA", "CAD"], +]; module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..9d4be7c87 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,9 +1,7 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); - /* - + Create a lookup object of key value pairs from an array of code pairs Acceptance Criteria: @@ -33,3 +31,17 @@ It should return: 'CA': 'CAD' } */ + +test("creates a country currency code lookup for multiple codes", () => { + const input = [ + ["US", "USD"], + ["CA", "CAD"], + ]; + + const expected = { + US: "USD", + CA: "CAD", + }; + + expect(createLookup(input)).toEqual(expected); +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..75c95c83b 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,16 +1,24 @@ +// start your function; function parseQueryString(queryString) { const queryParams = {}; if (queryString.length === 0) { return queryParams; } - const keyValuePairs = queryString.split("&"); - - for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + // URLSearchParams is a built-in JavaScript object that can read query strings. + const params = new URLSearchParams(queryString); // params stores the query string internally as key-value pairs. + for (let pair of params) { + let key = pair[0]; + let value = pair[1]; + queryParams[key] = value.replace(/ /g, "+"); } - return queryParams; } +let queryString = "equation=x=y+1"; module.exports = parseQueryString; + +// Notice: ===> URLSearchParams is a class, not a plain function. +// Classes in JS must be called with new. +// new URLSearchParams(...) → creates a new instance (object) of the class. +// That instance knows how to store and iterate key-value pairs. +// URLSearchParams decodes URL-encoded characters, and in query strings, a + represents a space. and this is why we need .replace(/ /g, "+"); diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..717ee6901 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,10 @@ // Below is one test case for an edge case the implementation doesn't handle well. // Fix the implementation for this test, and try to think of as many other edge cases as possible - write tests and fix those too. -const parseQueryString = require("./querystring.js") +const parseQueryString = require("./querystring.js"); test("parses querystring values containing =", () => { expect(parseQueryString("equation=x=y+1")).toEqual({ - "equation": "x=y+1", + equation: "x=y+1", }); }); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..6cddd4258 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,15 @@ -function tally() {} - +function tally(arr) { + let output = {}; + if (!Array.isArray(arr)) { + throw new Error("Invalid input"); + } else if (arr.length === 0) { + return output; + } + for (let item of arr) { + let count = arr.filter((x) => x === item).length; + output[item] = count; + } + return output; +} +let arr = ["a", "b"]; module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..9a0776f30 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -19,16 +19,30 @@ 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("returns count 1 for a single item", () => { + const arr = ["a"]; + expect(tally(arr)).toEqual({ a: 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("returns an empty object when given an empty array", () => { + const arr = []; + expect(tally(arr)).toEqual({}); +}); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item - +test("returns counts for each unique item for given duplicate items ", () => { + const arr = ["a", "a", "b", "c"]; + expect(tally(arr)).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("it throw an error if given a string", () => { + const str = "a"; + expect(() => tally(str)).toThrowError(); +}); From ef27922808b309dbbf504b9de2c6c20e518f2627 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Mon, 16 Mar 2026 14:07:31 +0000 Subject: [PATCH 03/11] inverted function and jest test implemented --- Sprint-2/interpret/invert.js | 11 +++++++++-- Sprint-2/interpret/invert.test.js | 13 +++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 Sprint-2/interpret/invert.test.js diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..3765bdbcd 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,27 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; } return invertedObj; } - +obj = { a: 1, b: 2 }; +module.exports = invert; // a) What is the current return value when invert is called with { a : 1 } +// answer ===> the output will be {"key": 1} // b) What is the current return value when invert is called with { a: 1, b: 2 } +// answer ===> the output ll be {"key": 2} // c) What is the target return value when invert is called with {a : 1, b: 2} +// answer ===> the output ll be {"1": "a", "2": "b"}; // c) What does Object.entries return? Why is it needed in this program? +// answer ===> converts an object into an array of key-value pairs, we need it to loop through and access the elements. // d) Explain why the current return value is different from the target output +// answer ===> because of this line --> invertedObj.key = value; as it creates a "key" and assign the value to it. and with variables we need to use bracket notation not dot notation. // e) Fix the implementation of invert (and write tests to prove it's fixed!) +// I have fixed the implementation: from invertedObj.key = value to invertedObj[value] = key; diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..4921a429b --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,13 @@ +const invert = require("./invert.js"); +// testing time +test("Empty object returns empty object", () => { + expect(invert({})).toEqual({}); +}); + +test("Single key-value pair", () => { + expect(invert({ a: 1 })).toEqual({ 1: "a" }); +}); + +test("Given an object, then it should swap the keys and values in the object", () => { + expect(invert({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" }); +}); From df349cf71be0384bb08f3df8289cf863a444927a Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Mon, 16 Mar 2026 17:48:13 +0000 Subject: [PATCH 04/11] test and functions implemented for stretch files --- Sprint-2/stretch/count-words.js | 15 +++++++++++++++ Sprint-2/stretch/mode.js | 31 ++++++++++++++++--------------- Sprint-2/stretch/till.js | 11 +++++++---- Sprint-2/stretch/till.test.js | 23 +++++++++++++++++++++++ 4 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 Sprint-2/stretch/till.test.js diff --git a/Sprint-2/stretch/count-words.js b/Sprint-2/stretch/count-words.js index 8e85d19d7..78bd31ebf 100644 --- a/Sprint-2/stretch/count-words.js +++ b/Sprint-2/stretch/count-words.js @@ -12,6 +12,8 @@ countWords("you and me and you") then the target output is { you: 2, and: 2, me: 1 } + + To complete this exercise you should understand - Strings and string manipulation - Loops @@ -26,3 +28,16 @@ 3. Order the results to find out which word is the most common in the input */ +function countWords(str) { + let cleanStr = str.split(" "); + let objWords = {}; + for (let word of cleanStr) { + if (word !== "") continue; + if (objWords[word]) { + objWords[word]++; + } else { + objWords[word] = 1; + } + } + return objWords; +} diff --git a/Sprint-2/stretch/mode.js b/Sprint-2/stretch/mode.js index 3f7609d79..208b3f038 100644 --- a/Sprint-2/stretch/mode.js +++ b/Sprint-2/stretch/mode.js @@ -9,28 +9,29 @@ // into smaller functions using the stages above function calculateMode(list) { - // track frequency of each value - let freqs = new Map(); - + // Stage 1: Count frequencies + let freqs = {}; for (let num of list) { - if (typeof num !== "number") { - continue; - } + if (typeof num !== "number") continue; // skip non-number values - freqs.set(num, (freqs.get(num) || 0) + 1); + if (freqs[num] === undefined) { + freqs[num] = 1; + } else { + freqs[num] += 1; + } } - - // Find the value with the highest frequency + // Stage 2: 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; + let mode = null; + + for (let num in freqs) { + if (freqs[num] > maxFreq) { + maxFreq = freqs[num]; + mode = Number(num); // convert string key to number } } - return maxFreq === 0 ? NaN : mode; + return mode; } module.exports = calculateMode; diff --git a/Sprint-2/stretch/till.js b/Sprint-2/stretch/till.js index 6a08532e7..f61b806c3 100644 --- a/Sprint-2/stretch/till.js +++ b/Sprint-2/stretch/till.js @@ -8,24 +8,27 @@ function totalTill(till) { let total = 0; for (const [coin, quantity] of Object.entries(till)) { - total += coin * quantity; + const coinValue = parseInt(coin); // "50p" -> 50 + total += coinValue * quantity; } - return `£${total / 100}`; + return `£${(total / 100).toFixed(2)}`; } - 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 +// ===> target output ll be 4.40 pound. // b) Why do we need to use Object.entries inside the for...of loop in this function? +// answer ===> we need Object.entries inside the loop, as our input is a key-value pair object. to access them its the easiest way to convert them to key-value array and do the other operations. // c) What does coin * quantity evaluate to inside the for...of loop? +// answer ===> it operates multiplication on the key and the value. // 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..4751cfd92 --- /dev/null +++ b/Sprint-2/stretch/till.test.js @@ -0,0 +1,23 @@ +const totalTill = require("./till.js"); + +// writing tests: + +test("calculates total correctly for a standard till", () => { + const till = { "1p": 10, "5p": 6, "50p": 4, "20p": 10 }; + expect(totalTill(till)).toBe("£4.40"); +}); + +test("returns £0.00 when all coins are 0", () => { + const till = { "1p": 0, "2p": 0, "50p": 0 }; + expect(totalTill(till)).toBe("£0.00"); +}); + +test("calculates correctly with only a few coins", () => { + const till = { "1p": 100, "50p": 2 }; + expect(totalTill(till)).toBe("£2.00"); +}); + +test("works with a mix of coins", () => { + const till = { "5p": 4, "10p": 5, "20p": 2 }; + expect(totalTill(till)).toBe("£1.10"); // correct calculation +}); From 4b5aff7026be5f1b20a1e961af750d0c45c1d355 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Mon, 16 Mar 2026 17:49:59 +0000 Subject: [PATCH 05/11] Nothing special --- Sprint-2/package-lock.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sprint-2/package-lock.json b/Sprint-2/package-lock.json index 9b4c725d6..ceda7296e 100644 --- a/Sprint-2/package-lock.json +++ b/Sprint-2/package-lock.json @@ -56,6 +56,7 @@ "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.25.7", @@ -1368,6 +1369,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001663", "electron-to-chromium": "^1.5.28", From 748465baace8c4a63f15b2c82b36981fa8065048 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Sun, 22 Mar 2026 18:48:15 +0000 Subject: [PATCH 06/11] recipe.js has been refactored --- Sprint-2/debug/recipe.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index abb87ece7..ef823d17f 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -13,7 +13,5 @@ const recipe = { }; console.log(`${recipe.title} serves ${recipe.serves} -ingredients:`); -for (let fruits of recipe.ingredients) { - console.log(fruits); -} +ingredients: ${recipe.ingredients.join("\n")}`); +// this approach looks clean and advanced From 40215f88a9202992fdc29b5807436f44f639d24e Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Sun, 22 Mar 2026 19:18:14 +0000 Subject: [PATCH 07/11] Jest tests and contains function updated --- Sprint-2/implement/contains.js | 10 +++++---- Sprint-2/implement/contains.test.js | 32 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index b509ba799..d0458e69e 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,11 +1,13 @@ // start your function function contains(object, key) { - if (Array.isArray(object) && Object.keys(object).length === 0) { + // Reject arrays, null, or non-object types + if (object === null || typeof object !== "object" || Array.isArray(object)) { return false; - } else if (object.hasOwnProperty(key)) { - return true; - } else return false; + } + + // Safely check if the object has the property + return Object.prototype.hasOwnProperty.call(object, key); } let key = "name"; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 2713519ca..8e7015468 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -63,3 +63,35 @@ test("It return false when an array is passed instead of object", () => { let key = "name"; expect(contains(object, key)).toEqual(false); }); + +// New testes after receiving feedback ==> + +test("It return false when null is passed instead of object", () => { + let object = null; + let key = "name"; + expect(contains(object, key)).toEqual(false); +}); + +test("It return false when undefined is passed instead of object", () => { + let object = undefined; + let key = "name"; + expect(contains(object, key)).toEqual(false); +}); + +test("It return false when a number is passed instead of object", () => { + let object = 123; + let key = "name"; + expect(contains(object, key)).toEqual(false); +}); + +test("It return false when a string is passed instead of object", () => { + let object = "hello"; + let key = "name"; + expect(contains(object, key)).toEqual(false); +}); + +test("It return false when a boolean is passed instead of object", () => { + let object = true; + let key = "name"; + expect(contains(object, key)).toEqual(false); +}); From 17557e23c85f6e699165b1d326ea78e42c4b58d4 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Sun, 22 Mar 2026 19:23:05 +0000 Subject: [PATCH 08/11] replaced let with const in createLookup function --- Sprint-2/implement/lookup.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index 0a01c5652..d587847ab 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,9 +1,9 @@ // start your function; function createLookup(input) { - let output = {}; + const output = {}; for (let pair of input) { - let key = pair[0]; - let value = pair[1]; + const key = pair[0]; + const value = pair[1]; output[key] = value; } return output; From f5ad08e16d6725b4a6aaacde57ea19f14fa32c56 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Sun, 22 Mar 2026 19:42:58 +0000 Subject: [PATCH 09/11] tally function and jest tests updated --- Sprint-2/implement/tally.js | 18 +++++++++--------- Sprint-2/implement/tally.test.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index 6cddd4258..a60936249 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,13 +1,13 @@ function tally(arr) { - let output = {}; - if (!Array.isArray(arr)) { - throw new Error("Invalid input"); - } else if (arr.length === 0) { - return output; - } - for (let item of arr) { - let count = arr.filter((x) => x === item).length; - output[item] = count; + if (!Array.isArray(arr)) throw new Error("Invalid input"); + + const output = {}; + for (const item of arr) { + if (output[item]) { + output[item] += 1; // increment count if already exists + } else { + output[item] = 1; // first occurrence + } } return output; } diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 9a0776f30..aa7a55d51 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -46,3 +46,33 @@ test("it throw an error if given a string", () => { const str = "a"; expect(() => tally(str)).toThrowError(); }); +// ===> new tests after feedback; +// Invalid input: number +test("it throw an error if given a number", () => { + let num = 123; + expect(() => tally(num)).toThrowError(); +}); + +// Invalid input: null +test("it throw an error if given null", () => { + let value = null; + expect(() => tally(value)).toThrowError(); +}); + +// Invalid input: undefined +test("it throw an error if given undefined", () => { + let value = undefined; + expect(() => tally(value)).toThrowError(); +}); + +// Invalid input: boolean +test("it throw an error if given a boolean", () => { + let value = true; + expect(() => tally(value)).toThrowError(); +}); + +// Invalid input: object +test("it throw an error if given an object instead of array", () => { + let obj = { a: 1 }; + expect(() => tally(obj)).toThrowError(); +}); From 05d578c337025353420cb8c995dc3b5748124f83 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Mon, 23 Mar 2026 12:50:24 +0000 Subject: [PATCH 10/11] count-words and mode function have been refactored and updated --- Sprint-2/stretch/count-words.js | 13 +++++------ Sprint-2/stretch/mode.js | 38 +++++++++++++++++---------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Sprint-2/stretch/count-words.js b/Sprint-2/stretch/count-words.js index 78bd31ebf..c630040bf 100644 --- a/Sprint-2/stretch/count-words.js +++ b/Sprint-2/stretch/count-words.js @@ -29,14 +29,15 @@ 3. Order the results to find out which word is the most common in the input */ function countWords(str) { - let cleanStr = str.split(" "); - let objWords = {}; + const cleanStr = str.split(" "); + const objWords = Object.create(null); // object with no prototype; for (let word of cleanStr) { - if (word !== "") continue; - if (objWords[word]) { - objWords[word]++; + if (word === "") continue; + const lower = word.toLowerCase(); // normalize case + if (objWords[lower]) { + objWords[lower]++; } else { - objWords[word] = 1; + objWords[lower] = 1; } } return objWords; diff --git a/Sprint-2/stretch/mode.js b/Sprint-2/stretch/mode.js index 208b3f038..d2d633749 100644 --- a/Sprint-2/stretch/mode.js +++ b/Sprint-2/stretch/mode.js @@ -8,30 +8,32 @@ // refactor calculateMode by splitting up the code // into smaller functions using the stages above -function calculateMode(list) { - // Stage 1: Count frequencies - let freqs = {}; - for (let num of list) { - if (typeof num !== "number") continue; // skip non-number values - - if (freqs[num] === undefined) { - freqs[num] = 1; - } else { - freqs[num] += 1; - } +function countFrequencies(list) { + const freqs = new Map(); // used Map instead of {} + for (const num of list) { + if (typeof num !== "number") continue; + freqs.set(num, (freqs.get(num) || 0) + 1); } - // Stage 2: Find the value with the highest frequency + return freqs; +} + +function findMode(freqs) { let maxFreq = 0; let mode = null; - - for (let num in freqs) { - if (freqs[num] > maxFreq) { - maxFreq = freqs[num]; - mode = Number(num); // convert string key to number + for (const [num, freq] of freqs) { + if (freq > maxFreq) { + maxFreq = freq; + mode = num; } } - return mode; } +function calculateMode(list) { + const freqs = countFrequencies(list); + return findMode(freqs); +} + +console.log(calculateMode([1, 2, 2, 3, 3, 3, 4])); // Output: 3 + module.exports = calculateMode; From a76c37bad275319f86ec41d7f20a79f0d549ac08 Mon Sep 17 00:00:00 2001 From: RomanSanaye Date: Tue, 24 Mar 2026 09:54:54 +0000 Subject: [PATCH 11/11] Add test to ensure arrays are rejected in contains function --- Sprint-2/implement/contains.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 8e7015468..e46165b4c 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -58,9 +58,9 @@ test("It returns false when the object does NOT have the specified property", () // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error -test("It return false when an array is passed instead of object", () => { - let object = [1, 2, 3]; - let key = "name"; +test("It returns false even if array contains the key (arrays are invalid)", () => { + const object = [1, 2, 3]; + const key = 0; expect(contains(object, key)).toEqual(false); });