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 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..f27b7dd16 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], "0")).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/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..1b7c774d9 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,7 +6,11 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + 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/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" }); }); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..966754b3a 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 (Object.hasOwn(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(); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..fa46e3694 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -6,24 +6,49 @@ // 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 } // 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!) + +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" }