From d1e2c7442439f64e8dcddf9aacb1489e6ba229c3 Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Fri, 7 Nov 2025 16:56:23 +0000 Subject: [PATCH 01/13] Sprint 1: Fix median function and complete implement/refactor exercises --- Sprint-1/fix/median.js | 39 +++++++++++++++++++++++++++++++++++--- Sprint-1/package-lock.json | 2 ++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Sprint-1/fix/median.js b/Sprint-1/fix/median.js index b22590bc6..6b4ff09b9 100644 --- a/Sprint-1/fix/median.js +++ b/Sprint-1/fix/median.js @@ -6,9 +6,42 @@ // or 'list' has mixed values (the function is expected to sort only numbers). function calculateMedian(list) { - const middleIndex = Math.floor(list.length / 2); - const median = list.splice(middleIndex, 1)[0]; - return median; + if (!Array.isArray(list)) { + return null; + } + + const numbers = []; + for (let i = 0; i < list.length; i++) { + if (typeof list[i] === 'number') { + numbers.push(list[i]); + } + } + + if (numbers.length === 0) { + return null; + } + + for (let i = 0; i < numbers.length; i++) { + for (let j = i + 1; j < numbers.length; j++) { + if (numbers[i] > numbers[j]) { + const temp = numbers[i]; + numbers[i] = numbers[j]; + numbers[j] = temp; + } + } + } + + const middleIndex = Math.floor(numbers.length / 2); + + + if (numbers.length % 2 === 0) { + + return (numbers[middleIndex - 1] + numbers[middleIndex]) / 2; + } else { + + return numbers[middleIndex]; + } } + module.exports = calculateMedian; diff --git a/Sprint-1/package-lock.json b/Sprint-1/package-lock.json index 83e427d0b..b52480af5 100644 --- a/Sprint-1/package-lock.json +++ b/Sprint-1/package-lock.json @@ -56,6 +56,7 @@ "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.25.7", @@ -1368,6 +1369,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001663", "electron-to-chromium": "^1.5.28", From 9651da664f2e8f6fbc368b85ac474dba8ea9d40c Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Wed, 12 Nov 2025 21:01:05 +0000 Subject: [PATCH 02/13] Implement findMax function to return the largest number in an array --- Sprint-1/implement/max.js | 17 +++++++++++++++++ Sprint-1/package-lock.json | 2 -- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Sprint-1/implement/max.js b/Sprint-1/implement/max.js index 6dd76378e..15adb4c1b 100644 --- a/Sprint-1/implement/max.js +++ b/Sprint-1/implement/max.js @@ -1,4 +1,21 @@ function findMax(elements) { + if (elements.length === 0) { + return -Infinity; + } + + let max = -Infinity; + + for (let i = 0; i < elements.length; i++) { + let current = elements[i]; + + if (typeof current === "number") { + if (current > max) { + max = current; + } + } + } + + return max; } module.exports = findMax; diff --git a/Sprint-1/package-lock.json b/Sprint-1/package-lock.json index b52480af5..83e427d0b 100644 --- a/Sprint-1/package-lock.json +++ b/Sprint-1/package-lock.json @@ -56,7 +56,6 @@ "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.25.7", @@ -1369,7 +1368,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001663", "electron-to-chromium": "^1.5.28", From 22655f0a2329f0645a1a553a6439361017eadcd5 Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Wed, 12 Nov 2025 21:04:55 +0000 Subject: [PATCH 03/13] Implement sum function to add numbers in an array --- Sprint-1/implement/sum.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Sprint-1/implement/sum.js b/Sprint-1/implement/sum.js index 9062aafe3..06164b255 100644 --- a/Sprint-1/implement/sum.js +++ b/Sprint-1/implement/sum.js @@ -1,4 +1,19 @@ function sum(elements) { + // This variable will store the total sum + let total = 0; + + // Go through each element in the array + for (let i = 0; i < elements.length; i++) { + let current = elements[i]; // get the current element + + // Only add it if it's a number + if (typeof current === "number") { + total = total + current; // add to total + } + } + + // Return the sum of all numbers + return total; } module.exports = sum; From 9f0d9c4ddece438bf98c5bf05f3ce4d6f7fa709d Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Wed, 12 Nov 2025 21:40:35 +0000 Subject: [PATCH 04/13] Refactor includes function to use for...of loop --- Sprint-1/refactor/includes.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Sprint-1/refactor/includes.js b/Sprint-1/refactor/includes.js index 29dad81f0..3001fa212 100644 --- a/Sprint-1/refactor/includes.js +++ b/Sprint-1/refactor/includes.js @@ -1,13 +1,12 @@ // Refactor the implementation of includes to use a for...of loop function includes(list, target) { - for (let index = 0; index < list.length; index++) { - const element = list[index]; + for (const element of list) { if (element === target) { - return true; + return true; } } - return false; + return false; } module.exports = includes; From f6f580c20b16d7fed1bd8fefeeaf6e7e4a5d807c Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Sat, 15 Nov 2025 11:28:09 +0000 Subject: [PATCH 05/13] wrote the test for sum.js and updated the sum function to check if the input is not NaN --- Sprint-1/implement/sum.js | 2 +- Sprint-1/implement/sum.test.js | 42 +++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Sprint-1/implement/sum.js b/Sprint-1/implement/sum.js index 06164b255..66f0a3dda 100644 --- a/Sprint-1/implement/sum.js +++ b/Sprint-1/implement/sum.js @@ -7,7 +7,7 @@ function sum(elements) { let current = elements[i]; // get the current element // Only add it if it's a number - if (typeof current === "number") { + if (typeof current === "number" && !Number.isNaN(current)) { total = total + current; // add to total } } diff --git a/Sprint-1/implement/sum.test.js b/Sprint-1/implement/sum.test.js index dd0a090ca..ecfc8e28d 100644 --- a/Sprint-1/implement/sum.test.js +++ b/Sprint-1/implement/sum.test.js @@ -13,24 +13,64 @@ const sum = require("./sum.js"); // Given an empty array // When passed to the sum function // Then it should return 0 -test.todo("given an empty array, returns 0") +test("given an empty array, returns 0",()=>{ + const currentOutput = sum([]); + const targetPitPut= 0; + + expect(currentOutput).toEqual(targetPitPut); +}) + // Given an array with just one number // When passed to the sum function // Then it should return that number +test("given an array whit only 1 number, returns same input",()=>{ + const currentOutput = sum([4]); + const targetPitPut= 4; + + expect(currentOutput).toEqual(targetPitPut); +}) + // Given an array containing negative numbers // When passed to the sum function // Then it should still return the correct total sum +test("given an array whit a negative number, returns correct total (subtract the negative number)",()=>{ + const currentOutput = sum([1,2,3,4,-5,]); + const targetPitPut= 5; + + expect(currentOutput).toEqual(targetPitPut); +}) + // Given an array with decimal/float numbers // When passed to the sum function // Then it should return the correct total sum +test("given an array whit a decimal/float number, returns correct total (decimal/float number)",()=>{ + const currentOutput = sum([1,2,3,4,3.5,]); + const targetPitPut= 13.5; + + expect(currentOutput).toEqual(targetPitPut); +}) + + // Given an array containing non-number values // When passed to the sum function // Then it should ignore the non-numerical values and return the sum of the numerical elements +test("given an array whit a non-number values, returns correct total (ignore the NaN and sum the others)",()=>{ + const currentOutput = sum([1,2,3,4,"hi",5]); + const targetPitPut= 15; + + expect(currentOutput).toEqual(targetPitPut); +}) // Given an array with only non-number values // When passed to the sum function // Then it should return the least surprising value given how it behaves for all other inputs +test("given an array with only non-number values, returns the least surprising value (0)", () => { + const currentOutput = sum(["hello", null, undefined, {}, [], true, NaN]); + const targetOutput = 0; + + expect(currentOutput).toEqual(targetOutput); +}); From 8a4b703d3bde2f45faf7602971522ead104bb038 Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Sat, 15 Nov 2025 11:51:07 +0000 Subject: [PATCH 06/13] added the test for the max function --- Sprint-1/implement/max.test.js | 49 +++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/Sprint-1/implement/max.test.js b/Sprint-1/implement/max.test.js index 82f18fd88..9ed7f3365 100644 --- a/Sprint-1/implement/max.test.js +++ b/Sprint-1/implement/max.test.js @@ -16,28 +16,75 @@ const findMax = require("./max.js"); // When passed to the max function // Then it should return -Infinity // Delete this test.todo and replace it with a test. -test.todo("given an empty array, returns -Infinity"); + +test("given an empty array, returns -Infinity", () => { + const currentOutput = findMax([]); + const targetOutput = -Infinity; + + expect(currentOutput).toEqual(targetOutput); + }); // Given an array with one number // When passed to the max function // Then it should return that number +test("given an array with one number, returns that number", () => { + const currentOutput = findMax([42]); + const targetOutput = 42; + + expect(currentOutput).toEqual(targetOutput); +}); + + // Given an array with both positive and negative numbers // When passed to the max function // Then it should return the largest number overall +test("given an array with positive and negative numbers, returns the largest", () => { + const currentOutput = findMax([-25, 5, 20, -3, 15]); + const targetOutput = 20; + + expect(currentOutput).toEqual(targetOutput); +}); // Given an array with just negative numbers // When passed to the max function // Then it should return the closest one to zero +test("given an array of only negative numbers, returns the closest to zero", () => { + const currentOutput = findMax([-50, -3, -20, -10]); + const targetOutput = -3; + + expect(currentOutput).toEqual(targetOutput); +}); + // Given an array with decimal numbers // When passed to the max function // Then it should return the largest decimal number + test("given an array of decimal numbers, returns the largest decimal", () => { + const currentOutput = findMax([1.1, 3.5, 2.9, 3.4]); + const targetOutput = 3.5; + + expect(currentOutput).toEqual(targetOutput); +}); + // Given an array with non-number values // When passed to the max function // Then it should return the max and ignore non-numeric values +test("given an array with non-number values, returns correct max (ignoring non-numbers)", () => { + const currentOutput = findMax([10, "hi", 50, true, 3]); + const targetOutput = 50; + + expect(currentOutput).toEqual(targetOutput); +}); + // Given an array with only non-number values // When passed to the max function // Then it should return the least surprising value given how it behaves for all other inputs +test("given an array with only non-number values, returns -Infinity", () => { + const currentOutput = findMax(["a", null, undefined, {}, [], true]); + const targetOutput = -Infinity; + + expect(currentOutput).toEqual(targetOutput); +}); From 5c3214a5a5429aba110f7eddd8a07d36c9799e72 Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Sat, 15 Nov 2025 12:08:47 +0000 Subject: [PATCH 07/13] implement the dedupe function and tests --- Sprint-1/implement/dedupe.js | 16 +++++++++++++++- Sprint-1/implement/dedupe.test.js | 22 ++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Sprint-1/implement/dedupe.js b/Sprint-1/implement/dedupe.js index 781e8718a..a0055e476 100644 --- a/Sprint-1/implement/dedupe.js +++ b/Sprint-1/implement/dedupe.js @@ -1 +1,15 @@ -function dedupe() {} +function dedupe(elements) { + const unique = []; + + for (let i = 0; i < elements.length; i++) { + const current = elements[i]; + + if (!unique.includes(current)) { + unique.push(current); + } + } + + return unique; +} + +module.exports = dedupe; diff --git a/Sprint-1/implement/dedupe.test.js b/Sprint-1/implement/dedupe.test.js index 23e0f8638..c591fca74 100644 --- a/Sprint-1/implement/dedupe.test.js +++ b/Sprint-1/implement/dedupe.test.js @@ -16,12 +16,30 @@ E.g. dedupe([1, 2, 1]) target output: [1, 2] // Given an empty array // When passed to the dedupe function // Then it should return an empty array -test.todo("given an empty array, it returns an empty array"); +test("given an empty array, it returns an empty array", () => { + const currentOutput = dedupe([]); + const targetOutput = []; + + expect(currentOutput).toEqual(targetOutput); +}); // Given an array with no duplicates // When passed to the dedupe function // Then it should return a copy of the original array +test("Given an array with no duplicates, it returns an the same array", () => { + const currentOutput = dedupe([1,4,6,"d","a","x","e",0,7,8]); + const targetOutput = [1,4,6,"d","a","x","e",0,7,8]; + + expect(currentOutput).toEqual(targetOutput); +}); // Given an array with strings or numbers // When passed to the dedupe function -// Then it should remove the duplicate values, preserving the first occurence of each element +// Then it should remove the duplicate values, preserving the first occurrence of each element + +test("Given an array with strings or numbers, it returns an the array whit no duplicated elements", () => { + const currentOutput = dedupe(['a','a','a','b','b','c']); + const targetOutput = ['a','b','c']; + + expect(currentOutput).toEqual(targetOutput); +}); \ No newline at end of file From 122b561b1dc7494e25994f622671b2fbf0aef2db Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Sun, 16 Nov 2025 17:21:23 +0000 Subject: [PATCH 08/13] fix: correct object key so house number prints correctly --- Sprint-2/debug/address.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..769b960b4 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,4 +1,7 @@ -// Predict and explain first... +// Prediction: +// The code will print "My house number is undefined" +// because address[0] does not exist. +// Objects use keys like address.houseNumber, not numbers. // This code should log out the houseNumber from the address object // but it isn't working... @@ -12,4 +15,5 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +// Use the correct key of the object +console.log(`My house number is ${address.houseNumber}`); From fad683be3493993f7a35479058190c1bdcf8bd56 Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Fri, 28 Nov 2025 23:23:15 +0000 Subject: [PATCH 09/13] fix object iteration: use Object.values to loop over author properties --- Sprint-2/debug/author.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..beffa693b 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,8 +1,9 @@ // Predict and explain first... - +//The code will give an error because for...of does not work on objects. // 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 - +/*This is because for...of only works with arrays or strings. + An object like author is not an array, so the code will give an error. Nothing will be printed.*/ const author = { firstName: "Zadie", lastName: "Smith", @@ -11,6 +12,6 @@ const author = { alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { console.log(value); } From 9feaf6972e523e6cf931d22761b92d66a7e1211b Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Fri, 28 Nov 2025 23:27:21 +0000 Subject: [PATCH 10/13] fix recipe ingredients: print each ingredient on a new line --- Sprint-2/debug/recipe.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..771e5608e 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,4 +1,5 @@ // Predict and explain first... +/*The code will show [object Object] instead of the ingredients because it tries to print the whole object as a string.*/ // This program should log out the title, how many it serves and the ingredients. // Each ingredient should be logged on a new line @@ -11,5 +12,6 @@ const recipe = { }; console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +ingredients: +${recipe.ingredients.join("\n")}`); + From b793f4b5682efa60cc99dff1583f2c6a22b4b558 Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Sat, 29 Nov 2025 00:32:52 +0000 Subject: [PATCH 11/13] the implement part is done --- Sprint-2/implement/contains.js | 13 ++++++++++++- Sprint-2/implement/contains.test.js | 19 ++++++++++++++++++- Sprint-2/implement/lookup.js | 15 +++++++++++++++ Sprint-2/implement/lookup.test.js | 13 +++++++++++++ Sprint-2/implement/querystring.js | 9 +++++++-- Sprint-2/implement/querystring.test.js | 21 +++++++++++++++++++++ Sprint-2/implement/tally.js | 21 ++++++++++++++++++++- Sprint-2/implement/tally.test.js | 11 ++++++++++- 8 files changed, 116 insertions(+), 6 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..ddaaac632 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,14 @@ -function contains() {} +function contains() {function contains(obj, key) { + // First, check if obj is a real object + if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { + return false; + } + + // Use hasOwnProperty to check if the key exists + return obj.hasOwnProperty(key); +} + +module.exports = contains; +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..745ec21aa 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -20,16 +20,33 @@ 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")).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", () => { + const obj = { a: 1, b: 2 }; + expect(contains(obj, "a")).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", () => { + const obj = { a: 1, b: 2 }; + expect(contains(obj, "c")).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 input", () => { + expect(contains([], "a")).toBe(false); + expect(contains(null, "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..274792411 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,20 @@ function createLookup() { // implementation here + function createLookup(pairs) { + const lookup = {}; + + for (let i = 0; i < pairs.length; i++) { + const pair = pairs[i]; + const country = pair[0]; + const currency = pair[1]; + lookup[country] = currency; + } + + return lookup; +} + +module.exports = createLookup; + } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..a161f57c5 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -33,3 +33,16 @@ It should return: 'CA': 'CAD' } */ + +const createLookup = require("./lookup.js"); + +test("creates a country currency code lookup for multiple codes", () => { + const countryCurrencyPairs = [['US', 'USD'], ['CA', 'CAD'], ['GB', 'GBP']]; + const result = createLookup(countryCurrencyPairs); + + expect(result).toEqual({ + 'US': 'USD', + 'CA': 'CAD', + 'GB': 'GBP' + }); +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..01f3a5152 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,12 +1,17 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { + if (!queryString) { return queryParams; } + const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + const index = pair.indexOf("="); // find the first = + if (index === -1) continue; // skip if no = + + const key = pair.substring(0, index); // before the = + const value = pair.substring(index + 1); // after the = queryParams[key] = value; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..d78adb7a7 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -10,3 +10,24 @@ test("parses querystring values containing =", () => { "equation": "x=y+1", }); }); +test("parses querystring values containing =", () => { + expect(parseQueryString("equation=x=y+1")).toEqual({ + "equation": "x=y+1", + }); +}); + +test("parses empty query string", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("parses multiple key-value pairs", () => { + expect(parseQueryString("a=1&b=2")).toEqual({ a: "1", b: "2" }); +}); + +test("parses key with empty value", () => { + expect(parseQueryString("foo=")).toEqual({ foo: "" }); +}); + +test("parses empty key with value", () => { + expect(parseQueryString("=bar")).toEqual({ "": "bar" }); +}); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..bb691a7c4 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,22 @@ -function tally() {} +function tally(arr) { + if (!Array.isArray(arr)) { + throw new Error("Input must be an array"); + } + + const counts = {}; + + for (let i = 0; i < arr.length; i++) { + const item = arr[i]; + + + if (counts[item]) { + counts[item] += 1; + } else { + counts[item] = 1; + } + } + + return counts; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..910fa2532 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -23,12 +23,21 @@ 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 on an empty array returns an empty object", () => { + expect(tally([])).toEqual({}); +}); + // Given an invalid input like a string // When passed to tally // Then it should throw an error +test("tally throws error for invalid input", () => { + expect(() => tally("not an array")).toThrow("Input must be an array"); +}); \ No newline at end of file From 08af2c857309462a19aef76e78bcb0a77e6de812 Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Sat, 6 Dec 2025 14:13:28 +0000 Subject: [PATCH 12/13] done sprint 2 --- Sprint-2/interpret/invert.js | 42 +++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..ac4de901c 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -6,24 +6,64 @@ // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} -function invert(obj) { +/*function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { invertedObj.key = value; } + return invertedObj; +}*/ + +//fixed code: +function invert(obj) { + const invertedObj = {}; + + for (const [key, value] of Object.entries(obj)) { + invertedObj[value] = key; // use the value as key, key as value + } + 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? + /*Object.entries(obj) returns an array of [key, value] pairs. +Needed because we want to loop through keys and values at the same time.*/ // d) Explain why the current return value is different from the target output +/*Because the code is wrong in one place: + +invertedObj.key = value; + + +This uses the word "key" as a property name instead of using the variable key. + +So instead of creating: + +{ 1: "a" } + + +it always creates: + +{ key: 1 } + + +and for multiple items, it keeps overwriting the same "key" */ + + // e) Fix the implementation of invert (and write tests to prove it's fixed!) + // I did write upt there. \ No newline at end of file From 2b7ed5be47df1db2c5bba63a75986101b49d1b4c Mon Sep 17 00:00:00 2001 From: Hadi Vahidi Date: Sat, 20 Dec 2025 23:20:39 +0000 Subject: [PATCH 13/13] Fix Sprint-2 implementations and revert Sprint-1 files --- Sprint-1/fix/median.js | 39 ++------------------ Sprint-1/implement/dedupe.js | 16 +-------- Sprint-1/implement/dedupe.test.js | 22 ++---------- Sprint-1/implement/max.js | 17 --------- Sprint-1/implement/max.test.js | 49 +------------------------- Sprint-1/implement/sum.js | 15 -------- Sprint-1/implement/sum.test.js | 42 +--------------------- Sprint-1/refactor/includes.js | 7 ++-- Sprint-2/implement/contains.js | 9 ++--- Sprint-2/implement/contains.test.js | 2 +- Sprint-2/implement/lookup.js | 20 ++++------- Sprint-2/implement/lookup.test.js | 2 -- Sprint-2/implement/querystring.js | 12 +++++-- Sprint-2/implement/querystring.test.js | 11 +++--- Sprint-2/implement/tally.js | 4 +-- Sprint-2/implement/tally.test.js | 12 +++++-- 16 files changed, 49 insertions(+), 230 deletions(-) diff --git a/Sprint-1/fix/median.js b/Sprint-1/fix/median.js index 6b4ff09b9..b22590bc6 100644 --- a/Sprint-1/fix/median.js +++ b/Sprint-1/fix/median.js @@ -6,42 +6,9 @@ // or 'list' has mixed values (the function is expected to sort only numbers). function calculateMedian(list) { - if (!Array.isArray(list)) { - return null; - } - - const numbers = []; - for (let i = 0; i < list.length; i++) { - if (typeof list[i] === 'number') { - numbers.push(list[i]); - } - } - - if (numbers.length === 0) { - return null; - } - - for (let i = 0; i < numbers.length; i++) { - for (let j = i + 1; j < numbers.length; j++) { - if (numbers[i] > numbers[j]) { - const temp = numbers[i]; - numbers[i] = numbers[j]; - numbers[j] = temp; - } - } - } - - const middleIndex = Math.floor(numbers.length / 2); - - - if (numbers.length % 2 === 0) { - - return (numbers[middleIndex - 1] + numbers[middleIndex]) / 2; - } else { - - return numbers[middleIndex]; - } + const middleIndex = Math.floor(list.length / 2); + const median = list.splice(middleIndex, 1)[0]; + return median; } - module.exports = calculateMedian; diff --git a/Sprint-1/implement/dedupe.js b/Sprint-1/implement/dedupe.js index a0055e476..781e8718a 100644 --- a/Sprint-1/implement/dedupe.js +++ b/Sprint-1/implement/dedupe.js @@ -1,15 +1 @@ -function dedupe(elements) { - const unique = []; - - for (let i = 0; i < elements.length; i++) { - const current = elements[i]; - - if (!unique.includes(current)) { - unique.push(current); - } - } - - return unique; -} - -module.exports = dedupe; +function dedupe() {} diff --git a/Sprint-1/implement/dedupe.test.js b/Sprint-1/implement/dedupe.test.js index c591fca74..23e0f8638 100644 --- a/Sprint-1/implement/dedupe.test.js +++ b/Sprint-1/implement/dedupe.test.js @@ -16,30 +16,12 @@ E.g. dedupe([1, 2, 1]) target output: [1, 2] // Given an empty array // When passed to the dedupe function // Then it should return an empty array +test.todo("given an empty array, it returns an empty array"); -test("given an empty array, it returns an empty array", () => { - const currentOutput = dedupe([]); - const targetOutput = []; - - expect(currentOutput).toEqual(targetOutput); -}); // Given an array with no duplicates // When passed to the dedupe function // Then it should return a copy of the original array -test("Given an array with no duplicates, it returns an the same array", () => { - const currentOutput = dedupe([1,4,6,"d","a","x","e",0,7,8]); - const targetOutput = [1,4,6,"d","a","x","e",0,7,8]; - - expect(currentOutput).toEqual(targetOutput); -}); // Given an array with strings or numbers // When passed to the dedupe function -// Then it should remove the duplicate values, preserving the first occurrence of each element - -test("Given an array with strings or numbers, it returns an the array whit no duplicated elements", () => { - const currentOutput = dedupe(['a','a','a','b','b','c']); - const targetOutput = ['a','b','c']; - - expect(currentOutput).toEqual(targetOutput); -}); \ No newline at end of file +// Then it should remove the duplicate values, preserving the first occurence of each element diff --git a/Sprint-1/implement/max.js b/Sprint-1/implement/max.js index 15adb4c1b..6dd76378e 100644 --- a/Sprint-1/implement/max.js +++ b/Sprint-1/implement/max.js @@ -1,21 +1,4 @@ function findMax(elements) { - if (elements.length === 0) { - return -Infinity; - } - - let max = -Infinity; - - for (let i = 0; i < elements.length; i++) { - let current = elements[i]; - - if (typeof current === "number") { - if (current > max) { - max = current; - } - } - } - - return max; } module.exports = findMax; diff --git a/Sprint-1/implement/max.test.js b/Sprint-1/implement/max.test.js index 9ed7f3365..82f18fd88 100644 --- a/Sprint-1/implement/max.test.js +++ b/Sprint-1/implement/max.test.js @@ -16,75 +16,28 @@ const findMax = require("./max.js"); // When passed to the max function // Then it should return -Infinity // Delete this test.todo and replace it with a test. - -test("given an empty array, returns -Infinity", () => { - const currentOutput = findMax([]); - const targetOutput = -Infinity; - - expect(currentOutput).toEqual(targetOutput); - }); +test.todo("given an empty array, returns -Infinity"); // Given an array with one number // When passed to the max function // Then it should return that number -test("given an array with one number, returns that number", () => { - const currentOutput = findMax([42]); - const targetOutput = 42; - - expect(currentOutput).toEqual(targetOutput); -}); - - // Given an array with both positive and negative numbers // When passed to the max function // Then it should return the largest number overall -test("given an array with positive and negative numbers, returns the largest", () => { - const currentOutput = findMax([-25, 5, 20, -3, 15]); - const targetOutput = 20; - - expect(currentOutput).toEqual(targetOutput); -}); // Given an array with just negative numbers // When passed to the max function // Then it should return the closest one to zero -test("given an array of only negative numbers, returns the closest to zero", () => { - const currentOutput = findMax([-50, -3, -20, -10]); - const targetOutput = -3; - - expect(currentOutput).toEqual(targetOutput); -}); - // Given an array with decimal numbers // When passed to the max function // Then it should return the largest decimal number - test("given an array of decimal numbers, returns the largest decimal", () => { - const currentOutput = findMax([1.1, 3.5, 2.9, 3.4]); - const targetOutput = 3.5; - - expect(currentOutput).toEqual(targetOutput); -}); - // Given an array with non-number values // When passed to the max function // Then it should return the max and ignore non-numeric values -test("given an array with non-number values, returns correct max (ignoring non-numbers)", () => { - const currentOutput = findMax([10, "hi", 50, true, 3]); - const targetOutput = 50; - - expect(currentOutput).toEqual(targetOutput); -}); - // Given an array with only non-number values // When passed to the max function // Then it should return the least surprising value given how it behaves for all other inputs -test("given an array with only non-number values, returns -Infinity", () => { - const currentOutput = findMax(["a", null, undefined, {}, [], true]); - const targetOutput = -Infinity; - - expect(currentOutput).toEqual(targetOutput); -}); diff --git a/Sprint-1/implement/sum.js b/Sprint-1/implement/sum.js index 66f0a3dda..9062aafe3 100644 --- a/Sprint-1/implement/sum.js +++ b/Sprint-1/implement/sum.js @@ -1,19 +1,4 @@ function sum(elements) { - // This variable will store the total sum - let total = 0; - - // Go through each element in the array - for (let i = 0; i < elements.length; i++) { - let current = elements[i]; // get the current element - - // Only add it if it's a number - if (typeof current === "number" && !Number.isNaN(current)) { - total = total + current; // add to total - } - } - - // Return the sum of all numbers - return total; } module.exports = sum; diff --git a/Sprint-1/implement/sum.test.js b/Sprint-1/implement/sum.test.js index ecfc8e28d..dd0a090ca 100644 --- a/Sprint-1/implement/sum.test.js +++ b/Sprint-1/implement/sum.test.js @@ -13,64 +13,24 @@ const sum = require("./sum.js"); // Given an empty array // When passed to the sum function // Then it should return 0 -test("given an empty array, returns 0",()=>{ - const currentOutput = sum([]); - const targetPitPut= 0; - - expect(currentOutput).toEqual(targetPitPut); -}) - +test.todo("given an empty array, returns 0") // Given an array with just one number // When passed to the sum function // Then it should return that number -test("given an array whit only 1 number, returns same input",()=>{ - const currentOutput = sum([4]); - const targetPitPut= 4; - - expect(currentOutput).toEqual(targetPitPut); -}) - // Given an array containing negative numbers // When passed to the sum function // Then it should still return the correct total sum -test("given an array whit a negative number, returns correct total (subtract the negative number)",()=>{ - const currentOutput = sum([1,2,3,4,-5,]); - const targetPitPut= 5; - - expect(currentOutput).toEqual(targetPitPut); -}) - // Given an array with decimal/float numbers // When passed to the sum function // Then it should return the correct total sum -test("given an array whit a decimal/float number, returns correct total (decimal/float number)",()=>{ - const currentOutput = sum([1,2,3,4,3.5,]); - const targetPitPut= 13.5; - - expect(currentOutput).toEqual(targetPitPut); -}) - - // Given an array containing non-number values // When passed to the sum function // Then it should ignore the non-numerical values and return the sum of the numerical elements -test("given an array whit a non-number values, returns correct total (ignore the NaN and sum the others)",()=>{ - const currentOutput = sum([1,2,3,4,"hi",5]); - const targetPitPut= 15; - - expect(currentOutput).toEqual(targetPitPut); -}) // Given an array with only non-number values // When passed to the sum function // Then it should return the least surprising value given how it behaves for all other inputs -test("given an array with only non-number values, returns the least surprising value (0)", () => { - const currentOutput = sum(["hello", null, undefined, {}, [], true, NaN]); - const targetOutput = 0; - - expect(currentOutput).toEqual(targetOutput); -}); diff --git a/Sprint-1/refactor/includes.js b/Sprint-1/refactor/includes.js index 3001fa212..29dad81f0 100644 --- a/Sprint-1/refactor/includes.js +++ b/Sprint-1/refactor/includes.js @@ -1,12 +1,13 @@ // Refactor the implementation of includes to use a for...of loop function includes(list, target) { - for (const element of list) { + for (let index = 0; index < list.length; index++) { + const element = list[index]; if (element === target) { - return true; + return true; } } - return false; + return false; } module.exports = includes; diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index ddaaac632..4dee84379 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,14 +1,9 @@ -function contains() {function contains(obj, key) { - // First, check if obj is a real object +function contains(obj, key) { if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { return false; } - // Use hasOwnProperty to check if the key exists - return obj.hasOwnProperty(key); -} - -module.exports = contains; + 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 745ec21aa..09c826e89 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -46,7 +46,7 @@ test("contains returns false for non-existent property", () => { // When passed to contains // Then it should return false or throw an error test("contains returns false for invalid input", () => { - expect(contains([], "a")).toBe(false); + expect(contains([], "length")).toBe(false); expect(contains(null, "a")).toBe(false); expect(contains("string", "a")).toBe(false); }); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index 274792411..48c25a3e2 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,20 +1,14 @@ -function createLookup() { - // implementation here - function createLookup(pairs) { - const lookup = {}; +function createLookup(pairs) { + const lookup = {}; for (let i = 0; i < pairs.length; i++) { - const pair = pairs[i]; - const country = pair[0]; - const currency = pair[1]; - lookup[country] = currency; + const pair = pairs[i]; + const country = pair[0]; + const currency = pair[1]; + lookup[country] = currency; } - return lookup; -} - -module.exports = createLookup; - + return lookup; } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index a161f57c5..62975fbcc 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -34,8 +34,6 @@ It should return: } */ -const createLookup = require("./lookup.js"); - test("creates a country currency code lookup for multiple codes", () => { const countryCurrencyPairs = [['US', 'USD'], ['CA', 'CAD'], ['GB', 'GBP']]; const result = createLookup(countryCurrencyPairs); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 01f3a5152..55b7de1fe 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -4,14 +4,22 @@ function parseQueryString(queryString) { return queryParams; } + function decodePart(text) { + try { + return decodeURIComponent(text); + } catch (error) { + return text; + } + } + const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { const index = pair.indexOf("="); // find the first = if (index === -1) continue; // skip if no = - const key = pair.substring(0, index); // before the = - const value = pair.substring(index + 1); // after the = + const key = decodePart(pair.substring(0, index)); // before the = + const value = decodePart(pair.substring(index + 1)); // after the = queryParams[key] = value; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index d78adb7a7..3193b5284 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -5,11 +5,6 @@ const parseQueryString = require("./querystring.js") -test("parses querystring values containing =", () => { - expect(parseQueryString("equation=x=y+1")).toEqual({ - "equation": "x=y+1", - }); -}); test("parses querystring values containing =", () => { expect(parseQueryString("equation=x=y+1")).toEqual({ "equation": "x=y+1", @@ -31,3 +26,9 @@ test("parses key with empty value", () => { test("parses empty key with value", () => { expect(parseQueryString("=bar")).toEqual({ "": "bar" }); }); + +test("parses url encoded key and value", () => { + expect(parseQueryString("tags%5B%5D=hello%20world")).toEqual({ + "tags[]": "hello world", + }); +}); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index bb691a7c4..2f0a61d15 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -3,13 +3,13 @@ function tally(arr) { throw new Error("Input must be an array"); } - const counts = {}; + const counts = Object.create(null); for (let i = 0; i < arr.length; i++) { const item = arr[i]; - if (counts[item]) { + if (Object.prototype.hasOwnProperty.call(counts, item)) { counts[item] += 1; } else { counts[item] = 1; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 910fa2532..cbe4a8198 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -24,14 +24,20 @@ const tally = require("./tally.js"); // When passed to tally // Then it should return an empty object test("tally on an empty array returns an empty object", () => { - expect(tally([])).toEqual({}); + expect(tally([])).toEqual(Object.create(null)); }); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item test("tally on an empty array returns an empty object", () => { - expect(tally([])).toEqual({}); + expect(tally([])).toEqual(Object.create(null)); +}); + +test("tally counts keys like toString", () => { + const expected = Object.create(null); + expected.toString = 2; + expect(tally(["toString", "toString"])).toEqual(expected); }); @@ -40,4 +46,4 @@ test("tally on an empty array returns an empty object", () => { // Then it should throw an error test("tally throws error for invalid input", () => { expect(() => tally("not an array")).toThrow("Input must be an array"); -}); \ No newline at end of file +});