diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..36d2f865d 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -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..7ba8f4c75 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -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..a413b3332 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -12,4 +12,4 @@ const recipe = { console.log(`${recipe.title} serves ${recipe.serves} ingredients: -${recipe}`); +${recipe.ingredients.join("\n")}`); diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..b702a1bc8 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,8 @@ -function contains() {} +function contains(obj, prop) { + if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { + return false; + } + return Object.prototype.hasOwnProperty.call(obj, prop); +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..265170a4b 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -20,16 +20,27 @@ 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 returns false for an empty object", () => { + 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 an existing property", () => { + 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 for a non-existent property", () => { + 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 for array input", () => { + expect(contains([1, 2, 3], "a")).toBe(false); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..2493c39d8 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,9 @@ -function createLookup() { - // implementation here +function createLookup(countryCurrencyPairs) { + let lookup = {}; + for (let [country, currency] of countryCurrencyPairs) { + 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..cb3230055 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,7 +1,20 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); - +test("returns an empty object for an empty input array", () => { + expect(createLookup([])).toEqual({}); +}); + +test("creates a country currency code lookup for multiple codes", () => { + const countryCurrencyPairs = [ + ["US", "USD"], + ["CA", "CAD"], + ]; + const output = { + US: "USD", + CA: "CAD", + }; + expect(createLookup(countryCurrencyPairs)).toEqual(output); +}); /* 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..73e600656 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,7 +6,15 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + const idx = pair.indexOf("="); + let key, value; + if (idx === -1) { + key = decodeURIComponent(pair); + value = undefined; + } else { + key = decodeURIComponent(pair.slice(0, idx)); + value = decodeURIComponent(pair.slice(idx + 1)); + } queryParams[key] = value; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..5e244ce7e 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,22 @@ // 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"); + +test("parses empty string", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("parses key with empty value", () => { + expect(parseQueryString("abc=")).toEqual({ abc: "" }); +}); test("parses querystring values containing =", () => { - expect(parseQueryString("equation=x=y+1")).toEqual({ - "equation": "x=y+1", + expect(parseQueryString("ab=x=y+1")).toEqual({ + ab: "x=y+1", }); }); + +test("parses multiple pairs", () => { + expect(parseQueryString("a=1&b=2")).toEqual({ a: "1", b: "2" }); +}); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..590fa6c7d 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,19 @@ -function tally() {} +function tally(arr) { + if (!Array.isArray(arr)) { + throw new Error("Input should be an array"); + } + const obj = {}; + if (arr.length === 0) { + return obj; + } + arr.forEach((item) => { + if (obj[item]) { + obj[item]++; + } else { + obj[item] = 1; + } + }); + return obj; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..e94c0e6a0 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -19,16 +19,30 @@ 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("tally returns empty object for empty array", () => { + expect( + tally(["apple", "banana", "apple", "orange", "banana", "apple"]) + ).toEqual({ apple: 3, banana: 2, orange: 1 }); +}); // 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 returns empty object for empty array", () => { + expect(tally([])).toEqual({}); +}); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item +test("tally counts repeated elements", () => { + expect(tally(["a", ["a"]])).toEqual({ a: 2 }); + 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, throw an error", () => { + expect(() => tally("abc")).toThrow("Input should be an array"); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..4197b3cd0 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -7,23 +7,39 @@ // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} function invert(obj) { + if (obj.length === 0) { + return obj; + } + const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + invertedObj[value] = key; } return invertedObj; } +module.exports = invert; + // a) What is the current return value when invert is called with { a : 1 } +// invert({ a: 1 }); // b) What is the current return value when invert is called with { a: 1, b: 2 } +// invert({ a: 1, b: 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? +/* +Using Object.entries() allows you to use a for...of loop + to access both keys making it easy to swap them when creating a new object. + */ // d) Explain why the current return value is different from the target output +//Because in the line 13 the invertedObj.key statement +// is not using the value of the 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..a7722f3d6 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,20 @@ +const invert = require("./invert"); + +test("handles empty object", () => { + expect(invert({})).toEqual({}); +}); + +test("invert returns empty object for empty obj", () => { + expect(invert({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" }); +}); + +test("inverts an object with numbers as values", () => { + expect(invert({ a: 1, b: 2 })).toEqual({ 1: "a", 2: "b" }); +}); + +test("inverts an object with string values", () => { + expect(invert({ cat: "animal", carrot: "vegetable" })).toEqual({ + animal: "cat", + vegetable: "carrot", + }); +});