From 8ae3d62f5e16f40dd60757ae00e7bfb6227844ca Mon Sep 17 00:00:00 2001 From: MehrozMunir Date: Wed, 18 Mar 2026 11:38:52 +0000 Subject: [PATCH 1/3] changes madee in the debug and implement files --- Sprint-2/debug/address.js | 6 +- Sprint-2/debug/author.js | 9 ++- Sprint-2/debug/recipe.js | 10 +++- Sprint-2/implement/contains.js | 7 ++- Sprint-2/implement/contains.test.js | 91 +++++++++++++++++++++++------ 5 files changed, 96 insertions(+), 27 deletions(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..00c8a4cb1 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -4,6 +4,10 @@ // but it isn't working... // Fix anything that isn't working +/*Ans: My prediction is that because address is an object and not an array, +therefore, its elements should be accessed by providing the value of key +rather than writing index such as we do in array 0,1,2...*/ + const address = { houseNumber: 42, street: "Imaginary Road", @@ -12,4 +16,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..e03109102 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -3,6 +3,11 @@ // 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 +/*My prediction is that in the below code we are trying to run a for..of loop +on an object like we do for an array, i am not sure how can we make this work for +object, but this one seems like really not going to work because value is not a +value but a key and value */ + const author = { firstName: "Zadie", lastName: "Smith", @@ -11,6 +16,4 @@ const author = { alive: true, }; -for (const value of author) { - console.log(value); -} +console.log(Object.values(author)); diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..2323a3d4a 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -4,12 +4,16 @@ // Each ingredient should be logged on a new line // How can you fix it? +//Ans: The code will log title and serves but all the ingredients will be printed in the same line +// In order to log each ingredient in a new line we need to access each ingredient +// and add a new line character after that, right now we are just logging recipe object there + 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} +ingredients: +${recipe.ingredients[0]}\n${recipe.ingredients[1]}\n${recipe.ingredients[2]}\n${recipe.ingredients[3]}`); diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..559251561 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,6 @@ -function contains() {} - +function contains(object, property) { + if (object === null || typeof object !== "object" || Array.isArray(object)) + throw new Error("It is not a valid object"); + else return Object.hasOwn(object, property); +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..89d4778b8 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -7,29 +7,84 @@ 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 +describe("containsProperty", () => { + // 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 + [ + { input: { a: 1, b: 2 }, property: "a", expected: true }, + { input: { a: 1, b: 2 }, property: "c", expected: false }, + { input: { a: 1, b: 2, sdk: 28299 }, property: "what", expected: false }, + ].forEach(({ input, property, expected }) => + it(`Should return true if the object ${input} contains the property ${property} otherwise false`, () => + expect(contains(input, property)).toEqual(expected)) + ); -// Given an empty object -// When passed to contains -// Then it should return false -test.todo("contains on empty object returns false"); + // Given an empty object + // When passed to contains + // Then it should return false + [ + { input: {}, property: "a", expected: false }, + { input: {}, property: "", expected: false }, + { input: {}, property: null, expected: false }, + ].forEach(({ input, property, expected }) => + it(`Should return false if the object ${input} is empty`, () => + expect(contains(input, property)).toEqual(expected)) + ); -// 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 an existing property name + // Then it should return true + [ + { input: { a: 1, dog: 2 }, property: "dog", expected: true }, + { + input: { a: 1, b: 2, property3: "here you go" }, + property: "property3", + expected: true, + }, + { input: { a: 1, b: 2, sdk: 28299 }, property: "sdk", expected: true }, + ].forEach(({ input, property, expected }) => + it(`Should return true if the object ${input} contains the property ${property}`, () => + expect(contains(input, property)).toEqual(expected)) + ); -// Given an object with properties -// When passed to contains with a non-existent property name -// Then it should return false + // Given an object with properties + // When passed to contains with a non-existent property name + // Then it should return false + [ + { input: { a: 1, dog: 2 }, property: "cat", expected: false }, + { + input: { a: 1, b: 2, property3: "here you go" }, + property: "", + expected: false, + }, + { + input: { a: 1, b: 2, thatProperty: 28299 }, + property: "", + expected: false, + }, + ].forEach(({ input, property, expected }) => + it(`Should return false if the object ${input} doesn't contain the property ${property}`, () => + expect(contains(input, property)).toEqual(expected)) + ); -// Given invalid parameters like an array -// When passed to contains -// Then it should return false or throw an error + // Given invalid parameters like an array + // When passed to contains + // Then it should return false or throw an error + [ + { input: [1], property: "a", expected: true }, + { input: null, property: "c", expected: false }, + { input: undefined, property: "what", expected: false }, + { input: [1, 2, 3], property: "what", expected: false }, + ].forEach(({ input, property, expected }) => + it(`Should return false as ${input} is not an object`, () => + expect(() => contains(input, property)).toThrow( + "It is not a valid object" + )) + ); +}); From e84acb9787ac69f683089910ede30ef28d973b5c Mon Sep 17 00:00:00 2001 From: MehrozMunir Date: Thu, 19 Mar 2026 15:00:16 +0000 Subject: [PATCH 2/3] worked on implement files --- Sprint-2/implement/contains.js | 2 +- Sprint-2/implement/contains.test.js | 12 +++--- Sprint-2/implement/lookup.js | 6 ++- Sprint-2/implement/lookup.test.js | 40 ++++++++++++++++++- Sprint-2/implement/querystring.js | 3 +- Sprint-2/implement/querystring.test.js | 54 +++++++++++++++++++++++++- Sprint-2/implement/tally.test.js | 1 - 7 files changed, 103 insertions(+), 15 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index 559251561..e8ecc8b49 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,6 +1,6 @@ function contains(object, property) { if (object === null || typeof object !== "object" || Array.isArray(object)) - throw new Error("It is not a valid object"); + throw new Error("Input is not a valid object"); else return Object.hasOwn(object, property); } module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 89d4778b8..819eabe6d 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -77,14 +77,14 @@ describe("containsProperty", () => { // When passed to contains // Then it should return false or throw an error [ - { input: [1], property: "a", expected: true }, - { input: null, property: "c", expected: false }, - { input: undefined, property: "what", expected: false }, - { input: [1, 2, 3], property: "what", expected: false }, - ].forEach(({ input, property, expected }) => + { input: [1], property: "a" }, + { input: null, property: "c" }, + { input: undefined, property: "what" }, + { input: [1, 2, 3], property: "what" }, + ].forEach(({ input, property }) => it(`Should return false as ${input} is not an object`, () => expect(() => contains(input, property)).toThrow( - "It is not a valid object" + "Input is not a valid object" )) ); }); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..add412243 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,7 @@ -function createLookup() { +function createLookup(array) { // implementation here + if (!Array.isArray(array) || !array.every((item) => Array.isArray(item))) + throw new Error("Invalid Input"); + return Object.fromEntries(array); } - module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..4cc6b2508 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,7 +1,5 @@ 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 @@ -33,3 +31,41 @@ It should return: 'CA': 'CAD' } */ + +describe("lookup", () => { + const countryCurrencyArray = [ + ["US", "USD"], + ["CA", "CAD"], + ["PAK", "RS"], + ]; + const countryCurrencyObject = { + US: "USD", + CA: "CAD", + PAK: "RS", + }; + + // CASE 1: Should return an object with key as country codes and value + // as currency codes when an array is passed containing arrays of countries + // codes and their currencies codes + test(`should return an object of the array [${countryCurrencyArray}]`, () => + expect(createLookup(countryCurrencyArray)).toEqual(countryCurrencyObject)); + + // CASE 2: Should return an empty object if empty array is passed + test(`should return an empty object if the array is empty`, () => + expect(createLookup([[]])).toEqual({})); + + // CASE 3: Input is null + test(`should throw an error if null is passed`, () => + expect(() => createLookup(null)).toThrow("Invalid Input")); + // CASE 4: Input is undefined + test(`should throw an error if undefined is passed`, () => + expect(() => createLookup(undefined)).toThrow("Invalid Input")); + + // CASE 5: Input is empty object {} + test(`should throw an error if undefined is passed`, () => + expect(() => createLookup({})).toThrow("Invalid Input")); + + // CASE 5: Input is single value {} + test(`should throw an error if single value array is passed`, () => + expect(() => createLookup([123])).toThrow("Invalid Input")); +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..f4774db55 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,4 +1,5 @@ function parseQueryString(queryString) { + if (typeof queryString !== "string") throw new Error("Invalid Input"); const queryParams = {}; if (queryString.length === 0) { return queryParams; @@ -6,7 +7,7 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + let [key, value] = pair.split(/=(.*)/); queryParams[key] = value; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..3dc2610d0 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,60 @@ // 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"); +//Case 1 : equal sign within the value of query string test("parses querystring values containing =", () => { expect(parseQueryString("equation=x=y+1")).toEqual({ - "equation": "x=y+1", + equation: "x=y+1", }); }); + +//Case 2 : multiple params in the query string +test("parses querystring containing multiple params", () => { + expect(parseQueryString("equation=x=y+1&job=student")).toEqual({ + equation: "x=y+1", + job: "student", + }); +}); + +//Case 3 : empty or missing value in the param +test("parses querystring containing empty or missing value in one of the params", () => { + expect(parseQueryString("equation=x=y+1&job=")).toEqual({ + equation: "x=y+1", + job: "", + }); +}); + +//Case 4 : extra & in the start, mid or at the end +test("parses querystring containing empty or missing value in one of the params", () => { + expect(parseQueryString("&equation=x=y+1&&job=&")).toEqual({ + equation: "x=y+1", + job: "", + }); +}); + +//Case 4 : querystring is empty +test("an empty query string should return an empty object", () => { + expect(parseQueryString("")).toEqual({}); +}); + +//Case 5 : querystring is null +test("if the querystring is null then an error should be thrown", () => { + expect(() => parseQueryString(null)).toThrow("Invalid Input"); +}); + +//Case 6 : querystring is undefined +test("if the querystring is undefined then an error should be thrown", () => { + expect(() => parseQueryString(undefined)).toThrow("Invalid Input"); +}); + +//Case 7 : querystring is a number +test("if the querystring is undefined then an error should be thrown", () => { + expect(() => parseQueryString(123)).toThrow("Invalid Input"); +}); + +//Case 8 : querystring is an object +test("if the querystring is an object then an error should be thrown", () => { + expect(() => parseQueryString({})).toThrow("Invalid Input"); +}); diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..e3c71473a 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -23,7 +23,6 @@ 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"); // Given an array with duplicate items // When passed to tally From baf14c8dee3007bc1c1692a348ecb8d6c1217108 Mon Sep 17 00:00:00 2001 From: MehrozMunir Date: Fri, 20 Mar 2026 00:24:03 +0000 Subject: [PATCH 3/3] completed sprint 2 --- Sprint-2/implement/querystring.js | 2 +- Sprint-2/implement/tally.js | 11 +++++++++-- Sprint-2/implement/tally.test.js | 33 +++++++++++++++++++++++++++++++ Sprint-2/interpret/invert.js | 14 ++++++++++++- Sprint-2/interpret/invert.test.js | 29 +++++++++++++++++++++++++++ Sprint-2/stretch/count-words.js | 25 +++++++++++++++++++++++ Sprint-2/stretch/mode.js | 32 +++++++++++------------------- Sprint-2/stretch/till.js | 7 +++++-- 8 files changed, 127 insertions(+), 26 deletions(-) create mode 100644 Sprint-2/interpret/invert.test.js diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index f4774db55..f7fd53d33 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -7,7 +7,7 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - let [key, value] = pair.split(/=(.*)/); + const [key, value] = pair.split(/=(.*)/); queryParams[key] = value; } diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..f2bd7517c 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,10 @@ -function tally() {} - +function tally(array) { + if (!Array.isArray(array)) throw new Error("Not an array"); + const tallyObject = {}; + for (const item of array) { + if (Object.hasOwn(tallyObject, item)) tallyObject[item] += 1; + else tallyObject[item] = 1; + } + return tallyObject; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index e3c71473a..b0d815028 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -19,15 +19,48 @@ 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("returns an object containing the count for each unique item in the array", () => { + [ + { input: ["a"], expected: { a: 1 } }, + { input: ["a", "b", "c"], expected: { a: 1, b: 1, c: 1 } }, + { input: ["a", 1, 3, 5], expected: { a: 1, 1: 1, 3: 1, 5: 1 } }, + { + input: ["", " ", "what", 10], + expected: { "": 1, " ": 1, what: 1, 10: 1 }, + }, + ].forEach(({ input, expected }) => expect(tally(input)).toEqual(expected)); +}); // Given an empty array // When passed to tally // Then it should return an empty object +test("returns an empty object when an empty array is passed", () => { + expect(tally([])).toEqual({}); +}); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item +test("returns an object containing the count for each unique item in the array", () => { + [ + { input: ["a"], expected: { a: 1 } }, + { input: ["a", "a", "a"], expected: { a: 3 } }, + { input: ["a", "a", "b", "c"], expected: { a: 2, b: 1, c: 1 } }, + { + input: [1, 1, "q", 1, "q", 5, "javascript", "what", "javascript"], + expected: { 1: 3, q: 2, 5: 1, javascript: 2, what: 1 }, + }, + { + input: [0, "", 0, 5, "", "", "not_empty", " ", " "], + expected: { 0: 2, "": 3, 5: 1, not_empty: 1, " ": 2 }, + }, + ].forEach(({ input, expected }) => expect(tally(input)).toEqual(expected)); +}); // Given an invalid input like a string // When passed to tally // Then it should throw an error +["just a string", 123, null, undefined, {}].forEach((input) => + test(`Should throw an error as ${input} is not a valid input`, () => + expect(() => tally(input)).toThrow("Not an array")) +); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..19153594d 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -7,23 +7,35 @@ // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} function invert(obj) { + if (obj === null || typeof obj !== "object" || Array.isArray(obj)) + throw new Error("Input is not a valid object"); const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; } return invertedObj; } +console.log(invert({ a: 1, b: 2 })); // a) What is the current return value when invert is called with { a : 1 } +//Ans: { key : 1} // b) What is the current return value when invert is called with { a: 1, b: 2 } +//Ans: { key : 2} // c) What is the target return value when invert is called with {a : 1, b: 2} +// Ans: { '1': 'a', '2': 'b'} // c) What does Object.entries return? Why is it needed in this program? +//Ans: it returns a key/values pair that we need when swapping them for inverting an object // d) Explain why the current return value is different from the target output +//Ans: it is different because when accessing the element using '.' operator after object instance, +// we are calling the property name of object and not using the key name from the array in the loop // e) Fix the implementation of invert (and write tests to prove it's fixed!) +//fixed the implementation and have created a separate file in this directory for the tests + +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..5dbf2117f --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,29 @@ +const invert = require("./invert.js"); + +// Given an object +// When invert is passed this object +// Then it should swap the keys and values in the object +test("returns an inverted object", () => { + [ + { input: { 1: "a" }, expected: { a: "1" } }, + { + input: { name: "mehroz", job: "student" }, + expected: { mehroz: "name", student: "job" }, + }, + ].forEach(({ input, expected }) => expect(invert(input)).toEqual(expected)); +}); + +// Given an empty object +// When passed to invert +// Then it should return an empty object +test("returns an empty object when an empty object is passed", () => { + expect(invert({})).toEqual({}); +}); + +// Given an invalid input like a string, array, null +// When passed to invert +// Then it should throw an error +["just a string", 123, null, undefined, [1, 2, 3], []].forEach((input) => + test(`Should throw an error as ${input} is not a valid object`, () => + expect(() => invert(input)).toThrow("Input is not a valid object")) +); diff --git a/Sprint-2/stretch/count-words.js b/Sprint-2/stretch/count-words.js index 8e85d19d7..a1ed6a30f 100644 --- a/Sprint-2/stretch/count-words.js +++ b/Sprint-2/stretch/count-words.js @@ -26,3 +26,28 @@ 3. Order the results to find out which word is the most common in the input */ + +function countWords(string) { + if (typeof string !== "string") { + console.log(string + " is not a valid string"); + return; + } + + const wordsCountObject = {}; + + // Replace anything that is NOT a word character or a space with an empty string + const cleanText = string.replace(/[^\w\s]|_/g, "").toLowerCase(); + + cleanText.split(" ").forEach((word) => { + if (Object.hasOwn(wordsCountObject, word)) wordsCountObject[word] += 1; + else wordsCountObject[word] = 1; + }); + + const sortedArray = Object.entries(wordsCountObject).sort( + (a, b) => b[1] - a[1] + ); + + return Object.fromEntries(sortedArray); +} + +console.log(countWords("you and me and you and me me me me")); diff --git a/Sprint-2/stretch/mode.js b/Sprint-2/stretch/mode.js index 3f7609d79..4d695733b 100644 --- a/Sprint-2/stretch/mode.js +++ b/Sprint-2/stretch/mode.js @@ -9,28 +9,20 @@ // into smaller functions using the stages above function calculateMode(list) { - // track frequency of each value - let freqs = new Map(); - - for (let num of list) { - if (typeof num !== "number") { - continue; - } - - freqs.set(num, (freqs.get(num) || 0) + 1); - } - - // Find the value with the highest frequency - let maxFreq = 0; - let mode; - for (let [num, freq] of freqs) { - if (freq > maxFreq) { - mode = num; - maxFreq = freq; - } + if (!Array.isArray(list)) throw new Error("Not an array"); + //tracking the frequency of each number using object + frequencyObject = {}; + for (const number of list) { + if (typeof number !== "number") continue; + if (Object.hasOwn(frequencyObject, number)) frequencyObject[number] += 1; + else frequencyObject[number] = 1; } - return maxFreq === 0 ? NaN : mode; + //sorting the number with the highest frequency at the lowest index + const sortedArray = Object.entries(frequencyObject).sort( + (a, b) => b[1] - a[1] + ); + return Number(sortedArray[0][0]); } module.exports = calculateMode; diff --git a/Sprint-2/stretch/till.js b/Sprint-2/stretch/till.js index 6a08532e7..acc7231be 100644 --- a/Sprint-2/stretch/till.js +++ b/Sprint-2/stretch/till.js @@ -8,7 +8,7 @@ function totalTill(till) { let total = 0; for (const [coin, quantity] of Object.entries(till)) { - total += coin * quantity; + total += Number(coin.slice(0, coin.length - 1)) * quantity; } return `£${total / 100}`; @@ -23,9 +23,12 @@ const till = { const totalAmount = totalTill(till); // a) What is the target output when totalTill is called with the till object +//Ans: £4.4 // b) Why do we need to use Object.entries inside the for...of loop in this function? - +//Ans:To get an array of key/ values pairs // c) What does coin * quantity evaluate to inside the for...of loop? +//Ans: coin * quantity gives us the total amount in pence of a coin in one key/value pair // d) Write a test for this function to check it works and then fix the implementation of totalTill +console.log(totalAmount);