From b88166f3cd73f3199ec4630b1f284d0329165e0d Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 19:45:12 +0000 Subject: [PATCH 01/52] Fix console log to correctly display house number from address object --- Sprint-2/debug/address.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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}`); From 7158a9b1ffd2bbb156d52fe7324566b2500aba2d Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 19:46:21 +0000 Subject: [PATCH 02/52] Remove commented-out code and explanations from address.js --- Sprint-2/debug/address.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 36d2f865d..0886a5278 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,9 +1,3 @@ -// Predict and explain first... - -// This code should log out the houseNumber from the address object -// but it isn't working... -// Fix anything that isn't working - const address = { houseNumber: 42, street: "Imaginary Road", From 16a2b1a4eb1c911caf12666ce3e7f43bb2d9b8ae Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 19:58:04 +0000 Subject: [PATCH 03/52] Add explanation for for...of loop issue in author.js --- Sprint-2/debug/author.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..2ba2284a9 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -3,6 +3,10 @@ // 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 +// explanation: +// The code could not be executed because the for...of loop could not access object "author". +// for...loops are used in arrays, strings or with certain array methods. + const author = { firstName: "Zadie", lastName: "Smith", From b124b4dc19d13aa8b3fcb55740e7748faa15444f Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 19:59:36 +0000 Subject: [PATCH 04/52] Fix for...of loop to correctly iterate over author object properties --- Sprint-2/debug/author.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 2ba2284a9..63eec98c1 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -15,6 +15,6 @@ const author = { alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { console.log(value); } From fa4aee7c5a064cc75bfcf5fedb9ea088884b2be5 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 19:59:59 +0000 Subject: [PATCH 05/52] Remove unnecessary comments and explanations from author.js --- Sprint-2/debug/author.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 63eec98c1..925ea5f54 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,8 +1,3 @@ -// Predict and explain first... - -// 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 - // explanation: // The code could not be executed because the for...of loop could not access object "author". // for...loops are used in arrays, strings or with certain array methods. From ee40cba11f4615174035b20304e0991674ab4268 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 20:06:24 +0000 Subject: [PATCH 06/52] refactor console log to correctly display ingredients list in recipe.js --- Sprint-2/debug/recipe.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..a413b3332 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -12,4 +12,4 @@ const recipe = { console.log(`${recipe.title} serves ${recipe.serves} ingredients: -${recipe}`); +${recipe.ingredients.join("\n")}`); From f4617bf3bf9934320f481d0b1bea42405cdd0e22 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 20:08:00 +0000 Subject: [PATCH 07/52] Remove unnecessary comments from recipe.js --- Sprint-2/debug/recipe.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index a413b3332..e6230ac41 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,9 +1,3 @@ -// Predict and explain first... - -// 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? - const recipe = { title: "bruschetta", serves: 2, From 106c55f1c4ce090eb875934c13f1c89d137173d1 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 20:24:45 +0000 Subject: [PATCH 08/52] Implement test for contains function on empty object --- Sprint-2/implement/contains.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 719272787..ecfa3f5c5 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -23,7 +23,9 @@ as an array isn't an object // 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({}, "name")).toBe(false); +}); // Given an object with properties // When passed to contains with an existing property name From 9f620988b544bbaeff669dce09ecb0da4a429c8a Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 20:25:30 +0000 Subject: [PATCH 09/52] Implement contains function to return false --- Sprint-2/implement/contains.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..1a566c258 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,5 @@ -function contains() {} +function contains() { + return false; +} module.exports = contains; From 51be35cc252ee9c8437c724a7617ecddcadb7956 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 20:50:20 +0000 Subject: [PATCH 10/52] Add test for contains function with existing property --- Sprint-2/implement/contains.test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index ecfa3f5c5..276386018 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -30,6 +30,9 @@ test("contains on empty object returns false", () => { // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test("contains on object with existing property returns true", () => { + expect(contains({ name: "Moses", age: 30 }, "name")).toBe(true); +}); // Given an object with properties // When passed to contains with a non-existent property name From f2a26fce347d1c9a0631bf0b88ddcb44bf6b5833 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 20:53:39 +0000 Subject: [PATCH 11/52] Implement contains function to check for property existence in object --- Sprint-2/implement/contains.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index 1a566c258..de6e9c3e0 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,5 +1,5 @@ -function contains() { - return false; +function contains(object, property) { + return Object.hasOwn(object, property); } module.exports = contains; From 96cdc69ac158f1181bcc266b747d27dc5531def5 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:03:07 +0000 Subject: [PATCH 12/52] Add tests for contains function with non-existent property and error handling --- Sprint-2/implement/contains.test.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 276386018..00b283f0d 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -37,9 +37,17 @@ test("contains on object with existing property returns true", () => { // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test("contains on object with non-existent property returns false", () => { + expect(contains({ name: "Moses", age: 30 }, "city")).toBe(false); +}); // Given a value that isn't an object - an array, a string, a number, // null, or no argument at all // When passed to contains // Then it should throw Error("contains requires an object") // (careful: typeof [] and typeof null are both "object") +test("contains throws an error for an array", () => { + expect(() => contains([1, 2, 3], "name")).toThrow( + "contains requires an object" + ); +}); From 990790e93fff5596639044780209d67f3e81cf9c Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:04:34 +0000 Subject: [PATCH 13/52] Add test for contains function to throw error for string input --- Sprint-2/implement/contains.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 00b283f0d..5ca725663 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -51,3 +51,9 @@ test("contains throws an error for an array", () => { "contains requires an object" ); }); + +test("contains throws an error for a string", () => { + expect(() => contains("Manchester", "name")).toThrow( + "contains requires an object" + ); +}); From 8ea5fab7cb883a97146b19a447f29275483aabe9 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:05:50 +0000 Subject: [PATCH 14/52] Add test for contains function to throw error for number input --- Sprint-2/implement/contains.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 5ca725663..297a625d8 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -57,3 +57,7 @@ test("contains throws an error for a string", () => { "contains requires an object" ); }); + +test("contains throws an error for a number", () => { + expect(() => contains(30, "name")).toThrow("contains requires an object"); +}); From e586a0bf07291e59570f940c2c13afe11e390339 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:06:37 +0000 Subject: [PATCH 15/52] Add test for contains function to throw error for null input --- Sprint-2/implement/contains.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 297a625d8..6f0579912 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -61,3 +61,7 @@ test("contains throws an error for a string", () => { test("contains throws an error for a number", () => { expect(() => contains(30, "name")).toThrow("contains requires an object"); }); + +test("contains throws an error for null", () => { + expect(() => contains(null, "name")).toThrow("contains requires an object"); +}); From 9b77ec849188f780303a556d6eeecaa671d84d8e Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:07:34 +0000 Subject: [PATCH 16/52] Add test for contains function to throw error when no argument is given --- Sprint-2/implement/contains.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 6f0579912..202fb3ea9 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -65,3 +65,7 @@ test("contains throws an error for a number", () => { test("contains throws an error for null", () => { expect(() => contains(null, "name")).toThrow("contains requires an object"); }); + +test("contains throws an error when no argument is given", () => { + expect(() => contains()).toThrow("contains requires an object"); +}); From 3ede0847677a034dd6d5e9631ff7e9b1fc6e64e9 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:09:07 +0000 Subject: [PATCH 17/52] Remove unnecessary comments from contains test file --- Sprint-2/implement/contains.test.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 202fb3ea9..19c6c9e4c 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -1,19 +1,5 @@ const contains = require("./contains.js"); -/* -Implement a function called contains that checks an object contains a -particular property - -E.g. contains({a: 1, b: 2}, 'a') // returns true -as the object contains a key of 'a' - -E.g. contains({a: 1, b: 2}, 'c') // returns false -as the object doesn't contains a key of 'c' - -E.g. contains([1, 2, 3], 'a') throws Error("contains requires an object") -as an array isn't an object -*/ - // Acceptance criteria: // Given a contains function From 26939b17b88b4c627b1375eb06b2bbfdc67f8d70 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:11:29 +0000 Subject: [PATCH 18/52] Add validation to contains function to ensure input is an object --- Sprint-2/implement/contains.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index de6e9c3e0..f96b86673 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,4 +1,7 @@ function contains(object, property) { + if (typeof object !== "object" || object === null || Array.isArray(object)) { + throw new Error("contains requires an object"); + } return Object.hasOwn(object, property); } From c6044d52d264e6980753816d48e43adb6d8053f1 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:27:40 +0000 Subject: [PATCH 19/52] Implement test for country-currency pairs --- Sprint-2/implement/lookup.test.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..3c052eaf6 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,14 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); +test("creates a country currency code lookup for multiple codes", () => { + const pairs = [ + ["US", "USD"], + ["CA", "CAD"], + ]; + const result = createLookup(pairs); + + expect(result).toEqual({ US: "USD", CA: "CAD" }); +}); /* From 8c357a8ad817c8dde13212f2c6dd4a1e71232e1a Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:28:19 +0000 Subject: [PATCH 20/52] Implement createLookup function to map country codes to currency codes --- Sprint-2/implement/lookup.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..c24eeec29 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,13 @@ -function createLookup() { - // implementation here +function createLookup(pairs) { + const lookup = []; + + for (const pair of pairs) { + const countryCode = pair[0]; + const currencyCode = pair[1]; + + lookup[countryCode] = currencyCode; + } + return lookup; } module.exports = createLookup; From df599c5906079606e11317a6f8b7fd9faafecb92 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:32:04 +0000 Subject: [PATCH 21/52] Refactor createLookup function to use an object instead of an array for lookup --- Sprint-2/implement/lookup.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index c24eeec29..8d214aa97 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,5 @@ function createLookup(pairs) { - const lookup = []; + const lookup = {}; for (const pair of pairs) { const countryCode = pair[0]; From 6440839ed9737f69d8bfe2277b371d9edb34c371 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:33:16 +0000 Subject: [PATCH 22/52] Remove unnecessary comments from lookup test file --- Sprint-2/implement/lookup.test.js | 32 ------------------------------- 1 file changed, 32 deletions(-) diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 3c052eaf6..ada04588c 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -9,35 +9,3 @@ test("creates a country currency code lookup for multiple codes", () => { expect(result).toEqual({ US: "USD", CA: "CAD" }); }); - -/* - -Create a lookup object of key value pairs from an array of code pairs - -Acceptance Criteria: - -Given - - An array of arrays representing country code and currency code pairs - e.g. [['US', 'USD'], ['CA', 'CAD']] - -When - - createLookup function is called with the country-currency array as an argument - -Then - - It should return an object where: - - The keys are the country codes - - The values are the corresponding currency codes - -Example -Given: [['US', 'USD'], ['CA', 'CAD']] - -When -createLookup(countryCurrencyPairs) is called - -Then -It should return: - { - 'US': 'USD', - 'CA': 'CAD' - } -*/ From dc9fbcd566115b27909bc78fdc3827aa586f20f0 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:40:50 +0000 Subject: [PATCH 23/52] Implement test for tally function on an empty array --- Sprint-2/implement/tally.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index ca763c296..55a4c77ad 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -23,7 +23,9 @@ 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("tally on an empty array returns an empty object", () => { + expect(tally([])).toEqual({}); +}); // Given an array with duplicate items // When passed to tally From cbe89f11dbbf206561f73e2d406621dabfad1847 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:41:24 +0000 Subject: [PATCH 24/52] Implement return statement in tally function --- Sprint-2/implement/tally.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..011b683a8 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,5 @@ -function tally() {} +function tally() { + return {}; +} module.exports = tally; From 0df25388be10c0157732b08b9ffcf28238917739 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:50:41 +0000 Subject: [PATCH 25/52] Add test for tally function to count duplicate items --- Sprint-2/implement/tally.test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 55a4c77ad..7fce65e22 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -30,6 +30,9 @@ test("tally on an empty array returns an empty object", () => { // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item +test("tally counts duplicate items", () => { + expect(tally(["a", "a", "a"])).toEqual({ a: 3 }); +}); // Given an invalid input like a string, a number, or no argument at all // When passed to tally From c08d7ad2e7716c7b8b908b91beabf2a99ee13f0a Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:52:21 +0000 Subject: [PATCH 26/52] Add test for tally function to verify counts for unique items --- Sprint-2/implement/tally.test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 7fce65e22..35254ddb2 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -33,6 +33,9 @@ test("tally on an empty array returns an empty object", () => { test("tally counts duplicate items", () => { expect(tally(["a", "a", "a"])).toEqual({ a: 3 }); }); +test("tally returns count for each unique item", () => { + expect(tally(["a", "a", "b", "c"])).toEqual({ a: 2, b: 1, c: 1 }); +}); // Given an invalid input like a string, a number, or no argument at all // When passed to tally From f82e06420e945a9883afb435497b938e6615908a Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 21:59:37 +0000 Subject: [PATCH 27/52] Implement tally function to count occurrences of items in an array --- Sprint-2/implement/tally.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index 011b683a8..f548aecef 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,5 +1,14 @@ -function tally() { - return {}; +function tally(items) { + const counts = {}; + + for (const item of items) { + if (counts[item] === undefined) { + counts[item] = 1; + } else { + counts[item] += 1; + } + } + return counts; } module.exports = tally; From b6f88e865650149279a98b5b8211fd55bcdd7002 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 22:15:24 +0000 Subject: [PATCH 28/52] Add test for tally function to handle invalid input --- Sprint-2/implement/tally.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 35254ddb2..2dcbde96c 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -40,3 +40,8 @@ test("tally returns count for each unique item", () => { // Given an invalid input like a string, a number, or no argument at all // When passed to tally // Then it should throw Error("tally requires an array") +test("tally throws an error when given an invalid input", () => { + expect(() => tally("hello")).toThrow(new Error("tally requires an array")); + expect(() => tally(30)).toThrow(new Error("tally requires an array")); + expect(() => tally()).toThrow(new Error("tally requires an array")); +}); From b8fe136a4b9b9b27d2f315f3c37eaa79a8f93f5e Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 22:17:33 +0000 Subject: [PATCH 29/52] Add input validation to tally function to ensure an array is provided --- Sprint-2/implement/tally.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f548aecef..7ab2d0dc6 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,4 +1,8 @@ function tally(items) { + if (!Array.isArray(items)) { + throw new Error("tally requires an array"); + } + const counts = {}; for (const item of items) { From 625a080d8f660df50d68a4b12f21380b826b5149 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 22:19:18 +0000 Subject: [PATCH 30/52] Remove unnecessary comments from tally test file for clarity --- Sprint-2/implement/tally.test.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2dcbde96c..ab50cbead 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -1,19 +1,5 @@ const tally = require("./tally.js"); -/** - * tally array - * - * In this task, you'll need to implement a function called tally - * that will take a list of items and count the frequency of each item - * in an array - * - * For example: - * - * tally(['a']), target output: { a: 1 } - * tally(['a', 'a', 'a']), target output: { a: 3 } - * tally(['a', 'a', 'b', 'c']), target output: { a : 2, b: 1, c: 1 } - */ - // Acceptance criteria: // Given a function called tally From 86acb174f5a39d3fde17ad61c3f77722d67827b9 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 22:37:21 +0000 Subject: [PATCH 31/52] Refactor parseQueryString to use indexOf and slice for key-value extraction --- Sprint-2/implement/querystring.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..84b6a41fa 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,7 +6,9 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + const index = pair.indexOf("="); + const key = pair.slice(0, index); + const value = pair.slice(index + 1); queryParams[key] = value; } From dff6f8c975653d7df3d603a2876d9f07a35d08e7 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 22:38:56 +0000 Subject: [PATCH 32/52] Fix parseQueryString to skip empty key-value pairs in query string --- Sprint-2/implement/querystring.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 84b6a41fa..b69ced1fd 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,6 +6,8 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { + if (pair === "") continue; + const index = pair.indexOf("="); const key = pair.slice(0, index); const value = pair.slice(index + 1); From 50c10376b99a82df84ed0e7fec3adfed318d1daf Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 22:44:48 +0000 Subject: [PATCH 33/52] Refactor parseQueryString to handle key-value extraction with conditional checks --- Sprint-2/implement/querystring.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index b69ced1fd..96c05a61b 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -9,8 +9,9 @@ function parseQueryString(queryString) { if (pair === "") continue; const index = pair.indexOf("="); - const key = pair.slice(0, index); - const value = pair.slice(index + 1); + const separator = index !== -1; + const key = separator ? pair.slice(0, index) : pair; + const value = separator ? pair.slice(index + 1) : ""; queryParams[key] = value; } From 53bcc81b136533c4fd4201ce971bffd420020927 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 22:54:16 +0000 Subject: [PATCH 34/52] Enhance parseQueryString to decode keys and values for improved query parameter handling --- Sprint-2/implement/querystring.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 96c05a61b..3c589930f 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,3 +1,7 @@ +function decode(text) { + return decodeURIComponent(text.replace(/\+/g, " ")); +} + function parseQueryString(queryString) { const queryParams = {}; if (queryString.length === 0) { @@ -10,8 +14,11 @@ function parseQueryString(queryString) { const index = pair.indexOf("="); const separator = index !== -1; - const key = separator ? pair.slice(0, index) : pair; - const value = separator ? pair.slice(index + 1) : ""; + const rawKey = separator ? pair.slice(0, index) : pair; + const rawValue = separator ? pair.slice(index + 1) : ""; + + const key = decode(rawKey); + const value = decode(rawValue); queryParams[key] = value; } From 40770d83b889d20f29596ca7195ff0aaf0e2039c Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 23:04:22 +0000 Subject: [PATCH 35/52] Refactor parseQueryString to support multiple values for the same key --- Sprint-2/implement/querystring.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 3c589930f..afc9ae1f1 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -19,7 +19,16 @@ function parseQueryString(queryString) { const key = decode(rawKey); const value = decode(rawValue); - queryParams[key] = value; + + if (key in queryParams) { + if (Array.isArray(queryParams[key])) { + queryParams[key].push(value); + } else { + queryParams[key] = [queryParams[key], value]; + } + } else { + queryParams[key] = value; + } } return queryParams; From 89aa7c840b9c7f12a5545b53d00106421b0cd220 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 23:11:04 +0000 Subject: [PATCH 36/52] add edge case test for malformed percent-encoding --- Sprint-2/implement/querystring.test.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 328b8df61..adbfc2965 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,7 +3,7 @@ // Below are some test cases the implementation doesn't handle well. // Fix the implementation for these tests, 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("should parse values containing '='", () => { expect(parseQueryString("equation=a=b-2")).toEqual({ @@ -46,3 +46,8 @@ test("should store values of a key in an array when the key has 2 or more values foo: "bar", }); }); + +// Edge cases +test("should not crash on malformed percent-encoding", () => { + expect(parseQueryString("a=%E0%A4%A")).toEqual({ a: "%E0%A4%A" }); +}); From 55bcc8ed09f98e1120f45bf901b55caf26e26f8b Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Thu, 5 Nov 2026 23:13:07 +0000 Subject: [PATCH 37/52] Refactor decode function to handle errors and replace plus signs with spaces --- Sprint-2/implement/querystring.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index afc9ae1f1..e457f3e56 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,5 +1,10 @@ function decode(text) { - return decodeURIComponent(text.replace(/\+/g, " ")); + const withSpaces = text.replace(/\+/g, " "); + try { + return decodeURIComponent(withSpaces); + } catch { + return withSpaces; + } } function parseQueryString(queryString) { From 01bf866bebe543528fff9dac4a3a7f422d9cc691 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:00:14 +0000 Subject: [PATCH 38/52] Update comment to clarify current return value for invert function --- Sprint-2/interpret/invert.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..5e6c342b9 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -17,6 +17,7 @@ function invert(obj) { } // a) What is the current return value when invert is called with { a : 1 } +// Current return value is 1. // b) What is the current return value when invert is called with { a: 1, b: 2 } From 3e4344a75d98e2eced5b2a74f1c67de47728517b Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:00:39 +0000 Subject: [PATCH 39/52] Update comment to clarify current return value for invert function with multiple key-value pairs --- Sprint-2/interpret/invert.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index 5e6c342b9..8d2bbd66b 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -20,6 +20,7 @@ function invert(obj) { // Current return value is 1. // b) What is the current return value when invert is called with { a: 1, b: 2 } +// The current return value is 2. // c) What is the target return value when invert is called with {a : 1, b: 2} From b3d7030499e8d93de9cf45e192f67be005da9a53 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:01:16 +0000 Subject: [PATCH 40/52] Update comment to clarify target return value for invert function with multiple key-value pairs --- Sprint-2/interpret/invert.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index 8d2bbd66b..7a88199b6 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -23,6 +23,7 @@ function invert(obj) { // The current return value is 2. // c) What is the target return value when invert is called with {a : 1, b: 2} +// The target value is { "1": "a", "2": "b"} // c) What does Object.entries return? Why is it needed in this program? From 4ebef47bb8a265212482058812b8040e34310ded Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:02:46 +0000 Subject: [PATCH 41/52] Update comments to clarify the purpose of Object.entries in the invert function --- Sprint-2/interpret/invert.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index 7a88199b6..ace76e913 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -26,6 +26,8 @@ function invert(obj) { // The target 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 object [key, value] pairs. +// It converts an object into an array, so the loop works, since plain objects can't be used with loops. // d) Explain why the current return value is different from the target output From cffb378b3101c1ece74766e9877ee5fb0120a44c Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:03:48 +0000 Subject: [PATCH 42/52] Update comments to clarify the reason for the current return value in the invert function --- Sprint-2/interpret/invert.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index ace76e913..5529e66fd 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -30,5 +30,7 @@ function invert(obj) { // It converts an object into an array, so the loop works, since plain objects can't be used with loops. // d) Explain why the current return value is different from the target output +// It differs due to the fact that "invertedObj.key" uses the wrong notation, "key", instead of the variable's value. +// Also, adding brackets to the literal name, [key], would only copy the object not fix it. // e) Fix the implementation of invert (and write tests to prove it's fixed!) From f6adbf39621a0dc1425407924280909d02d7f092 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:06:38 +0000 Subject: [PATCH 43/52] Fix invert function to correctly swap keys and values; add initial test file --- Sprint-2/interpret/invert.js | 2 +- Sprint-2/interpret/invert.test.js | 0 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 Sprint-2/interpret/invert.test.js diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index 5529e66fd..f4893135e 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,7 +10,7 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; } return invertedObj; diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..e69de29bb From c5db3decef0e5e7649f61da153cb2628714ac2d8 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:13:13 +0000 Subject: [PATCH 44/52] Add invert function and initial test case for key-value swapping --- Sprint-2/interpret/invert.js | 2 ++ Sprint-2/interpret/invert.test.js | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index f4893135e..fbe899b3c 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -16,6 +16,8 @@ function invert(obj) { return invertedObj; } +module.exports = invert; + // a) What is the current return value when invert is called with { a : 1 } // Current return value is 1. diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js index e69de29bb..6876445d2 100644 --- a/Sprint-2/interpret/invert.test.js +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,5 @@ +const invert = require("./invert"); + +test("swaps the key and value of a single pair", () => { + expect(invert({ a: 1 })).toEqual({ 1: "a" }); +}); From f0daded0c24aab8abe861849c5f72fa1b932bfe3 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:16:46 +0000 Subject: [PATCH 45/52] Add test case for multiple key-value pairs in invert function --- Sprint-2/interpret/invert.test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js index 6876445d2..4545dca3a 100644 --- a/Sprint-2/interpret/invert.test.js +++ b/Sprint-2/interpret/invert.test.js @@ -3,3 +3,7 @@ const invert = require("./invert"); test("swaps the key and value of a single pair", () => { expect(invert({ a: 1 })).toEqual({ 1: "a" }); }); + +test("swaps every pair when there are multiple pairs", () => { + expect(invert({ a: 1, b: 2 })).toEqual({ 1: "a", 2: "b" }); +}); From a45c5ea065c8801a123c7205b02a359e931091c7 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:18:13 +0000 Subject: [PATCH 46/52] Update test case for multiple key-value pairs in invert function --- Sprint-2/interpret/invert.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js index 4545dca3a..f63004f6a 100644 --- a/Sprint-2/interpret/invert.test.js +++ b/Sprint-2/interpret/invert.test.js @@ -5,5 +5,5 @@ test("swaps the key and value of a single pair", () => { }); test("swaps every pair when there are multiple pairs", () => { - expect(invert({ a: 1, b: 2 })).toEqual({ 1: "a", 2: "b" }); + expect(invert({ x: 10, y: 20 })).toEqual({ 10: "a", 20: "b" }); }); From 41a07a18c6f15e3bd05dfd18cbd2ece34a485319 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:27:19 +0000 Subject: [PATCH 47/52] Add test case for returning an empty object in invert function --- Sprint-2/interpret/invert.test.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js index f63004f6a..1b3933ba8 100644 --- a/Sprint-2/interpret/invert.test.js +++ b/Sprint-2/interpret/invert.test.js @@ -5,5 +5,9 @@ test("swaps the key and value of a single pair", () => { }); test("swaps every pair when there are multiple pairs", () => { - expect(invert({ x: 10, y: 20 })).toEqual({ 10: "a", 20: "b" }); + expect(invert({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" }); +}); + +test("returns an empty object when given an empty object", () => { + expect(invert({})).toEqual({}); }); From 1d46b2242762b528777ca5412ed0c961d14ed904 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Fri, 6 Nov 2026 01:29:24 +0000 Subject: [PATCH 48/52] Remove unnecessary comments --- Sprint-2/interpret/invert.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index fbe899b3c..70eae6771 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -1,11 +1,3 @@ -// Let's define how invert should work - -// Given an object -// When invert is passed this object -// Then it should swap the keys and values in the object - -// E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} - function invert(obj) { const invertedObj = {}; From 69b6210173f548e2dd6a3f27760422c2c06f7f08 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Sun, 8 Nov 2026 21:04:42 +0000 Subject: [PATCH 49/52] Update error message tests in contains function for consistency --- Sprint-2/implement/contains.test.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 19c6c9e4c..2be0788d7 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -33,13 +33,11 @@ test("contains on object with non-existent property returns false", () => { // Then it should throw Error("contains requires an object") // (careful: typeof [] and typeof null are both "object") test("contains throws an error for an array", () => { - expect(() => contains([1, 2, 3], "name")).toThrow( - "contains requires an object" - ); + expect(() => contains([1, 2, 3], "0")).toThrow("contains requires an object"); }); test("contains throws an error for a string", () => { - expect(() => contains("Manchester", "name")).toThrow( + expect(() => contains("Manchester", "0")).toThrow( "contains requires an object" ); }); From 78a1f82b6829c3e7269b7f5e11dcdae26514117f Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Sun, 8 Nov 2026 21:15:50 +0000 Subject: [PATCH 50/52] Add test case for handling items named like built-in object properties in tally function --- Sprint-2/implement/tally.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index ab50cbead..3ef40e625 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -31,3 +31,8 @@ test("tally throws an error when given an invalid input", () => { expect(() => tally(30)).toThrow(new Error("tally requires an array")); expect(() => tally()).toThrow(new Error("tally requires an array")); }); + +// Tally handles items named like built-in object properties +test("tally handles items named like built-in object properties", () => { + expect(tally(["toString", "toString"])).toEqual({ toString: 2 }); +}); From 12a783ed70d85b68c28b6487977c98ab5994aa09 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Sun, 8 Nov 2026 21:18:50 +0000 Subject: [PATCH 51/52] Refactor tally function to simplify item counting logic --- Sprint-2/implement/tally.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index 7ab2d0dc6..e5dbcbcbf 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -6,10 +6,10 @@ function tally(items) { const counts = {}; for (const item of items) { - if (counts[item] === undefined) { - counts[item] = 1; - } else { + if (Object.hasOwn(counts, item)) { counts[item] += 1; + } else { + counts[item] = 1; } } return counts; From 63734356764d2880ce705bf143470319eb879385 Mon Sep 17 00:00:00 2001 From: Precious Moses Date: Sun, 8 Nov 2026 21:25:08 +0000 Subject: [PATCH 52/52] Update comments in invert function to clarify return values --- Sprint-2/interpret/invert.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index 70eae6771..039621169 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -11,10 +11,10 @@ function invert(obj) { module.exports = invert; // a) What is the current return value when invert is called with { a : 1 } -// Current return value is 1. +// Current return value is an object, { key: 1 } // b) What is the current return value when invert is called with { a: 1, b: 2 } -// The current return value is 2. +// The current return value is an object also, { key: 2 } // c) What is the target return value when invert is called with {a : 1, b: 2} // The target value is { "1": "a", "2": "b"}