diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..4d3f84e4d 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,5 +1,5 @@ // Predict and explain first... - +// Prediction: 'address' is an object, not an array. Trying to access it with [0] will return undefined. // This code should log out the houseNumber from the address object // but it isn't working... // Fix anything that isn't working @@ -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}`); diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..06231169d 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,5 +1,5 @@ // Predict and explain first... - +// Prediction: 'author' is an object, not an array. The 'for...of' loop doesn't work on plain objects, so it will throw an error. // 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 +11,6 @@ const author = { alive: true, }; -for (const value of author) { - console.log(value); +for (const key in author) { + console.log(author[key]); } diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..8ed488f02 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,5 +1,5 @@ // Predict and explain first... - +// Prediction: The code is trying to print the whole 'recipe' object directly in the template literal, which will just output "[object Object]". We need to access its properties. // 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? @@ -11,5 +11,5 @@ const recipe = { }; console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +ingredients: +${recipe.ingredients.join("\n")}`); diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..f7132712d 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,8 @@ -function contains() {} +function contains(object, propertyName) { + if (typeof object !== "object" || object === null || Array.isArray(object)) { + return false; + } + return object.hasOwnProperty(propertyName); +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..cb578590d 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -20,16 +20,27 @@ as the object doesn't contains a key of 'c' // 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")).toEqual(false); +}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true - +test("contains with an existing property name returns true", () => { + expect(contains({ a: 1, b: 2 }, "a")).toEqual(true); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false - +test("contains with a non-existent property name returns false", () => { + expect(contains({ a: 1, b: 2 }, "c")).toEqual(false); +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test("contains with invalid parameters like an array or null returns false", () => { + expect(contains([1, 2, 3], "0")).toEqual(false); + expect(contains(null, "a")).toEqual(false); + expect(contains("not an object", "length")).toEqual(false); +}); \ No newline at end of file diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..44a428548 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,15 @@ -function createLookup() { - // implementation here +function createLookup(countryCurrencyPairs) { + if (!Array.isArray(countryCurrencyPairs)) { + return {}; + } + + const lookupObject = {}; + + for (const [country, currency] of countryCurrencyPairs) { + lookupObject[country] = currency; + } + + return lookupObject; } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..6148ec4fe 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,22 @@ 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"], + ]; + + const expectedOutput = { + US: "USD", + CA: "CAD", + }; + + expect(createLookup(countryCurrencyPairs)).toEqual(expectedOutput); +}); + +test("returns an empty object if an empty array is passed", () => { + expect(createLookup([])).toEqual({}); +}); /* diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..2678cc37d 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("="); + if (!pair) continue; + const [key, ...rest] = pair.split("="); + const value = rest.join("="); queryParams[key] = value; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..9af1edf23 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,28 @@ // 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", + }); +}); + +test("returns an empty object when query string is empty", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("handles keys without values properly", () => { + expect(parseQueryString("foo&bar=2")).toEqual({ + foo: "", + bar: "2", + }); +}); + +test("handles consecutive ampersands (empty pairs) correctly", () => { + expect(parseQueryString("key1=value1&&key2=value2")).toEqual({ + key1: "value1", + key2: "value2", }); }); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..097b13fe5 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,15 @@ -function tally() {} +function tally(itemsList) { + if (!Array.isArray(itemsList)) { + throw new Error("Input must be an array"); + } + + const counts = Object.create(null); + + for (const item of itemsList) { + 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..9414c4c27 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -23,12 +23,34 @@ 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 // Then it should return counts 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 invalid input like a string // When passed to tally // Then it should throw an error +test("throws an error when input is not an array", () => { + expect(() => { + tally("not an array"); + }).toThrow("Input must be an array"); + + expect(() => { + tally(null); + }).toThrow(); +}); + +test("handles built-in property names like toString correctly", () => { + const result = tally(["toString", "toString"]); + const expected = Object.create(null); + expected.toString = 2; + expect(result).toEqual(expected); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..51c0fb522 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,22 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; } return invertedObj; } - +module.exports = invert; // 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? - +// It returns an array of a given object's own enumerable string-keyed property [key, value] pairs. +// It is needed because plain JavaScript objects are not iterable by default, so we cannot use a 'for...of' loop directly on an object without it. // d) Explain why the current return value is different from the target output - +// Using dot notation (invertedObj.key) creates a literal property named "key" instead of evaluating the variable. +// We must use bracket notation (invertedObj[value]) to dynamically set the property name using the variable's value. // e) Fix the implementation of invert (and write tests to prove it's fixed!) diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..66ae4d36a --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,23 @@ +const invert = require("./invert"); + +describe("invert function", () => { + test("swaps keys and values for an object with numeric values", () => { + expect(invert({ a: 1, b: 2 })).toEqual({ 1: "a", 2: "b" }); + }); + + test("swaps keys and values for a single pair object", () => { + expect(invert({ x: 99 })).toEqual({ 99: "x" }); + }); + + test("returns an empty object when passed an empty object", () => { + expect(invert({})).toEqual({}); + }); + + test("handles string values correctly", () => { + expect(invert({ 1: "one", 2: "two" })).toEqual({ one: "1", two: "2" }); + }); + + test("handles mixed type values correctly", () => { + expect(invert({ a: 1, b: "hello" })).toEqual({ 1: "a", hello: "b" }); + }); +});