diff --git a/Sprint-1/fix/median.js b/Sprint-1/fix/median.js index f74d1dc0a..d359ecc2c 100644 --- a/Sprint-1/fix/median.js +++ b/Sprint-1/fix/median.js @@ -9,3 +9,5 @@ function calculateMedian(list) { } module.exports = calculateMedian; + + diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..660515776 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -12,4 +12,6 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +// Accessing the houseNumber property from the address object +console.log(`My house number is ${address.houseNumber}`); + diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..b8ccc96fb 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -3,6 +3,13 @@ // 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 +//author contains properties (key-value pairs), but a simple for...of loop treats it as if it's an array of values, which it isn't. + + + +// The for...in loop treats `author` as an object and iterates over its keys. +// We then use those keys to log out their corresponding values. + const author = { firstName: "Zadie", lastName: "Smith", @@ -11,6 +18,19 @@ const author = { alive: true, }; -for (const value of author) { - console.log(value); + +// Iterate over the object's keys and log their values +for (const value in author) { + // Logs the value associated with each key + console.log(author[value]); } + + + + +// ================= access to its keys ============== + +for (const key in author){ +console.log(key); +// console.log(key, author[key]); +} \ No newline at end of file diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..5f3defdce 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -4,12 +4,23 @@ // Each ingredient should be logged on a new line // How can you fix it? -const recipe = { - title: "bruschetta", - serves: 2, - ingredients: ["olive oil", "tomatoes", "salt", "pepper"], -}; - -console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +// const recipe = { +// title: "bruschetta", +// serves: 2, +// ingredients: ["olive oil", "tomatoes", "salt", "pepper"], +// }; + +// console.log(`${recipe.title} serves ${recipe.serves}`); + +// let ingredients = recipe["ingredients"]; + +// for (let i = 0; i < ingredients.length; i++) { +// console.log(ingredients[i]); +// } + +// ==================== one line with join method ==================== + +console.log(`${recipe.title} serves ${recipe.serves}`); + +// Display the ingredients in one line with a new line (\n) between each ingredient +console.log(recipe.ingredients.join("\n")); diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..780a339c8 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,14 @@ -function contains() {} +function contains(obj, property) { + // check if it is an Array + if (typeof obj !== "object" || obj === null || Array.isArray(obj) || obj === undefined ) { + return false; + } + for (let key in obj) { + if (key === property) { + 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..21dbf8f34 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -20,7 +20,6 @@ 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"); // Given an object with properties // When passed to contains with an existing property name @@ -33,3 +32,39 @@ 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 + + + +describe("contains function", () => { + // When the object is empty, it should return false + test("should return false for an empty object", () => { + expect(contains({}, "a")).toBe(false); + }); + + // When the key exists in the object, it should return true + test("should return true if the object contains the property", () => { + expect(contains({ a: 1, b: 2 }, "a")).toBe(true); + }); + + // When the key does not exist in the object, it should return false + test("should return false if the object does not contain the property", () => { + expect(contains({ a: 1, b: 2 }, "c")).toBe(false); + }); + + // When an array is passed as a parameter, it should return false + test("should return false for invalid parameters like arrays", () => { + expect(contains([1, 2, 3], "a")).toBe(false); + }); + + // It should handle null and undefined inputs gracefully (throw an error) + test("should handle null or undefined inputs gracefully", () => { + expect(contains(null, "a")).toBe(false); + expect(contains(undefined, "a")).toBe(false); + }); +}); + + +test.todo("contains on empty object returns false"); + + + diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..ef8dc002a 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,24 @@ -function createLookup() { - // implementation here +function createLookup(input) { + const objInput = {}; + + // Validate input is an array of arrays + if (!Array.isArray(input)) { + throw new Error("Input must be an array of arrays."); + } + + // Populate the object + for (const pair of input) { + if (!Array.isArray(pair) || pair.length !== 2) { + throw new Error( + "Each element of the input array must be a key-value pair." + ); + } + + const [key, value] = pair; + objInput[key] = value; + } + + return objInput; } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..472cedc71 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,6 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); +// test.todo("creates a country currency code lookup for multiple codes"); /* @@ -33,3 +33,22 @@ It should return: 'CA': 'CAD' } */ + + +// The tests below already cover multiple codes functionality. +test("Given empty array", () => { + expect(createLookup([])).toEqual({}); +}); + +test("Given single array", () => { + expect(createLookup([["US", "USD"]])).toEqual({ US: "USD" }); +}); + +test("Given multiple array", () => { + expect( + createLookup([ + ["US", "USD"], + ["CA", "CAD"], + ]) + ).toEqual({ US: "USD", CA: "CAD" }); +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..5ac11b31f 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -4,9 +4,12 @@ function parseQueryString(queryString) { return queryParams; } const keyValuePairs = queryString.split("&"); - for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + const [key, ...valueParts] = pair.split("="); + const value = + valueParts.length > 0 + ? decodeURIComponent(valueParts.join("=")) + : undefined; queryParams[key] = value; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..80e9958af 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,82 @@ // 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"); test("parses querystring values containing =", () => { expect(parseQueryString("equation=x=y+1")).toEqual({ - "equation": "x=y+1", + equation: "x=y+1", + }); +}); + +describe("parseQueryString", () => { + test("parses a simple query string", () => { + expect(parseQueryString("name=Sajad")).toEqual({ + name: "Sajad", + }); + }); + + test("handles multiple key-value pairs", () => { + expect(parseQueryString("name=Sajad&age=25")).toEqual({ + name: "Sajad", + age: "25", + }); + }); + + test("handles empty query string", () => { + expect(parseQueryString("")).toEqual({}); + }); + + test("handles a key without a value", () => { + expect(parseQueryString("name=")).toEqual({ + name: "", + }); + }); + + test("parses query string with duplicate keys, keeps the last value", () => { + expect(parseQueryString("name=Sajad&name=Ali")).toEqual({ + name: "Ali", + }); + }); + + test("handles keys with special characters", () => { + expect(parseQueryString("key@name=value")).toEqual({ + "key@name": "value", + }); + }); + + test("handles values with special characters", () => { + expect(parseQueryString("name=Sonia@25")).toEqual({ + name: "Sonia@25", + }); + }); + + test("handles query string with only a key", () => { + expect(parseQueryString("name")).toEqual({ + name: undefined, + }); + }); + + test("handles query string with no '=' or '&'", () => { + expect(parseQueryString("name&age")).toEqual({ + name: undefined, + age: undefined, + }); + }); + + test("handles query string with mixed valid and invalid parts", () => { + expect(parseQueryString("name=Sonia&age&=25")).toEqual({ + name: "Sonia", + age: undefined, + "": "25", + }); + }); + + test("handles special characters correctly", () => { + const specialQueryString = "name=%20G%C3%BCnter"; + expect(parseQueryString(specialQueryString)).toEqual({ + name: " Günter", + }); }); }); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..cff473803 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,23 @@ -function tally() {} +function tally(array) { + // check if input is an array + if (!Array.isArray(array)) { + return { error: "Input must be an array" }; + } + + // create an empty object to store counts + let counts = {}; + + // Loop through the array and count items + for (let item of array) { + if (counts[item]) { + //the loop checks if the current item already exists as a key in the counts object. + counts[item] += 1; // Increase count if item exists + } else { + counts[item] = 1; //Initialize count if item is new + } + } + + return counts; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..02412562e 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -23,12 +23,22 @@ 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.todo("tally on an empty array returns an empty object");// Then it should return an empty object +test("Given an empty array", () =>{ + expect(tally([])).toEqual({}); +}); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item - +test("Given an array with duplicate items", () =>{ + expect(tally(['a'])).toEqual({a: 1}); + expect(tally(['a', 'a', 'a'])).toEqual({a: 3 }); + expect(tally(['a', 'a', 'b', 'c'])).toEqual({a : 2, b: 1, c: 1}); +}); // Given an invalid input like a string // When passed to tally // Then it should throw an error +test("Given an invalid input like a string", () =>{ + expect(tally(1)).toEqual({ error: "Input must be an array"}); +}); \ No newline at end of file diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..16f370eeb 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -6,24 +6,54 @@ // 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" } + + +// d) What does Object.entries return? Why is it needed in this program? + + + // Object.entries() returns an array of [key, value] pairs from an object, e.g., { a: 1, b: 2 } becomes [["a", 1], ["b", 2]]. + // It’s needed to easily loop through the key-value pairs and swap them. + + +// e) Explain why the current return value is different from the target output + + // The code is incorrectly assigning value to the property key, so it only stores the last key-value pair. + +// f) 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.key = value; + invertedObj[value] = key; // Correct swap } - return invertedObj; } -// a) What is the current return value when invert is called with { a : 1 } -// b) What is the current return value when invert is called with { a: 1, b: 2 } - -// c) What is the target return value when invert is called with {a : 1, b: 2} +// console.log(invert({ a: 1 })); // { "1": "a" } +// console.log(invert({ a: 1, b: 2 })); // { "1": "a", "2": "b" } -// c) What does Object.entries return? Why is it needed in this program? -// d) Explain why the current return value is different from the target output +module.exports = invert; -// 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..0f20943e7 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,33 @@ +const invert = require("./invert"); + +describe("invert function", () => { + test("should invert an object with one key-value pair", () => { + const input = { a: 1 }; + const output = { 1: "a" }; + expect(invert(input)).toEqual(output); + }); + + test("should invert an object with multiple key-value pairs", () => { + const input = { a: 1, b: 2 }; + const output = { 1: "a", 2: "b" }; + expect(invert(input)).toEqual(output); + }); + + test("should handle an empty object", () => { + const input = {}; + const output = {}; + expect(invert(input)).toEqual(output); + }); + + test("should handle a single value being swapped", () => { + const input = { name: "John" }; + const output = { John: "name" }; + expect(invert(input)).toEqual(output); + }); + + test("should handle objects with numeric values", () => { + const input = { x: 10, y: 20 }; + const output = { 10: "x", 20: "y" }; + expect(invert(input)).toEqual(output); + }); +});