From ddab5f8bebb78dccf44c5db221c3e75dd0112feb Mon Sep 17 00:00:00 2001 From: Alina Sofragiu Date: Sun, 19 Jul 2026 00:48:07 +0100 Subject: [PATCH] Add Sprint-2 work only --- Sprint-2/debug/address.js | 4 +- Sprint-2/debug/author.js | 4 +- Sprint-2/debug/recipe.js | 5 +- Sprint-2/implement/contains.js | 8 ++- Sprint-2/implement/contains.test.js | 19 ++++--- Sprint-2/implement/lookup.js | 13 ++++- Sprint-2/implement/lookup.test.js | 11 ++++ Sprint-2/implement/querystring.js | 38 ++++++++++++- Sprint-2/implement/querystring.test.js | 36 ++++++++++++- Sprint-2/implement/tally.js | 18 ++++++- Sprint-2/implement/tally.test.js | 74 +++++++++++++++++++++++--- Sprint-2/interpret/invert.js | 14 ++--- Sprint-2/interpret/invert.test.js | 14 +++++ 13 files changed, 225 insertions(+), 33 deletions(-) create mode 100644 Sprint-2/interpret/invert.test.js diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..00c16466e 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,5 +1,5 @@ // Predict and explain first... - +//code won't work because it's looking for the house number in the wrong place // This code should log out the houseNumber from the address object // but it isn't working... // Fix anything that isn't working @@ -12,4 +12,4 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +console.log(`My house number is ${address.houseNumber}`); diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..5b6ea1e48 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,5 +1,5 @@ // Predict and explain first... - +//will give an error because author is an object, and you can't use a for...of loop directly on an object // 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 @@ -11,6 +11,6 @@ const author = { alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { console.log(value); } diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..faa715f1d 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,5 +1,5 @@ // Predict and explain first... - +//it won't print the ingredients properly because recipe is an object // This program should log out the title, how many it serves and the ingredients. // Each ingredient should be logged on a new line // How can you fix it? @@ -11,5 +11,4 @@ const recipe = { }; console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +Ingredients:${recipe.ingredients.join("\n")}`); diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..67bad14f5 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,9 @@ -function contains() {} +function contains(object, property) { + if (typeof object !== "object" || object === null || Array.isArray(object)) { + return false; + } + + return object.hasOwnProperty(property); +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..798b9ccb8 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -8,28 +8,35 @@ 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' +as the object doesn't contain 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 - // 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 when property exists", () => { + expect(contains({ a: 1, b: 2 }, "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 when property does not exist", () => { + expect(contains({ a: 1, b: 2 }, "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 when passed an array", () => { + expect(contains([], "length")).toBe(false); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..e87f9c05d 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,14 @@ -function createLookup() { - // implementation here +function createLookup(countryCurrencyPairs) { + const lookup = {}; + + for (const pair of countryCurrencyPairs) { + const countryCode = pair[0]; + const currencyCode = pair[1]; + + lookup[countryCode] = currencyCode; + } + + return lookup; } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..d5cb19e63 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -33,3 +33,14 @@ It should return: 'CA': 'CAD' } */ +test("creates a country currency code lookup for multiple codes", () => { + const countryCurrencyPairs = [ + ["US", "USD"], + ["CA", "CAD"], + ]; + + expect(createLookup(countryCurrencyPairs)).toEqual({ + US: "USD", + CA: "CAD", + }); +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..1b287f1c3 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,13 +1,47 @@ function parseQueryString(queryString) { const queryParams = {}; + + if (queryString === null || queryString === undefined) { + return queryParams; + } + if (queryString.length === 0) { return queryParams; } + const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + // Ignore empty pairs + if (pair === "") { + continue; + } + + // Split only on the first "=" + const [keyPart, ...valueParts] = pair.split("="); + + let key = keyPart; + let value = valueParts.join("="); + + // If there is no "=" the value should be empty + if (valueParts.length === 0) { + value = ""; + } + + // Replace + with spaces and decode URL encoding + key = decodeURIComponent(key.replace(/\+/g, " ")); + value = decodeURIComponent(value.replace(/\+/g, " ")); + + // Handle duplicate keys + if (Object.prototype.hasOwnProperty.call(queryParams, key)) { + if (Array.isArray(queryParams[key])) { + queryParams[key].push(value); + } else { + queryParams[key] = [queryParams[key], value]; + } + } else { + queryParams[key] = value; + } } return queryParams; diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 328b8df61..b468ef9d1 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,7 +3,7 @@ // Below are some test cases the implementation doesn't handle well. // Fix the implementation for these tests, and try to think of as many other edge cases as possible - write tests and fix those too. -const parseQueryString = require("./querystring.js") +const parseQueryString = require("./querystring.js"); test("should parse values containing '='", () => { expect(parseQueryString("equation=a=b-2")).toEqual({ @@ -46,3 +46,37 @@ test("should store values of a key in an array when the key has 2 or more values foo: "bar", }); }); + +test("should return an empty object for an empty query string", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("should handle values containing multiple equals signs", () => { + expect(parseQueryString("url=a=b=c")).toEqual({ + url: "a=b=c", + }); +}); + +test("should ignore multiple empty pairs", () => { + expect(parseQueryString("&&foo=bar&&")).toEqual({ + foo: "bar", + }); +}); + +test("should decode spaces using percent encoding", () => { + expect(parseQueryString("name=John%20Doe")).toEqual({ + name: "John Doe", + }); +}); + +test("should handle empty values with duplicate keys", () => { + expect(parseQueryString("key=&key=")).toEqual({ + key: ["", ""], + }); +}); + +test("should handle duplicate empty keys", () => { + expect(parseQueryString("=one&=two")).toEqual({ + "": ["one", "two"], + }); +}); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..68af13a7a 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,19 @@ -function tally() {} +function tally(items) { + if (!Array.isArray(items)) { + throw new Error("Input must be an array"); + } + + const counts = {}; + + for (const item of items) { + if (counts[item]) { + counts[item]++; + } 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..b729296c3 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -14,21 +14,81 @@ const tally = require("./tally.js"); * tally(['a', 'a', 'b', 'c']), target output: { a : 2, b: 1, c: 1 } */ -// Acceptance criteria: - -// Given a function called tally -// When passed an array of items -// Then it should return an object containing the count for each unique item - // 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 one item +// When passed to tally +// Then it should return the item with a count of 1 +test("tally counts a single item", () => { + 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 counts duplicate items", () => { + expect(tally(["a", "a", "a"])).toEqual({ + a: 3, + }); +}); + +// Given an array with multiple unique items +// When passed to tally +// Then it should return the count for each item +test("tally counts multiple different items", () => { + expect(tally(["a", "a", "b", "c"])).toEqual({ + a: 2, + b: 1, + c: 1, + }); +}); + +// Given an array with numbers +// When passed to tally +// Then it should count numbers correctly +test("tally counts numbers", () => { + expect(tally([1, 1, 2, 3, 3])).toEqual({ + 1: 2, + 2: 1, + 3: 2, + }); +}); + +// Given an array with mixed strings +// When passed to tally +// Then it should count each string correctly +test("tally counts different strings", () => { + expect(tally(["cat", "dog", "cat", "bird"])).toEqual({ + cat: 2, + dog: 1, + bird: 1, + }); +}); // Given an invalid input like a string // When passed to tally // Then it should throw an error +test("tally throws an error for invalid input", () => { + expect(() => tally("hello")).toThrow(); +}); + +// Given null as input +// When passed to tally +// Then it should throw an error +test("tally throws an error for null input", () => { + expect(() => tally(null)).toThrow(); +}); + +// Given undefined as input +// When passed to tally +// Then it should throw an error +test("tally throws an error for undefined input", () => { + expect(() => tally(undefined)).toThrow(); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..c082c8ef3 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,22 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; } return invertedObj; } -// a) What is the current return value when invert is called with { a : 1 } +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? - +//returns an array of key-value pairs and we need both the key and the value to swap them // d) Explain why the current return value is different from the target output - +//creates a property called "key" instead of using the actual key variable // 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..ac0b9632e --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,14 @@ +const invert = require("./invert"); + +test("inverts an object", () => { + expect(invert({ a: 1, b: 2 })).toEqual({ + 1: "a", + 2: "b", + }); +}); + +test("inverts a single key-value pair", () => { + expect(invert({ x: 10 })).toEqual({ + 10: "x", + }); +});