From b9c0a55db8bf482ca7ed11768a72abd02af429ac Mon Sep 17 00:00:00 2001 From: miriamjorna Date: Fri, 10 Apr 2026 15:44:50 +0100 Subject: [PATCH 1/6] Debugs --- Sprint-2/debug/address.js | 4 +++- Sprint-2/debug/author.js | 3 ++- Sprint-2/debug/recipe.js | 5 ++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..f7eeb4ad7 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -11,5 +11,7 @@ const address = { country: "England", postcode: "XYZ 123", }; +// not an array so an index number doesn't work +console.log(`My house number is ${address.houseNumber}`); + -console.log(`My house number is ${address[0]}`); diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..0ec777310 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -11,6 +11,7 @@ const author = { alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { console.log(value); } +//"author" is not iterable. for...of is for arrays, not for objects diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..9f5b32d5d 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -12,4 +12,7 @@ const recipe = { console.log(`${recipe.title} serves ${recipe.serves} ingredients: -${recipe}`); +${recipe.ingredients.join("\n")}`); + +// the ${recipe} tries to log everything into one string (and fails). +// used join to list them separately and on new lines From a3b820b21e29790507e9a1e5e8853c26ecc8ca62 Mon Sep 17 00:00:00 2001 From: miriamjorna Date: Fri, 10 Apr 2026 16:35:52 +0100 Subject: [PATCH 2/6] Implement section, but without the querystring task --- Sprint-2/implement/contains.js | 7 ++++++- Sprint-2/implement/contains.test.js | 14 +++++++++++++- Sprint-2/implement/lookup.js | 8 ++++++-- Sprint-2/implement/lookup.test.js | 27 +++++++++++++++++++++++++-- Sprint-2/implement/tally.js | 15 ++++++++++++++- Sprint-2/implement/tally.test.js | 14 +++++++++++++- 6 files changed, 77 insertions(+), 8 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..a2e1206e7 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,8 @@ -function contains() {} +function contains(obj, property) { + if (Array.isArray(obj) || typeof obj !== "object" || obj === null) { + return false; + } + return obj.hasOwnProperty(property); +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..32a1bf0ba 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -20,16 +20,28 @@ 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 an empty object returns false", () => { + expect(contains({}, "a")).toEqual(false); +}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test("given an object with properties, returns true for existing property", () => { + expect(contains({a: 1, b: 2}, "a")).toEqual(true); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test("given an object with properties, returns false for non-existent property", () => { + expect(contains({a: 1, b: 2}, "c")).toEqual(false); +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test("given invalid parameters like an array, returns false", () => { + expect(contains([1, 2, 3], "a")).toEqual(false); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..2bcbbd79b 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,9 @@ -function createLookup() { - // implementation here +function createLookup(pairs) { + const lookup = {}; + for (const [country, currency] of pairs) { + lookup[country] = currency; + } + return lookup; } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..4015cb19e 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,7 +1,30 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); - +test("returns an empty object for an empty array", () => { + expect(createLookup([])).toEqual({}); +}); + +test("creates a country currency code lookup for multiple codes", () => { + expect(createLookup([['US', 'USD'], ['CA', 'CAD']])).toEqual({ + 'US': 'USD', + 'CA': 'CAD' + }); +}); + +test("creates a lookup for a single pair", () => { + expect(createLookup([['GB', 'GBP']])).toEqual({ 'GB': 'GBP' }); +}); + +test("creates a lookup for multiple European countries with EUR", () => { + expect(createLookup([['NLD', 'EUR'], ['GER', 'EUR'], ['BEL', 'EUR'], ['FRA', 'EUR'], ['ESP', 'EUR'], ['POR', 'EUR']])).toEqual({ + 'NLD': 'EUR', + 'GER': 'EUR', + 'BEL': 'EUR', + 'FRA': 'EUR', + 'ESP': 'EUR', + 'POR': 'EUR' + }); +}); /* Create a lookup object of key value pairs from an array of code pairs diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..f4889e603 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,16 @@ -function tally() {} +function tally(arr) { + if (!Array.isArray(arr)) { + throw new Error("Input must be an array"); + } + const result = {}; + for (const item of arr) { + if (result[item]) { + result[item]++; + } else { + result[item] = 1; + } + } + return result; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..fcec42a90 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -23,12 +23,24 @@ 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({}); +}); + +test("given an array with one item, returns count of 1", () => { + expect(tally(['a'])).toEqual({ a: 1 }); +}); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item +test("tally on an array with duplicate items returns counts for each type", () => { + 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, throws an error", () => { + expect(() => tally("invalid")).toThrow(); +}); From f5c978ae8182c3793121e406e978d5f8400f54b8 Mon Sep 17 00:00:00 2001 From: miriamjorna Date: Fri, 10 Apr 2026 16:54:17 +0100 Subject: [PATCH 3/6] adding querystring task --- Sprint-2/implement/querystring.js | 4 +++- Sprint-2/implement/querystring.test.js | 23 +++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..84b6a41fa 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,7 +6,9 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + const index = pair.indexOf("="); + const key = pair.slice(0, index); + const value = pair.slice(index + 1); queryParams[key] = value; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..ddc748e71 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -7,6 +7,25 @@ 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 empty querystring", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("parses querystring with empty value", () => { + expect(parseQueryString("name=")).toEqual({ name: "" }); +}); + +test("parses querystring values containing =", () => { + expect(parseQueryString("equation=x=y+1")).toEqual({ equation: "x=y+1" }); +}); + +test("parses single key value pair", () => { + expect(parseQueryString("name=Ntombi")).toEqual({ name: "Ntombi" }); +}); + +test("parses multiple key value pairs", () => { + expect(parseQueryString("name=Ntombi&age=40")).toEqual({ name: "Ntombi", age: "40" }); }); From 30de7d5cdb2bbe68ea2398a2ff89f9ae11827927 Mon Sep 17 00:00:00 2001 From: miriamjorna Date: Fri, 10 Apr 2026 17:38:24 +0100 Subject: [PATCH 4/6] invert.js questions answered without fix or tests --- Sprint-2/interpret/invert.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..654ce6ed8 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -17,13 +17,21 @@ function invert(obj) { } // a) What is the current return value when invert is called with { a : 1 } +// the current return value is {key : 1 } // b) What is the current return value when invert is called with { a: 1, b: 2 } +// the current return value is {key : 2} because the key is overwritten and +// only the last value is stored // 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? +// Object.entries converts an object into an array of [key, value] pairs. it is needed because +// for...of needs an array to work with // d) Explain why the current return value is different from the target output +// invertedObj.key = value creates a literal string with the name "key" instead of the value key. +// also, the current code assigns "value" to the new property instead of "key" // e) Fix the implementation of invert (and write tests to prove it's fixed!) From b3b0ea7f724cd484d5a3c9add5e4e7912f993df7 Mon Sep 17 00:00:00 2001 From: miriamjorna Date: Fri, 10 Apr 2026 17:45:42 +0100 Subject: [PATCH 5/6] invert.js finished --- Sprint-2/interpret/invert.js | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index 654ce6ed8..fa46e3694 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -6,15 +6,18 @@ // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} -function invert(obj) { - const invertedObj = {}; +// leaving original code in for easier referral: - for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; - } +// function invert(obj) { +// const invertedObj = {}; +// +// for (const [key, value] of Object.entries(obj)) { +// invertedObj.key = value; +// } +// +// return invertedObj; +//} - return invertedObj; -} // a) What is the current return value when invert is called with { a : 1 } // the current return value is {key : 1 } @@ -35,3 +38,17 @@ function invert(obj) { // also, the current code assigns "value" to the new property instead of "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; +} + +// Tests +console.log(invert({ a: 1 })); // Expected output: { "1": "a" } +console.log(invert({ a: 1, b: 2 })); // Expected output: { "1": "a", "2": "b" } From 0c8870bfa461111c5b02d8514e948d528f633c53 Mon Sep 17 00:00:00 2001 From: miriamjorna Date: Thu, 16 Apr 2026 09:51:53 +0100 Subject: [PATCH 6/6] corrections after review --- Sprint-2/implement/contains.test.js | 2 +- Sprint-2/implement/querystring.js | 2 ++ Sprint-2/implement/tally.js | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 32a1bf0ba..f27b7dd16 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -43,5 +43,5 @@ test("given an object with properties, returns false for non-existent property", // When passed to contains // Then it should return false or throw an error test("given invalid parameters like an array, returns false", () => { - expect(contains([1, 2, 3], "a")).toEqual(false); + expect(contains([1, 2, 3], "0")).toEqual(false); }); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 84b6a41fa..1b7c774d9 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -7,8 +7,10 @@ function parseQueryString(queryString) { for (const pair of keyValuePairs) { const index = pair.indexOf("="); + if (index === -1) continue; // skip pairs with no "=" const key = pair.slice(0, index); const value = pair.slice(index + 1); + if (key === "") continue; // skip empty keys queryParams[key] = value; } diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f4889e603..966754b3a 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -4,13 +4,13 @@ function tally(arr) { } const result = {}; for (const item of arr) { - if (result[item]) { - result[item]++; - } else { - result[item] = 1; + if (Object.hasOwn(result, item)) { + result[item]++; +} else { + result[item] = 1; +} } } return result; -} module.exports = tally;