diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..2aeb91c2d 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -3,6 +3,7 @@ // This code should log out the houseNumber from the address object // but it isn't working... // Fix anything that isn't working +// It is not working because the object properties should be accessed using dot notation or bracket notation. const address = { houseNumber: 42, @@ -12,4 +13,4 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +console.log(`My house number is ${address["houseNumber"]}`); diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..388388184 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,4 +1,8 @@ // Predict and explain first... +// The for...of loop is used to iterate over iterable objects like arrays or strings. +// However, author is a plain object, which is not iterable by default. +// Therefore, using for...of directly on an object will result in a TypeError. +// To iterate over the values of an object, we can use Object.values(), Object.keys(), or a for...in loop. // 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 +15,10 @@ const author = { alive: true, }; -for (const value of author) { - console.log(value); -} +// for (const value of Object.values(author)) { +// console.log(value); +// } + +for (const key in author) { + console.log(author[key]); +} \ No newline at end of file diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..fa7db05ff 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,8 +1,7 @@ // 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", @@ -12,4 +11,4 @@ const recipe = { console.log(`${recipe.title} serves ${recipe.serves} ingredients: -${recipe}`); +${recipe.ingredients.join("\n")}`); diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..6420a8a14 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,16 @@ -function contains() {} + + +function contains(obj, prop) { + if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { + return false; + } + for (const key in obj) { + if (key === prop) { + return true; + } + } + return false; +} module.exports = contains; + diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..2d239e028 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -17,19 +17,37 @@ as the object doesn't contains a key of 'c' // 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"); +test("contains on empty object returns false", () => { + expect(contains({}, 'a')).toBe(false); +}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test("contains returns true for existing property", () => { + expect(contains({a: 1, b: 2}, 'a')).toBe(true); + expect(contains({x: 10, y: 20}, 'y')).toBe(true); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test("contains returns false for non-existent property", () => { + expect(contains({a: 1, b: 2}, 'c')).toBe(false); + expect(contains({x: 10, y: 20}, 'z')).toBe(false); +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test("contains returns false for invalid parameters", () => { + expect(contains([], 'a')).toBe(false); + expect(contains(null, 'a')).toBe(false); + expect(contains(undefined, 'a')).toBe(false); + expect(contains(42, 'a')).toBe(false); + expect(contains("string", 'a')).toBe(false); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..dc428475c 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,14 @@ -function createLookup() { - // implementation here + +function createLookup(pairs) { + const lookup = {}; + if (!Array.isArray(pairs)) return lookup; + for (const pair of pairs) { + if (Array.isArray(pair) && pair.length === 2) { + const [country, currency] = pair; + lookup[country] = currency; + } + } + return lookup; } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..86132ad24 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,21 @@ 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'], ['GB', 'GBP']]; + const expected = { US: 'USD', CA: 'CAD', GB: 'GBP' }; + expect(createLookup(pairs)).toEqual(expected); +}); + +test("returns empty object for empty input", () => { + expect(createLookup([])).toEqual({}); +}); + +test("ignores invalid pairs", () => { + const pairs = [['US', 'USD'], ['CA'], 'random', ['GB', 'GBP']]; + const expected = { US: 'USD', GB: 'GBP' }; + expect(createLookup(pairs)).toEqual(expected); +}); /* diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..71267a362 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,8 +6,17 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + const parts = pair.split("="); + const key = parts[0]; + + if (parts.length === 1) { + // No = found, set value to undefined + queryParams[key] = undefined; + } else { + // Join everything after the first = back together + const value = parts.slice(1).join("="); + queryParams[key] = value; + } } return queryParams; diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..487344c5d 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -10,3 +10,42 @@ test("parses querystring values containing =", () => { "equation": "x=y+1", }); }); + +test("parses basic single parameter", () => { + expect(parseQueryString("name=John")).toEqual({ + "name": "John", + }); +}); + +test("parses multiple parameters", () => { + expect(parseQueryString("name=John&age=30&city=London")).toEqual({ + "name": "John", + "age": "30", + "city": "London", + }); +}); + +test("handles empty querystring", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("handles parameters with empty values", () => { + expect(parseQueryString("name=&age=30")).toEqual({ + "name": "", + "age": "30", + }); +}); + +test("handles parameters without values (no = sign)", () => { + expect(parseQueryString("flag&name=John")).toEqual({ + "flag": undefined, + "name": "John", + }); +}); + +test("handles multiple = signs in value", () => { + expect(parseQueryString("formula=a=b=c&x=1")).toEqual({ + "formula": "a=b=c", + "x": "1", + }); +}); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..8690954d7 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,12 @@ -function tally() {} +function tally(items) { + if (!Array.isArray(items)) { + throw new Error("Invalid input"); + } + const counts = {}; + for (const item of items) { + counts[item] = (counts[item] || 0) + 1; + } + return counts; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..bd4d3c172 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -19,16 +19,32 @@ 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 returns correct counts for unique items", () => { + const items = ['apple', 'banana', 'orange']; + const expected = { apple: 1, banana: 1, orange: 1 }; + expect(tally(items)).toEqual(expected); +}); // 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 // Then it should return counts for each unique item - +test("tally returns correct counts for duplicate items", () => { + const items = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']; + const expected = { apple: 3, banana: 2, orange: 1 }; + expect(tally(items)).toEqual(expected); +}); // 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", () => { + expect(() => tally("invalid")).toThrowError("Invalid input"); + expect(() => tally(123)).toThrowError("Invalid input"); + expect(() => tally({})).toThrowError("Invalid input"); + expect(() => tally(null)).toThrowError("Invalid input"); + expect(() => tally(undefined)).toThrowError("Invalid input"); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..4f738b03f 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,38 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; // Previously invertedObj.key = value; BUG HERE! Fixed: use bracket notation and swap key/value } return invertedObj; } // a) What is the current return value when invert is called with { a : 1 } +// Answer: { key: 1 } +// The bug causes it to create a property literally named "key" instead of using the variable value // b) What is the current return value when invert is called with { a: 1, b: 2 } +// Answer: { key: 2 } +// It keeps overwriting the same "key" property with each value from the loop // c) What is the target return value when invert is called with {a : 1, b: 2} +// Answer: { "1": "a", "2": "b" } +// The values should become keys, and the keys should become values // c) What does Object.entries return? Why is it needed in this program? +// Answer: Object.entries(obj) returns an array of [key, value] pairs. +// Example: Object.entries({ a: 1, b: 2 }) returns [["a", 1], ["b", 2]] +// It's needed so we can iterate through both keys AND values of the object at the same time +// Without Object.entries, we can only loop through keys OR values, not both. // d) Explain why the current return value is different from the target output +// Answer: The bug is using dot notation: invertedObj.key = value +// Dot notation creates a property literally named "key" instead of using the variable's value +// It should use bracket notation: invertedObj[value] = key +// Also, it should swap the key and value positions // e) Fix the implementation of invert (and write tests to prove it's fixed!) +// Fixed! The correct line is: invertedObj[value] = key; +// Tests have been written in invert.test.js and all pass ✓ + +module.exports = invert; diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..de75ec281 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,24 @@ +const invert = require('./invert.js'); + +test('inverts object with single key-value pair', () => { + expect(invert({ a: 1 })).toEqual({ "1": "a" }); +}); + +test('inverts object with multiple key-value pairs', () => { + expect(invert({ a: 1, b: 2 })).toEqual({ "1": "a", "2": "b" }); +}); + +test('inverts object with string values', () => { + expect(invert({ x: 10, y: 20 })).toEqual({ "10": "x", "20": "y" }); +}); + +test('inverts object with string keys and values', () => { + expect(invert({ name: "Alice", city: "London" })).toEqual({ + "Alice": "name", + "London": "city" + }); +}); + +test('returns empty object for empty input', () => { + expect(invert({})).toEqual({}); +});