From 534a974b2023de0faa5fb4b4407a50e88b497ec6 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Tue, 17 Mar 2026 15:47:11 +0000 Subject: [PATCH 01/12] Fix: correct access to houseNumber property in address object --- Sprint-2/debug/address.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..d4ae41a89 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,4 +1,5 @@ // Predict and explain first... +// Prediction: There will be an error because address[0] is trying to access an index instead of a property // This code should log out the houseNumber from the address object // but it isn't working... @@ -12,4 +13,4 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +console.log(`My house number is ${address.houseNumber}`); From 0d798f0055559b1358ad30ee91e39770f75b3d10 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Tue, 17 Mar 2026 16:14:42 +0000 Subject: [PATCH 02/12] Fix: correct iteration method for logging author properties --- Sprint-2/debug/author.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..afcdcadc9 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,4 +1,5 @@ // Predict and explain first... +// Prediction: The error is that it is trying to iterate over a non-array object. // 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,11 @@ const author = { alive: true, }; -for (const value of author) { - console.log(value); +for (const key in author) { + console.log(author[key]); } + +// for iterating in an object for...in can be used +// and each iteration it returns the key +// then each value can be accessed using the key with bracket notation + From 5670003686f9959d41711131ed329860a7b97963 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Tue, 17 Mar 2026 16:41:37 +0000 Subject: [PATCH 03/12] Fix: recipe to be logged as a string --- Sprint-2/debug/recipe.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..655960dfd 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,4 +1,5 @@ // Predict and explain first... +// Prediction: The error is that recipe is an object and it cannot be logged as a string. // This program should log out the title, how many it serves and the ingredients. // Each ingredient should be logged on a new line @@ -12,4 +13,4 @@ const recipe = { console.log(`${recipe.title} serves ${recipe.serves} ingredients: -${recipe}`); +${recipe.ingredients.join(", ")}`); From 25e375418de91b0fe331a420454ffa41e737c190 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Tue, 17 Mar 2026 17:15:40 +0000 Subject: [PATCH 04/12] add utility to check if object contains a key --- Sprint-2/implement/contains.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..ef9793875 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,15 @@ -function contains() {} +function contains(object, name) { + if (object == null || Object.keys(object).length === 0) { + return false; + } + + for (let key in object) { + if (key === name) { + return true; + } + } + + return false; +} module.exports = contains; From 0667ae6e23cf7152f8a2c933aa6491b79aad5805 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Tue, 17 Mar 2026 19:52:52 +0000 Subject: [PATCH 05/12] added unit tests for contains() --- Sprint-2/implement/contains.test.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..1999891f3 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -20,16 +20,29 @@ as the object doesn't contains a key of 'c' // 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({}, "key1")).toEqual(false)); // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test("contains returns true when object contains the given property", () => + expect(contains({ key1: "value1", key2: "value2" }, "key1")).toEqual(true)); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test("contains returns false when object does not contain the given property", () => + expect(contains({ key1: "value1", key2: "value2" }, "key4")).toEqual(false)); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +it("contains returns false or throws an error if given parameter is not a valid object", () => { + expect(contains([], "key1")).toEqual(false); + expect(contains([1, 2], 1)).toEqual(false); + expect(contains("key1:value1", "1")).toEqual(false); + expect(contains(5235, "key1")).toEqual(false); + expect(contains(undefined, "key1")).toEqual(false); + expect(contains(null, "key1")).toEqual(false); +}); From 0300033c7f7422903bba9aeef37b9fae57f7a0c9 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Fri, 27 Mar 2026 17:35:14 +0000 Subject: [PATCH 06/12] Implement createLookup to map country-currency pairs into an object --- Sprint-2/implement/lookup.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..d7dd553eb 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,13 @@ function createLookup() { - // implementation here + const array = {}; + + for (const pair of countryCurrencyPairs) { + const country = pair[0]; + const currency = pair[1]; + array[country] = currency; + } + + return array; } module.exports = createLookup; From 8a041ebd93cfc287a513991c8c9bd01396c569c0 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Fri, 27 Mar 2026 18:48:58 +0000 Subject: [PATCH 07/12] Add tests for createLookup covering invalid input, empty array, and valid mappings --- Sprint-2/implement/lookup.test.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..ddf69b5d0 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,7 +1,5 @@ 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 @@ -33,3 +31,24 @@ It should return: 'CA': 'CAD' } */ + +describe("createLookup", () => { + it("returns empty object if the parameter passed is not an array", () => { + expect(createLookup("US:USD")).toEqual({}); + expect(createLookup(undefined)).toEqual({}); + }); + + it("returns an object of country initials and currency code", () => + expect( + createLookup([ + ["US", "USD"], + ["CA", "CAD"], + ]) + ).toEqual({ + US: "USD", + CA: "CAD", + })); + + it("returns an empty object if passed an empty array", () => + expect(createLookup([])).toEqual({})); +}); From 3efccb10477d7d685390d5904b5f10214d23f51d Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Fri, 27 Mar 2026 20:27:40 +0000 Subject: [PATCH 08/12] Improved parseQueryString to support URL decoding, keys without values, and multiple values per key --- Sprint-2/implement/querystring.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..c4714832c 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,8 +6,28 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + let key = "", + value = ""; + let equalSignIndex = pair.indexOf("="); + + if (equalSignIndex === -1) { + key = decodeURIComponent(pair); + value = ""; + } else { + key = decodeURIComponent(pair.slice(0, equalSignIndex)); + value = decodeURIComponent(pair.slice(equalSignIndex + 1)); + } + + if (key === "" && value === "") continue; + if (queryParams[key]) { + if (queryParams[key] === value) continue; + if (Array.isArray(queryParams[key])) { + queryParams[key].push(value); + } else { + const temp = queryParams[key]; + queryParams[key] = [temp, value]; + } + } else queryParams[key] = value; } return queryParams; From 2b8e74264543439d38ee44505342294f5f5f0381 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Fri, 27 Mar 2026 21:35:45 +0000 Subject: [PATCH 09/12] Added parseQueryString tests for duplicate keys, encoded values, missing '=', and edge cases --- Sprint-2/implement/querystring.test.js | 41 ++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..fb873c6fd 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,47 @@ // 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", }); }); + +test("parses querystring values containing repetitive keys", () => { + expect(parseQueryString("a=1&b=6&a=2&a=3&b=7")).toEqual({ + a: ["1", "2", "3"], + b: ["6", "7"], + }); +}); + +test("parses querystring values containing repetitive keys with same values", () => { + expect(parseQueryString("a=1&b=6&a=2&a=3&b=6")).toEqual({ + a: ["1", "2", "3"], + b: "6", + }); +}); + +test("parses querystring values containing no key and value but only =", () => { + expect(parseQueryString("=&b=6&a=2&=&=")).toEqual({ + a: "2", + b: "6", + }); +}); + +test("parses querystring values missing = sign", () => { + expect(parseQueryString("id=5&name=mohsen&age")).toEqual({ + id: "5", + name: "mohsen", + age: "", + }); +}); + +test("parses querystring with encoded keys and values", () => { + expect( + parseQueryString( + "search=hello%20world&city=New%20York&search=happy%20coding" + ) + ).toEqual({ search: ["hello world", "happy coding"], city: "New York" }); +}); From 458dd9415f8dd9c55d75def6ff707b53247dd617 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Fri, 27 Mar 2026 22:18:51 +0000 Subject: [PATCH 10/12] Added tally function to count occurrences of items in an array --- Sprint-2/implement/tally.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..f2ba06655 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,19 @@ -function tally() {} +function tally(itemsList) { + const counts = {}; + + if (itemsList.length === 0) { + return {}; + } + + if (!Array.isArray(itemsList)) { + throw new Error("Input must be an array"); + } + + for (const item of itemsList) { + counts[item] = (counts[item] || 0) + 1; + } + + return counts; +} module.exports = tally; From 47d6c64bccf314bd5a153d7813e2d359c65b6479 Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Fri, 27 Mar 2026 23:24:46 +0000 Subject: [PATCH 11/12] Added tally tests for empty array, item counting, and invalid input. --- Sprint-2/implement/tally.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..1ba68ca2f 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -32,3 +32,21 @@ 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("returns an empty object when given an empty array", () => { + expect(tally([])).toEqual({}); + }); + + test("returns counts for each unique item", () => { + expect(tally(["a", "a", "b", "c"])).toEqual({ + a: 2, + b: 1, + c: 1, + }); + }); + + test("throws an error when input is not an array", () => { + expect(() => tally("abc")).toThrow(); + }); +}); From 31506e5b02973f0352e57b9f9570e2c66664ca0c Mon Sep 17 00:00:00 2001 From: Asha Ahmed Date: Sat, 28 Mar 2026 00:29:14 +0000 Subject: [PATCH 12/12] fixed: implement correct key-value inversion and add tests for invert function --- Sprint-2/interpret/invert.js | 52 ++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..7498dbb9a 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -17,13 +17,65 @@ function invert(obj) { } // a) What is the current return value when invert is called with { a : 1 } +// Answer: Object.entries({a: 1}) → [["a", 1]] +//loop runs once: +// key = "a", value = 1 +// invertedObj.key = value sets property "key" to 1 +{ key: 1 } // b) What is the current return value when invert is called with { a: 1, b: 2 } +// Answer: Object.entries({a: 1, b: 2}) → [["a", 1], ["b", 2]] +// loop: +// first iteration sets { key: 1 } +// second iteration overwrites it to { key: 2 } +{ key: 2 } // c) What is the target return value when invert is called with {a : 1, b: 2} +// Answer: “Invert” should swap keys and values, and object keys are strings, so numeric values become string keys. +{ "1": "a", "2": "b" } // c) What does Object.entries return? Why is it needed in this program? +// Answer: Object.entries(obj) returns an array of [key, value] pairs for an object’s own enumerable string-keyed properties. +// It’s useful here because it lets you iterate an object as pairs. +// So Object.entries is just a clean, safe way to get both key and value in one go. // d) Explain why the current return value is different from the target output +// Answer: The output is wrong because the code has two main bugs: it uses dot notation (invertedObj.key), which creates a literal "key" property instead of using the variable key, +// and it doesn’t actually swap keys and values, it assigns the value back instead of inverting. +// As a result, each loop iteration overwrites the same "key" property, leaving only the last value. // e) Fix the implementation of invert (and write tests to prove it's fixed!) + +function invert(obj) { + const invertedObj = {}; + + for (const [key, value] of Object.entries(obj)) { + invertedObj[String(value)] = key; // ensure keys are strings + } + + return invertedObj; +} + + +describe("invert", () => { + test("inverts a single key-value pair", () => { + expect(invert({ a: 1 })).toEqual({ "1": "a" }); + }); + + test("inverts multiple key-value pairs", () => { + expect(invert({ a: 1, b: 2 })).toEqual({ "1": "a", "2": "b" }); + }); + + test("stringifies numeric values as keys", () => { + const result = invert({ x: 10, y: 20 }); + expect(result).toEqual({ "10": "x", "20": "y" }); + }); + + test("later keys override earlier keys when values collide", () => { + expect(invert({ a: 1, b: 1 })).toEqual({ "1": "b" }); + }); + + test("works with string values too", () => { + expect(invert({ a: "cat", b: "dog" })).toEqual({ cat: "a", dog: "b" }); + }); +});