diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..36d2f865d 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -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..7ba8f4c75 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -11,6 +11,6 @@ const author = { alive: true, }; -for (const value of author) { +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..d0b246f22 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -12,4 +12,4 @@ const recipe = { console.log(`${recipe.title} serves ${recipe.serves} ingredients: -${recipe}`); +${recipe.ingredients}`); diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..6b3ef26b9 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,7 @@ -function contains() {} +function contains(Obj, key) { + if (Obj === null || typeof Obj !== "object" || Array.isArray(Obj)) + return false; + return Object.prototype.hasOwnProperty.call(Obj, key); +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..0c4b1ced3 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -33,3 +33,19 @@ test.todo("contains on empty object returns false"); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error + +test("contains on empty object returns false", () => { + expect(contains({}, "a")).toEqual(false); +}); + +test("returns true when property exists", () => { + expect(contains({ a: 1, b: 2 }, "a")).toEqual(true); +}); + +test("returns false when property does not exist", () => { + expect(contains({ a: 1, b: 2 }, "c")).toEqual(false); +}); + +test("returns false for invalid input like array", () => { + expect(contains([1, 2, 3], "0")).toEqual(false); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..3281fdf20 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,8 @@ -function createLookup() { - // implementation here +function createLookup(countryCurrencyPairs) { + if (!Array.isArray(countryCurrencyPairs)) { + return {}; + } + return Object.fromEntries(countryCurrencyPairs); } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..d92c73101 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -33,3 +33,40 @@ It should return: 'CA': 'CAD' } */ + +test("returns empty object for empty array", () => { + expect(createLookup([])).toEqual({}); +}); + +test("creates lookup from country-currency pairs", () => { + expect( + createLookup([ + ["US", "USD"], + ["CA", "CAD"], + ]) + ).toEqual({ + US: "USD", + CA: "CAD", + }); +}); + +test("handles single pair", () => { + expect(createLookup([["JP", "JPY"]])).toEqual({ + JP: "JPY", + }); +}); + +test("overwrites duplicate country codes (last one wins)", () => { + expect( + createLookup([ + ["US", "USD"], + ["US", "USN"], + ]) + ).toEqual({ + US: "USN", + }); +}); + +test("handles invalid input (non-array)", () => { + expect(createLookup(null)).toEqual({}); +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..0872f83fa 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,12 +1,21 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { - return queryParams; + + if (!queryString) return queryParams; + + if (queryString.startsWith("?")) { + queryString = queryString.slice(1); } + const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + if (!pair) continue; + + const [rawKey, ...rawValue] = pair.split("="); + const key = decodeURIComponent(rawKey); + const value = decodeURIComponent(rawValue.join("=")); + queryParams[key] = value; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..e6b0e31df 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,69 @@ // 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("parses simple key-value pairs", () => { + expect(parseQueryString("name=alice&age=25")).toEqual({ + name: "alice", + age: "25", + }); +}); +test("strips leading question marks", () => { + expect(parseQueryString("?user=bob&status=active")).toEqual({ + user: "bob", + status: "active", + }); +}); +test("returns empty object for empty string", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("returns empty object for null or undefined", () => { + expect(parseQueryString(null)).toEqual({}); + expect(parseQueryString(undefined)).toEqual({}); +}); +test("decodes URL encoded keys and values", () => { + expect(parseQueryString("search=coding%20tutorials&city=New%20York")).toEqual( + { + search: "coding tutorials", + city: "New York", + } + ); +}); + +test("decodes complex characters and emojis", () => { + expect(parseQueryString("item=%F0%9F%8D%95&price=%2410")).toEqual({ + item: "🍕", + price: "$10", + }); +}); +test("ignores empty segments from double ampersands", () => { + expect(parseQueryString("a=1&&b=2")).toEqual({ + a: "1", + b: "2", + }); +}); + +test("ignores trailing ampersands", () => { + expect(parseQueryString("a=1&b=2&")).toEqual({ + a: "1", + b: "2", + }); +}); +test("handles keys with empty values", () => { + expect(parseQueryString("key=")).toEqual({ + key: "", + }); +}); + +test("handles keys without an equals sign", () => { + expect(parseQueryString("flag")).toEqual({ + flag: "", }); }); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..94a6da1cb 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,11 @@ -function tally() {} +function tally(array) { + const tally = {}; + if (!Array.isArray(array)) throw new TypeError("Expected an array"); + if (array.length === 0) return tally; + for (const item of array) { + tally[item] = (tally[item] || 0) + 1; + } + return tally; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..6b0888faf 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -24,6 +24,9 @@ const tally = require("./tally.js"); // When passed to tally // Then it should return an empty object test.todo("tally on an empty array returns an empty object"); +test("Empty array of items", () => { + expect(tally([])).toEqual({}); +}); // Given an array with duplicate items // When passed to tally @@ -32,3 +35,23 @@ test.todo("tally on an empty array returns an empty object"); // Given an invalid input like a string // When passed to tally // Then it should throw an error + +test("Invalid input", () => { + expect(() => tally("not an array")).toThrow(TypeError); +}); +test("Test duplicate array", () => { + const duplicatesArray = [ + "apple", + "banana", + "apple", + "orange", + "banana", + "apple", + ]; + const result = tally(duplicatesArray); + expect(result).toEqual({ + apple: 3, + banana: 2, + orange: 1, + }); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..f35a148fa 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -5,7 +5,7 @@ // Then it should swap the keys and values in the object // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} - +/* function invert(obj) { const invertedObj = {}; @@ -17,13 +17,33 @@ function invert(obj) { } // 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} +//The target return value is { "1": "a", "2": "b" }. // c) What does Object.entries return? Why is it needed in this program? +//It returns An array of the object's own property [key, value] pairs. +// It allows the for...of loop to easily access both the key and the value at the same time during each iteration. // d) Explain why the current return value is different from the target output +//Using invertedObj.key creates a literal property named "key" on the object. +// To use the value inside the key variable, you must use bracket notation: invertedObj[key]. +//The function is setting the original value to the key. +// To actually "invert" the object, it needs to swap them: invertedObj[value] = key;. // e) Fix the implementation of invert (and write tests to prove it's fixed!) +*/ +function invert(obj) { + const invertedObj = {}; + + for (const [key, value] of Object.entries(obj)) { + invertedObj[value] = key; + } + + return invertedObj; +} +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..5f879e88e --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,24 @@ +const invert = require("./invert.js"); +test("inverts a single key-value pair", () => { + const input = { a: 1 }; + expect(invert(input)).toEqual({ 1: "a" }); +}); +test("inverts multiple key-value pairs", () => { + const input = { a: 1, b: 2, c: 3 }; + const expected = { 1: "a", 2: "b", 3: "c" }; + + expect(invert(input)).toEqual(expected); +}); +test("returns empty object for empty input", () => { + expect(invert({})).toEqual({}); +}); +test("overwrites keys when duplicate values exist", () => { + const input = { a: 1, b: 1 }; + expect(invert(input)).toEqual({ 1: "b" }); +}); +test("turns boolean and null values into string keys", () => { + const input = { a: true, b: null }; + const expected = { true: "a", null: "b" }; + + expect(invert(input)).toEqual(expected); +});