From 1e86882113940527068076a34e13e128286d58fe Mon Sep 17 00:00:00 2001 From: Ali Date: Thu, 20 Nov 2025 20:44:25 +0000 Subject: [PATCH 1/9] Fix: correct access to houseNumber in address object --- Sprint-2/debug/address.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..7a4b4a680 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,4 +1,8 @@ // Predict and explain first... +// Prediction: The code will print "My house number is undefined". +// Explanation: The code uses address[0], but address is an object, not an array. +// Objects cannot be accessed using numeric indexes. Therefore address[0] does not exist +// and evaluates to undefined. // This code should log out the houseNumber from the address object // but it isn't working... @@ -12,4 +16,5 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +console.log(`My house number is ${address.houseNumber}`); +// Expected output: My house number is 42 \ No newline at end of file From 392cf81c3ad871843bc9acdec2d01f60b5b9fd15 Mon Sep 17 00:00:00 2001 From: Ali Date: Thu, 20 Nov 2025 20:51:36 +0000 Subject: [PATCH 2/9] Fix: update for...of loop to use Object.values for iterating over author object --- Sprint-2/debug/author.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..299792ae0 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,4 +1,9 @@ // Predict and explain first... +// Prediction: This code will throw a TypeError saying that author is not iterable. +// Explanation: The for...of loop only works on iterable objects (arrays, strings, maps, etc.). +// Regular objects like "author" are NOT iterable, so JavaScript cannot loop over them using for...of. +// To loop through an object's values, we need to use Object.values(author), Object.keys(author), +// or Object.entries(author). // This program attempts to log out all the property values in the object. // But it isn't working. Explain why first and then fix the problem @@ -11,6 +16,12 @@ const author = { alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { console.log(value); } +// Expected output: +// Zadie +// Smith +// writer +// 40 +// true \ No newline at end of file From 3df02499d59d8ad00c41ba8c03a49d2945d3440d Mon Sep 17 00:00:00 2001 From: Ali Date: Thu, 20 Nov 2025 20:52:38 +0000 Subject: [PATCH 3/9] Fix: correct logging of ingredients in recipe object --- Sprint-2/debug/recipe.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..296eda905 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,4 +1,11 @@ // Predict and explain first... +// Prediction: The code will not print the ingredients correctly. Instead of listing them, +// it will print "[object Object]" because the template string is trying to print +// the whole recipe object directly. +// Explanation: When you use ${recipe}, JavaScript tries to convert the entire object +// into a string, which results in "[object Object]". To log the ingredients properly, +// we must access recipe.ingredients and join or loop through them. +// Each ingredient should appear on its own line, so we need to format them correctly. // This program should log out the title, how many it serves and the ingredients. // Each ingredient should be logged on a new line @@ -11,5 +18,12 @@ const recipe = { }; console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +ingredients: +${recipe.ingredients.join("\n")}`); +// Expected output: +// bruschetta serves 2 +// ingredients: +// olive oil +// tomatoes +// salt +// pepper \ No newline at end of file From 3d1fb6a09f8d6995c946c13fc0d098cbd0a25e16 Mon Sep 17 00:00:00 2001 From: Ali Date: Thu, 20 Nov 2025 20:57:04 +0000 Subject: [PATCH 4/9] Implement contains function to check for property existence in an object --- Sprint-2/implement/contains.js | 47 +++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..c7c49c1e1 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,48 @@ -function contains() {} +function contains(obj, prop) { + // Validate that first argument is a non-null object and NOT an array + if (obj === null || typeof obj !== "object" || Array.isArray(obj)) { + return false; + } + + // Check if object has the given property + return Object.prototype.hasOwnProperty.call(obj, prop); +} module.exports = contains; + +/*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' +*/ + +// Acceptance criteria: + +// 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 + +// Given an empty object +// When passed to contains +// Then it should return false +console.log(contains({}, 'a')); // false + +// Given an object with properties +// When passed to contains with an existing property name +// Then it should return true +console.log(contains({a: 1, b: 2}, 'a')); // true + +// Given an object with properties +// When passed to contains with a non-existent property name +// Then it should return false +console.log(contains({a: 1, b: 2}, 'c')); // false + +// Given invalid parameters like an array +// When passed to contains +// Then it should return false or throw an error +console.log(contains([], 'a')); // false +console.log(contains(null, 'a')); // false \ No newline at end of file From 566abe2b5bdd36f88484bd126eddef52e81ef9b4 Mon Sep 17 00:00:00 2001 From: Ali Date: Thu, 20 Nov 2025 21:04:51 +0000 Subject: [PATCH 5/9] Implement tests and documentation for createLookup function to validate country-currency mapping --- Sprint-2/implement/lookup.js | 49 +++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..a63487aaf 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,46 @@ -function createLookup() { - // implementation here -} +const createLookup = require("./lookup.js"); -module.exports = createLookup; +test("creates a country currency code lookup for multiple codes", () => { + const countryCurrencyPairs = [ + ['US', 'USD'], + ['CA', 'CAD'], + ['GB', 'GBP'] + ]; + + const expected = { + US: 'USD', + CA: 'CAD', + GB: 'GBP' + }; + + expect(createLookup(countryCurrencyPairs)).toEqual(expected); +}); + +/*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 ad44c3ceb0e48a777fe911a6b22570c423a646dc Mon Sep 17 00:00:00 2001 From: Ali Date: Thu, 20 Nov 2025 21:15:11 +0000 Subject: [PATCH 6/9] Fix: improve query string parsing to handle values with '=' and update test for consistency --- Sprint-2/implement/querystring.js | 10 +++++----- Sprint-2/implement/querystring.test.js | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..998ffb8ea 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,13 +1,13 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { - return queryParams; - } + if (!queryString) return queryParams; + const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + // Split only on the first '=' to handle '=' in values + const [key, ...rest] = pair.split("="); + queryParams[key] = rest.join("=") || ""; // join rest to reconstruct value } return queryParams; 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", }); }); From 3d219450b0c85dbfed24b96f037a67dcfaa604ac Mon Sep 17 00:00:00 2001 From: Ali Date: Thu, 20 Nov 2025 21:19:31 +0000 Subject: [PATCH 7/9] Implement tally function to count occurrences of items in an array with input validation --- Sprint-2/implement/tally.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..459a301c7 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,15 @@ -function tally() {} +function tally(items) { + if (!Array.isArray(items)) { + throw new Error("Input must be an array"); + } + + const counts = {}; + + for (const item of items) { + counts[item] = (counts[item] || 0) + 1; + } + + return counts; +} module.exports = tally; From aa5f9a28b8c8ad12a8dd494bca4f99e12cb4e6da Mon Sep 17 00:00:00 2001 From: Ali Date: Thu, 20 Nov 2025 21:19:38 +0000 Subject: [PATCH 8/9] Fix invert function to correctly swap keys and values in the object --- Sprint-2/interpret/invert.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..039b264fe 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,30 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; // fixed: use value as key } return invertedObj; } // a) What is the current return value when invert is called with { a : 1 } +// Current return value (original code): { key: 1 } +// Correct return value (fixed code): { "1": "a" } // b) What is the current return value when invert is called with { a: 1, b: 2 } +// Current return value (original code): { key: 2 } +// Correct return value (fixed code): { "1": "a", "2": "b" } // c) What is the target return value when invert is called with {a : 1, b: 2} +// Target return value: { "1": "a", "2": "b" } // c) What does Object.entries return? Why is it needed in this program? +// Object.entries converts the object into an array of [key, value] pairs, e.g., [["a",1],["b",2]]. +// Needed to iterate over all key-value pairs so we can swap them in the new object. // d) Explain why the current return value is different from the target output +// The original code uses `invertedObj.key` which is literally the string "key" instead of the variable value. +// This overwrites the same property each time, giving only the last value. // e) Fix the implementation of invert (and write tests to prove it's fixed!) +// See function above: use `invertedObj[value] = key;` to correctly swap keys and values. From 7958d37e99bfc509724634d639978786343fa877 Mon Sep 17 00:00:00 2001 From: Ali Date: Fri, 28 Nov 2025 13:38:25 +0000 Subject: [PATCH 9/9] Refactor and fix various functions: update address and author object access, enhance recipe ingredient logging, improve contains function, add tests for tally and lookup functions, and fix query string parsing. --- Sprint-2/debug/address.js | 9 +--- Sprint-2/debug/author.js | 19 ++----- Sprint-2/debug/recipe.js | 26 +++------- Sprint-2/implement/contains.js | 52 +++---------------- Sprint-2/implement/contains.test.js | 38 ++++++++++++-- Sprint-2/implement/lookup.js | 55 ++++---------------- Sprint-2/implement/lookup.test.js | 13 ++++- Sprint-2/implement/querystring.js | 30 +++++++++-- Sprint-2/implement/querystring.test.js | 22 ++++++-- Sprint-2/implement/tally.js | 23 ++++++--- Sprint-2/implement/tally.test.js | 22 +++++++- Sprint-2/interpret/invert.js | 69 +++++++++++++++++++------- 12 files changed, 207 insertions(+), 171 deletions(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 7a4b4a680..3abde7b56 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,9 +1,5 @@ // Predict and explain first... -// Prediction: The code will print "My house number is undefined". -// Explanation: The code uses address[0], but address is an object, not an array. -// Objects cannot be accessed using numeric indexes. Therefore address[0] does not exist -// and evaluates to undefined. - +// this code will give an error because the address is not an array and we should use dot notation to access houseNumber. // This code should log out the houseNumber from the address object // but it isn't working... // Fix anything that isn't working @@ -16,5 +12,4 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address.houseNumber}`); -// Expected output: My house number is 42 \ No newline at end of file +console.log(`My house number is ${address.houseNumber}`); \ No newline at end of file diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 299792ae0..51aec4d8d 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,10 +1,5 @@ // Predict and explain first... -// Prediction: This code will throw a TypeError saying that author is not iterable. -// Explanation: The for...of loop only works on iterable objects (arrays, strings, maps, etc.). -// Regular objects like "author" are NOT iterable, so JavaScript cannot loop over them using for...of. -// To loop through an object's values, we need to use Object.values(author), Object.keys(author), -// or Object.entries(author). - +// this code will give an error because the for...of is generally used to iterate over iterable objects like arrays or strings. thus, we should use for...in loop to iterate over the properties of an 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 @@ -16,12 +11,6 @@ const author = { alive: true, }; -for (const value of Object.values(author)) { - console.log(value); -} -// Expected output: -// Zadie -// Smith -// writer -// 40 -// true \ No newline at end of file +for (const value in author) { + console.log(`${value}: ${author[value]}`); +}; \ No newline at end of file diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 296eda905..6819c8244 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,12 +1,5 @@ // Predict and explain first... -// Prediction: The code will not print the ingredients correctly. Instead of listing them, -// it will print "[object Object]" because the template string is trying to print -// the whole recipe object directly. -// Explanation: When you use ${recipe}, JavaScript tries to convert the entire object -// into a string, which results in "[object Object]". To log the ingredients properly, -// we must access recipe.ingredients and join or loop through them. -// Each ingredient should appear on its own line, so we need to format them correctly. - +// This code will not log out the ingredients correctly.we should access the ingredients array specifically. // 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? @@ -17,13 +10,10 @@ const recipe = { ingredients: ["olive oil", "tomatoes", "salt", "pepper"], }; -console.log(`${recipe.title} serves ${recipe.serves} -ingredients: -${recipe.ingredients.join("\n")}`); -// Expected output: -// bruschetta serves 2 -// ingredients: -// olive oil -// tomatoes -// salt -// pepper \ No newline at end of file +console.log(`${recipe.title} +serves ${recipe.serves} +ingredients:`); + +recipe.ingredients.forEach(ingredient => { + console.log(ingredient); +}); \ No newline at end of file diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index c7c49c1e1..db7e15c1b 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,48 +1,8 @@ -function contains(obj, prop) { - // Validate that first argument is a non-null object and NOT an array - if (obj === null || typeof obj !== "object" || Array.isArray(obj)) { +function contains(input, propertyName) { + if (input && typeof input === 'object' && !Array.isArray(input)) { + return input.hasOwnProperty(propertyName); + } return false; - } +}; - // Check if object has the given property - return Object.prototype.hasOwnProperty.call(obj, prop); -} - -module.exports = contains; - -/*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' -*/ - -// Acceptance criteria: - -// 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 - -// Given an empty object -// When passed to contains -// Then it should return false -console.log(contains({}, 'a')); // false - -// Given an object with properties -// When passed to contains with an existing property name -// Then it should return true -console.log(contains({a: 1, b: 2}, 'a')); // true - -// Given an object with properties -// When passed to contains with a non-existent property name -// Then it should return false -console.log(contains({a: 1, b: 2}, 'c')); // false - -// Given invalid parameters like an array -// When passed to contains -// Then it should return false or throw an error -console.log(contains([], 'a')); // false -console.log(contains(null, 'a')); // false \ No newline at end of file +module.exports = contains; \ No newline at end of file diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..90478d58f 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -16,20 +16,52 @@ 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("given a contains function ,returns true for existing property, false otherwise", () => { + const input = {a: 1, b: 2}; + const propertyName = 'a'; + const currentOutput = contains(input, propertyName); + const targetOutput = true; + expect(currentOutput).toBe(targetOutput); +}); // Given an empty object // When passed to contains // Then it should return false -test.todo("contains on empty object returns false"); - +test("given an empty object, contains returns false",()=>{ + const input = {}; + const propertyName = 'anyProp'; + const currentOutput = contains(input,propertyName); + const targetOutput = false; + expect(currentOutput).toBe(targetOutput); +}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test("given an object with properties, returns true for existing property",()=>{ + const input = {x:10,y:20,z:30}; + const propertyName = 'y'; + const currentOutput = contains(input,propertyName); + const targetOutput = true; + expect(currentOutput).toBe(targetOutput); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test("given an object with properties, returns false for non-existing property",()=>{ + const input = {x:10,y:20,z:30}; + const propertyName = 'a'; + const currentOutput = contains(input,propertyName); + const targetOutput = false; + expect(currentOutput).toBe(targetOutput); +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test("given invalid parameters like an array, contains returns false",()=>{ + const input = [1,2,3]; + const propertyName = '0'; + const currentOutput = contains(input,propertyName); + const targetOutput = false; + expect(currentOutput).toBe(targetOutput); +}); \ No newline at end of file diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a63487aaf..3d78d9059 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,46 +1,9 @@ -const createLookup = require("./lookup.js"); - -test("creates a country currency code lookup for multiple codes", () => { - const countryCurrencyPairs = [ - ['US', 'USD'], - ['CA', 'CAD'], - ['GB', 'GBP'] - ]; - - const expected = { - US: 'USD', - CA: 'CAD', - GB: 'GBP' - }; - - expect(createLookup(countryCurrencyPairs)).toEqual(expected); -}); - -/*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' - } -*/ +function createLookup(pairs) { + const lookup = {}; + for (const [countryCode, currencyCode] of pairs) { + lookup[countryCode] = currencyCode; + } + return lookup; +} + +module.exports = createLookup; \ No newline at end of file diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..b83e152a1 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,15 @@ 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 input = [['US', 'USD'], ['CA', 'CAD'], ['GB', 'GBP']]; + const currentOutput = createLookup(input); + const targetOutput = { + 'US': 'USD', + 'CA': 'CAD', + 'GB': 'GBP' + }; + expect(currentOutput).toEqual(targetOutput); +}); /* @@ -32,4 +41,4 @@ It should return: 'US': 'USD', 'CA': 'CAD' } -*/ +*/ \ No newline at end of file diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 998ffb8ea..51de262ed 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,16 +1,38 @@ function parseQueryString(queryString) { const queryParams = {}; - if (!queryString) return queryParams; + + if (!queryString) { + return queryParams; + } + + if (queryString.startsWith("?")) { + queryString = queryString.slice(1); + } const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - // Split only on the first '=' to handle '=' in values - const [key, ...rest] = pair.split("="); - queryParams[key] = rest.join("=") || ""; // join rest to reconstruct value + if (!pair) continue; + + const firstEq = pair.indexOf("="); + + let key; + let value; + + if (firstEq === -1) { + // No "=" found → key with empty value + key = decodeURIComponent(pair); + value = ""; + } else { + key = decodeURIComponent(pair.slice(0, firstEq)); + value = decodeURIComponent(pair.slice(firstEq + 1)); + } + + queryParams[key] = value; } return queryParams; } module.exports = parseQueryString; +console.log(parseQueryString("?text=Hello%20World&amount=5%25")); \ No newline at end of file diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 717ee6901..8fce8228b 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,24 @@ // 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 multiple key=value pairs", () => { + expect(parseQueryString("a=1&b=2")).toEqual({ a: "1", b: "2" }); +}); + +test("parses empty querystring", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("parses key with no '=' (a)", () => { + expect(parseQueryString("a")).toEqual({ a: "" }); +}); + +test("last value wins for duplicate keys", () => { + expect(parseQueryString("a=1&a=2")).toEqual({ a: "2" }); +}); \ No newline at end of file diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index 459a301c7..934ca410b 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,15 +1,22 @@ function tally(items) { - if (!Array.isArray(items)) { - throw new Error("Input must be an array"); - } + if (!Array.isArray(items)) { + throw new TypeError('Input must be an array'); + } - const counts = {}; + const tallyResult = Object.create(null); - for (const item of items) { - counts[item] = (counts[item] || 0) + 1; - } + for (const item of items) { + if (tallyResult[item] === undefined) { + tallyResult[item] = 1; + } else { + tallyResult[item]++; + } + } - return counts; + return tallyResult; } module.exports = tally; + +console.log("Test : tally with 'toString':"); +console.log(tally(["toString", "toString"])); \ No newline at end of file diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..64391033d 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -20,15 +20,35 @@ const tally = require("./tally.js"); // When passed an array of items // Then it should return an object containing the count for each unique item +test("tally returns counts for each unique item", () => { + const input = ['a', 'a', 'b', 'c']; + const expectedOutput = { a: 2, b: 1, 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 correctly", () => { + const input = ['a', 'b', 'a', 'c', 'b', 'a']; + const expectedOutput = { a: 3, b: 2, c: 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 non-array input", () => { + const input = "not an array"; + expect(() => tally(input)).toThrow(TypeError); +}); \ No newline at end of file diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index 039b264fe..7db085cdf 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -6,34 +6,69 @@ // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} +//function invert(obj) { +// const invertedObj = {}; + + // for (const [key, value] of Object.entries(obj)) { + // invertedObj.key = value; + // } + + // return invertedObj; +//} + +// a) What is the current return value when invert is called with { a : 1 }: {key: 1} + +// b) What is the current return value when invert is called with { a: 1, b: 2 }: {key: 2} + +// 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? +// Object.entries returns an array of a given object's own enumerable string-keyed property [key, value] pairs. It is needed in this program to iterate over each key-value pair in the object so that we can swap them when creating the inverted object. + +// d) Explain why the current return value is different from the target output? +// The current return value is different from the target output because in the line (invertedObj.key = value;), the property name 'key' is being used literally instead of using the variable (key). This means that every iteration of the loop is overwriting the same property 'key' in the inverted object, resulting in only the last value being stored. + +// e) Fix the implementation of invert (and write tests to prove it's fixed!) + +// Fixed implementation: + + function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj[value] = key; // fixed: use value as key + invertedObj[value] = key; } return invertedObj; } +module.exports = invert; -// a) What is the current return value when invert is called with { a : 1 } -// Current return value (original code): { key: 1 } -// Correct return value (fixed code): { "1": "a" } -// b) What is the current return value when invert is called with { a: 1, b: 2 } -// Current return value (original code): { key: 2 } -// Correct return value (fixed code): { "1": "a", "2": "b" } +// Test cases: -// c) What is the target return value when invert is called with {a : 1, b: 2} -// Target return value: { "1": "a", "2": "b" } +const invert = require('./invert'); -// c) What does Object.entries return? Why is it needed in this program? -// Object.entries converts the object into an array of [key, value] pairs, e.g., [["a",1],["b",2]]. -// Needed to iterate over all key-value pairs so we can swap them in the new object. +// single key: -// d) Explain why the current return value is different from the target output -// The original code uses `invertedObj.key` which is literally the string "key" instead of the variable value. -// This overwrites the same property each time, giving only the last value. +test('invert single key-value pair', () => { + expect(invert({ a: 1 })).toEqual({ "1": "a" }); +}); -// e) Fix the implementation of invert (and write tests to prove it's fixed!) -// See function above: use `invertedObj[value] = key;` to correctly swap keys and values. +// multiple keys: + +test('invert multiple key-value pairs', () => { + expect(invert({ a: 1, b: 2 })).toEqual({ "1": "a", "2": "b" }); +}); + +// Empty object: + +test('invert empty object', () => { + expect(invert({})).toEqual({}); +}); + +// Non-string values: + +test('invert with non-string values', () => { + expect(invert({ x: 10, y: 20 })).toEqual({ "10": "x", "20": "y" }); +}); \ No newline at end of file