diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..3546131c8 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,7 +1,11 @@ // Predict and explain first... +//answer: this code defines an object called address, and it has several properties (key–value pairs). + // This code should log out the houseNumber from the address object // but it isn't working... +//answer : it doesnt work because in object, we dont use numeric index and the result is undefined + // Fix anything that isn't working const address = { @@ -12,4 +16,5 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +// console.log(`My house number is ${address[0]}`); wrong +console.log(`My house number is ${address.houseNumber}`); //correct diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..746ba6894 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -3,6 +3,8 @@ // 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 +//answer : the result is TypeError: author is not iterable Because objects are not iterable with for...of — that loop only works with arrays, strings, and other iterable types. + const author = { firstName: "Zadie", lastName: "Smith", @@ -11,6 +13,10 @@ const author = { alive: true, }; -for (const value of author) { +// for (const value of author) { wrong code +// console.log(value); +// } + +for (const value of Object.values(author)) { console.log(value); } diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..5a255b392 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -4,12 +4,24 @@ // Each ingredient should be logged on a new line // How can you fix it? +//answer : +// ${recipe.title} → "bruschetta" + +// ${recipe.serves} → 2 + +// ${recipe} → wrong! = > because recipe is an object, and when you interpolate it into a string, it becomes: + +// [object Object] + const recipe = { title: "bruschetta", serves: 2, ingredients: ["olive oil", "tomatoes", "salt", "pepper"], }; -console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +console.log(recipe.title + "serves" + recipe.serves); +console.log("ingredients:"); + +for (let i = 0; i < recipe.ingredients.length; i++) { + console.log(recipe.ingredients[i]); +} diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..4bb1ebb46 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,13 @@ -function contains() {} +// Implement the contains function +function contains(obj, propName) { + // Check that the first argument is an object, not null or an array + if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { + return false; + } + // Check if the object has the property + return obj.hasOwnProperty(propName); +} + +// Export the function so other files can use it module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..0e756f880 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -3,33 +3,31 @@ 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' */ -// 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 -test.todo("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 - -// Given an object with properties -// When passed to contains with a non-existent property name -// Then it should return false - -// Given invalid parameters like an array -// When passed to contains -// Then it should return false or throw an error +// Test 1: Empty object +test("contains on empty object returns false", () => { + expect(contains({}, "a")).toBe(false); +}); + +// Test 2: Existing property +test("returns true when the property exists in the object", () => { + const obj = { a: 1, b: 2 }; + expect(contains(obj, "a")).toBe(true); +}); + +// Test 3: Non-existent property +test("returns false when the property does not exist in the object", () => { + const obj = { a: 1, b: 2 }; + expect(contains(obj, "c")).toBe(false); +}); + +// Test 4: Invalid input - array +test("returns false if input is an array", () => { + expect(contains([], "a")).toBe(false); +}); + +// Test 5: Invalid input - null +test("returns false if input is null", () => { + expect(contains(null, "a")).toBe(false); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..76aedea36 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,22 @@ -function createLookup() { - // implementation here +// This function takes an array of country-currency pairs +// and turns it into an object where keys are country codes +// and values are currency codes +function createLookup(pairs) { + // Create an empty object to store the result + const lookup = {}; + + // Loop through each pair in the array + for (let i = 0; i < pairs.length; i++) { + const pair = pairs[i]; // e.g. ['US', 'USD'] + const country = pair[0]; // first element is country code + const currency = pair[1]; // second element is currency code + + lookup[country] = currency; // add it to the object + } + + // Return the final lookup object + return lookup; } +// Export the function so it can be used in tests or other files module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..54e0c8c64 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,35 +1,45 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); - /* - -Create a lookup object of key value pairs from an array of code pairs - -Acceptance Criteria: - -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' - } +Implement a function called createLookup that turns an array of +country-currency code pairs into an object. */ + +// Test 1: multiple pairs +test("creates a country currency code lookup for multiple codes", () => { + const input = [ + ["US", "USD"], + ["CA", "CAD"], + ["GB", "GBP"], + ]; + const expected = { + US: "USD", + CA: "CAD", + GB: "GBP", + }; + expect(createLookup(input)).toEqual(expected); +}); + +// Test 2: empty array should return empty object +test("returns empty object when given an empty array", () => { + const input = []; + const expected = {}; + expect(createLookup(input)).toEqual(expected); +}); + +// Test 3: single pair +test("works with a single country-currency pair", () => { + const input = [["JP", "JPY"]]; + const expected = { JP: "JPY" }; + expect(createLookup(input)).toEqual(expected); +}); + +// Test 4: duplicate keys (last one wins) +test("handles duplicate country codes (last value wins)", () => { + const input = [ + ["US", "USD"], + ["US", "USN"], + ]; + const expected = { US: "USN" }; // last one overwrites + expect(createLookup(input)).toEqual(expected); +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..73f81e400 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,13 +1,24 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { + + // Return empty object if input is empty + if (!queryString || queryString.length === 0) { return queryParams; } + const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + // Split only on the first '=' + const index = pair.indexOf("="); + if (index === -1) { + // No '=' in this pair, treat as key with empty string value + queryParams[pair] = ""; + } else { + const key = pair.slice(0, index); + const value = pair.slice(index + 1); + queryParams[key] = value; + } } return queryParams; diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..bb486cc76 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -1,12 +1,31 @@ -// In the prep, we implemented a function to parse query strings. -// Unfortunately, it contains several bugs! -// 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 1: normal key=value pairs +test("parses simple query string", () => { + expect(parseQueryString("a=1&b=2")).toEqual({ a: "1", b: "2" }); +}); +// Test 2: value contains '=' 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 3: empty string returns empty object +test("returns empty object for empty query string", () => { + expect(parseQueryString("")).toEqual({}); +}); + +// Test 4: key without value +test("parses key without value as empty string", () => { + expect(parseQueryString("keyWithoutValue")).toEqual({ keyWithoutValue: "" }); +}); + +// Test 5: multiple keys including empty value +test("parses multiple keys including empty values", () => { + expect(parseQueryString("a=1&b=&c=3")).toEqual({ a: "1", b: "", c: "3" }); +}); + +// Test 6: only & characters with empty keys +test("handles consecutive & correctly", () => { + expect(parseQueryString("a=1&&b=2")).toEqual({ a: "1", b: "2", "": "" }); }); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..5601bdda3 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,23 @@ -function tally() {} +function tally(items) { + // Check if input is an array + if (!Array.isArray(items)) { + throw new Error("Input must be an array"); + } + + const result = {}; + + // Loop through each item in the array + for (const item of items) { + // If the item already exists in result, increment the count + if (result[item]) { + result[item] += 1; + } else { + // Otherwise, initialize the count to 1 + result[item] = 1; + } + } + + return result; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..87ee7d556 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -1,34 +1,26 @@ 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 } - */ +// Test 1: empty array should return empty object +test("tally on an empty array returns an empty object", () => { + expect(tally([])).toEqual({}); +}); -// Acceptance criteria: +// Test 2: array with single item +test("counts a single item", () => { + expect(tally(["a"])).toEqual({ a: 1 }); +}); -// 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 3: array with multiple duplicates +test("counts duplicates correctly", () => { + expect(tally(["a", "a", "a"])).toEqual({ a: 3 }); +}); -// 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 4: array with multiple unique items +test("counts multiple unique items correctly", () => { + expect(tally(["a", "a", "b", "c"])).toEqual({ a: 2, b: 1, c: 1 }); +}); -// Given an array with duplicate items -// When passed to tally -// Then it should return counts for each unique item - -// Given an invalid input like a string -// When passed to tally -// Then it should throw an error +// Test 5: invalid input should throw an error +test("throws an error if input is not an array", () => { + expect(() => tally("not an array")).toThrow("Input must be an array"); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..ed25ee36e 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -18,12 +18,24 @@ function invert(obj) { // a) What is the current return value when invert is called with { a : 1 } +// // returns { key: 1 } : Wrong because invertedObj.key = value; literally uses the string "key" as a property instead of using the variable key + // b) What is the current return value when invert is called with { a: 1, b: 2 } +//// returns { key: 2 } Only one property "key" exists. It overwrites "key" twice. + // 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(obj) returns an array of key-value pairs. Needed because we want to loop over each key-value pair in the object. + // d) Explain why the current return value is different from the target output +//invertedObj.key = value; → sets the literal property "key" instead of using the variable value as the + // e) Fix the implementation of invert (and write tests to prove it's fixed!) + +// diff --git a/Sprint-3/package.json b/Sprint-3/package.json index 711a5390f..c58a56a6b 100644 --- a/Sprint-3/package.json +++ b/Sprint-3/package.json @@ -26,7 +26,7 @@ "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/user-event": "^14.6.1", - "jest": "^30.0.4", + "jest": "^30.2.0", "jest-environment-jsdom": "^30.0.4" } }