From 841dc4cd104af4b716726ff52709a616a920073f Mon Sep 17 00:00:00 2001 From: zilinskyte Date: Wed, 19 Nov 2025 13:51:51 +0000 Subject: [PATCH 1/8] address bug predicted and fixed --- Sprint-2/debug/address.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..fb8c2029e 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -5,11 +5,11 @@ // Fix anything that isn't working const address = { - houseNumber: 42, + houseNumber: "42", street: "Imaginary Road", city: "Manchester", country: "England", postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +console.log(`My house number is ${address.houseNumber}`); \ No newline at end of file From c6f3d61a71ccfe5a662c4bbb7ebce562ea66274e Mon Sep 17 00:00:00 2001 From: zilinskyte Date: Wed, 19 Nov 2025 14:39:32 +0000 Subject: [PATCH 2/8] author bug predicted and fixed --- Sprint-2/debug/author.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..60a12a63e 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -7,10 +7,10 @@ const author = { firstName: "Zadie", lastName: "Smith", occupation: "writer", - age: 40, + age: "40", alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { console.log(value); } From b81e384113cfc43eb24189316c62940d6c8f6e5a Mon Sep 17 00:00:00 2001 From: zilinskyte Date: Wed, 19 Nov 2025 14:57:06 +0000 Subject: [PATCH 3/8] recipe bug predicted and fixed --- Sprint-2/debug/recipe.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..aac3d4c7b 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -5,11 +5,11 @@ // How can you fix it? const recipe = { - title: "bruschetta", + title: "bruschetta", serves: 2, ingredients: ["olive oil", "tomatoes", "salt", "pepper"], }; - -console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +console.log(`${recipe.title} \nserves ${recipe.serves} \nIngredients:`); +for (const ingredient of recipe.ingredients) { +console.log(ingredient); +} From c00af0fc207bf58eb9f2e8299ab79962b825228d Mon Sep 17 00:00:00 2001 From: zilinskyte Date: Wed, 19 Nov 2025 19:12:18 +0000 Subject: [PATCH 4/8] contains function - passes 5 cases --- Sprint-2/implement/contains.js | 7 ++++++- Sprint-2/implement/contains.test.js | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..d07ecdacb 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,8 @@ -function contains() {} +function contains(obj, prop) { + if (obj !== null && typeof obj === 'object' && !Array.isArray(obj)) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + return false; +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..7c4af6653 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -16,20 +16,40 @@ 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 +test("returns true for existing property and false for non-existing property", () => { + const obj = { a: 1, b: 2}; + expect(contains(obj, "a")).toBe(true); + expect(contains(obj, "c")).toBe(false); +}); // Given an empty object // When passed to contains // Then it should return false -test.todo("contains on empty object returns false"); +test("returns false if contains empty object", () => { + const obj = {}; + expect(contains(obj, "a")).toBe(false); +}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test("returns true for existing property", () => { + const obj = { firstName: "Gina", lastName: "Rogers" }; + expect(contains(obj, "firstName")).toBe(true); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test("returns false for non-existing property", () => { + const obj = { firstName: "Gina", lastName: "Rogers" }; + expect(contains(obj, "age")).toBe(false); +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test("handles invalid parameters", () => { + const arr = [1, 2, 3]; + expect(contains(arr, "0")).toBe(false); +}); From 527ecda0f8612cfcce3b1d8281452c24f5938dfb Mon Sep 17 00:00:00 2001 From: zilinskyte Date: Fri, 21 Nov 2025 14:23:47 +0000 Subject: [PATCH 5/8] lookup function wirtten and tested --- Sprint-2/implement/lookup.js | 18 ++++++++++++++++-- Sprint-2/implement/lookup.test.js | 17 +++++++++++++++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..d00dd5197 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,19 @@ -function createLookup() { - // implementation here +function createLookup(pairs) { + // function accepts one parameter: pairs of country-currency code arrays + if (!Array.isArray(pairs)) { + return {}; // Return empty object if input is not an array + } + + const lookup = {}; // Initialize empty lookup object, key-value pairs stored in this object + for (const pair of pairs) { + // Iterate over each pair in the input array + if (Array.isArray(pair) && pair.length >= 2) { + const [countryCode, currencyCode] = pair; // Destructuring first two elements of pair array and assigning them to country and currency + lookup[countryCode] = currencyCode; // Add country-currency pair to the lookup object - later entries overwrite earlier ones if duplicate country codes exist. + } +} + return lookup; } + module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..837776d70 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,7 +1,20 @@ 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 countryCurrencyPairs = [ + ["US", "USD"], + ["CA", "CAD"], + ["GB", "GBP"], + ]; + + const expectedLookup = { + US: "USD", + CA: "CAD", + GB: "GBP", + }; + + expect(createLookup(countryCurrencyPairs)).toEqual(expectedLookup); +}); /* Create a lookup object of key value pairs from an array of code pairs From 9d2c82242c6c45df5090b82821f4b2e8c65fcebb Mon Sep 17 00:00:00 2001 From: zilinskyte Date: Mon, 24 Nov 2025 16:02:03 +0000 Subject: [PATCH 6/8] tally function written and tested --- Sprint-2/implement/tally.js | 17 ++++++++++++++++- Sprint-2/implement/tally.test.js | 20 +++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..e8dfbf5ff 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,18 @@ -function tally() {} +function tally(items) { + if (!Array.isArray(items)) { + throw new Error("Input must be an array"); + } + const counts = {}; // empty object to store tally of key-value pairs + for (const item of items) { + // iterates through each element in the items array + if (counts[item]) { + // checks if the item already exists as a key in counts object + counts[item] += 1; // increments the count for that item by 1 + } else { + counts[item] = 1; // initializes the count for that item to 1 + } + } + return counts; // returns the final counts object +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..b27d8729f 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -19,16 +19,34 @@ 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("tally counts frequency of each item in an array", () => { + const input = ["a", "a", "b", "c", "b", "a"]; + const expectedOutput = { a: 3, b: 2, c: 1 }; + expect(tally(input)).toEqual(expectedOutput); +}); // 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", () => { + const input = []; + const expectedOutput = {}; + expect(tally(input)).toEqual(expectedOutput); +}); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item +test("tally counts each unique item in an array with duplicates", () => { + const input = ["x", "y", "x", "z", "y", "x"]; + const expectedOutput = { x: 3, y: 2, z: 1 }; + expect(tally(input)).toEqual(expectedOutput); +}); // Given an invalid input like a string // When passed to tally // Then it should throw an error +test("tally throws an error for invalid input", () => { + const input = "invalid input"; + expect(() => tally(input)).toThrow("Input must be an array"); +}); From 81cbc64b525a949ffc825ddd1dd58323200ac117 Mon Sep 17 00:00:00 2001 From: zilinskyte Date: Mon, 24 Nov 2025 16:51:35 +0000 Subject: [PATCH 7/8] querystring function implemented and tested --- Sprint-2/implement/querystring.js | 18 ++++++++---- Sprint-2/implement/querystring.test.js | 39 ++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..ec58b27ba 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,13 +1,21 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { - return queryParams; + if (!queryString || queryString.length === 0) { + return queryParams; // Return empty object for empty query string } - const keyValuePairs = queryString.split("&"); + const keyValuePairs = queryString.split("&"); // Split by '&' to get individual key-value pairs for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + // Iterate over each key-value pair + if (!pair.includes("=")) { + // Handle missing equals sign + queryParams[pair] = undefined; // Assign undefined for keys without equals sign + continue; // Move to the next pair + } + const [key, ...rest] = pair.split("="); // array destructured into key and rest of array. + // Split by '=' to separate key and value. + const value = rest.join("="); // Join back any '=' in the value using rest operator + queryParams[key] = value; // Assign key-value pair to the result object } return queryParams; diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..7dfbb4a1f 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,43 @@ // 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", + expect(parseQueryString("equation=x=y+1")).toEqual({ equation: "x=y+1" }); +}); + +test("parses empty string to empty object", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("parses single key-value pair", () => { + expect(parseQueryString("name=Gina")).toEqual({ name: "Gina" }); +}); + +test("parses multiple key-value pairs", () => { + expect(parseQueryString("name=Gina&age=23&city=Nottingham")).toEqual({ + name: "Gina", + age: "23", + city: "Nottingham", }); }); + +test("handles keys without values", () => { + expect(parseQueryString("name=&age=23")).toEqual({ name: "", age: "23" }); +}); + +test("handles values without keys", () => { + expect(parseQueryString("=Gina&age=23")).toEqual({ "": "Gina", age: "23" }); +}); + +test("handles missing equals sign", () => { + expect(parseQueryString("nameGina&age23")).toEqual({ + nameGina: undefined, + age23: undefined, + }); +}); + +test("handles repeated keys", () => { + expect(parseQueryString("name=Gina&name=Jane")).toEqual({ name: "Jane" }); +}); From 6156a96d7b9a809aabff2220b2d5b4c567fabe77 Mon Sep 17 00:00:00 2001 From: zilinskyte Date: Mon, 24 Nov 2025 17:21:49 +0000 Subject: [PATCH 8/8] improved invert function, added test case file, all questions answered --- Sprint-2/interpret/invert.js | 17 ++++++++++++++++- Sprint-2/interpret/invert.test.js | 22 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 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 bb353fb1f..5f0eb92fa 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -7,23 +7,38 @@ // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} function invert(obj) { + // Guard: only accept plain objects (not null, not arrays, not primitives) + if (obj === null || typeof obj !== 'object' || Array.isArray(obj)) { + return {}; + } + const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + // Ensure the value is used as a string key (object keys are strings) + invertedObj[String(value)] = key; } return invertedObj; } +module.exports = invert; + // a) What is the current return value when invert is called with { a : 1 } +// { '1': 'a' } // b) What is the current return value when invert is called with { a: 1, b: 2 } +// { '1': 'a', '2': 'b' } // c) What is the target return value when invert is called with {a : 1, b: 2} +// { '1': 'a', '2': 'b' } // c) What does Object.entries return? Why is it needed in this program? +// It returns an array of key-value pairs from the object. +// It is needed to iterate over each key-value pair in the input object. // d) Explain why the current return value is different from the target output +// Numeric key values are converted to strings in the output object keys. // e) Fix the implementation of invert (and write tests to prove it's fixed!) +// Invert function passes 4 edge case tests. diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..c8e5642b7 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,22 @@ +const invert = require('./invert'); + +describe('invert', () => { + test('inverts a single key', () => { + expect(invert({ a: 1 })).toEqual({ '1': 'a' }); + }); + + test('inverts multiple keys', () => { + expect(invert({ a: 1, b: 2 })).toEqual({ '1': 'a', '2': 'b' }); + }); + + test('duplicate values: last key wins', () => { + expect(invert({ a: 1, b: 1 })).toEqual({ '1': 'b' }); + }); + + test('non-object inputs return empty object', () => { + expect(invert(null)).toEqual({}); + expect(invert(undefined)).toEqual({}); + expect(invert(123)).toEqual({}); + expect(invert([1, 2, 3])).toEqual({}); + }); +});