diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..fbd2f3a7f 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,8 +1,8 @@ // Predict and explain first... -// This code should log out the houseNumber from the address object -// but it isn't working... -// Fix anything that isn't working +// Answer: +// The address in the code is an object not an array, +// it wont log out because we try to use the array syntax instead of the object syntax const address = { houseNumber: 42, @@ -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..31c5eb0d1 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,7 +1,7 @@ // Predict and explain first... -// 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 +// Answer: +// A plain object is not iterable, so 'for...of cannot loop over author directly const author = { firstName: "Zadie", @@ -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..f4f6f24ce 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,8 +1,7 @@ // Predict and explain first... -// 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? +// The program won't print the ingredients because it wasn't called on line 14 + const recipe = { title: "bruschetta", @@ -12,4 +11,5 @@ 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..0b4758042 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,8 @@ -function contains() {} +function contains(object, property) { + if (typeof object !== "object" || object === null || Array.isArray(object)) { + throw new Error("contains requires an object"); + } + return property in object; +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 719272787..72c19df6c 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -1,40 +1,17 @@ const contains = require("./contains.js"); -/* -Implement a function called contains that checks an object contains a -particular property +test("contains on empty object returns false", () => { + expect(contains({}, "a")).toBe(false); +}); -E.g. contains({a: 1, b: 2}, 'a') // returns true -as the object contains a key of 'a' +test("contains an object with existing property, return true", () => { + expect(contains({ a: 1, b: 2 }, "a")).toBe(true); +}); -E.g. contains({a: 1, b: 2}, 'c') // returns false -as the object doesn't contains a key of 'c' +test("contains an object with missing property, return false", () => { + expect(contains({ a: 1, b: 2 }, "c")).toBe(false); +}); -E.g. contains([1, 2, 3], 'a') throws Error("contains requires an object") -as an array isn't an object -*/ - -// 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"); - -// Given an object with properties -// When passed to contains with an existing property name -// Then it should return true - -// Given an object with properties -// When passed to contains with a non-existent property name -// Then it should return false - -// Given a value that isn't an object - an array, a string, a number, -// null, or no argument at all -// When passed to contains -// Then it should throw Error("contains requires an object") -// (careful: typeof [] and typeof null are both "object") +test("contains throws for an array", () => { + expect(() => contains([1, 2, 3], "a")).toThrow("contains requires an object"); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..cdd6788c0 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,9 @@ -function createLookup() { - // implementation here +function createLookup(countryCurrencyPairs) { + const lookup = {}; + for (const [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..342c7d398 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,35 +1,10 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); - -/* - -Create a lookup object of key value pairs from an array of code pairs - -Acceptance Criteria: - -Given - - An array of arrays representing country code and currency code pairs - e.g. [['US', 'USD'], ['CA', 'CAD']] - -When - - createLookup function is called with the country-currency array as an argument - -Then - - It should return an object where: - - The keys are the country codes - - The values are the corresponding currency codes - -Example -Given: [['US', 'USD'], ['CA', 'CAD']] - -When -createLookup(countryCurrencyPairs) is called - -Then -It should return: - { - 'US': 'USD', - 'CA': 'CAD' - } -*/ +test("creates a country currency code lookup for multiple codes", () => { + expect( + createLookup([ + ["US", "USD"], + ["CA", "CAD"], + ]) + ).toEqual({ US: "USD", CA: "CAD" }); +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..cb391d9e1 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,15 +1,26 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { - return queryParams; - } - const keyValuePairs = queryString.split("&"); - for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; - } + for (const pair of queryString.split("&")) { + if (pair === "") continue; + + const eq = pair.indexOf("="); + const key = eq === -1 ? pair : pair.slice(0, eq); + const value = eq === -1 ? "" : pair.slice(eq + 1); + const k = decodeURIComponent(key.split("+").join(" ")); + const v = decodeURIComponent(value.split("+").join(" ")); + + if (k in queryParams) { + if (Array.isArray(queryParams[k])) { + queryParams[k].push(v); + } else { + queryParams[k] = [queryParams[k], v]; + } + } else { + queryParams[k] = v; + } + } return queryParams; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 328b8df61..1ffb17a7f 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -37,12 +37,17 @@ test("should replace '+' by ' '", () => { }); }); -// Stretch exercise: Handling query strings that contain identical keys - -// Delete this test if you are not working on this optional case test("should store values of a key in an array when the key has 2 or more values", () => { expect(parseQueryString("key=value1&key=value2&key=value3&foo=bar")).toEqual({ key: ["value1", "value2", "value3"], foo: "bar", }); }); + +test("returns an empty query string", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("handles a value with both + and percent-encoding", () => { + expect(parseQueryString("q=a+b%26c")).toEqual({q: "a b&c"}); +}); \ No newline at end of file diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..380da62f5 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,14 @@ -function tally() {} +function tally(list) { + if (!Array.isArray(list)) { + throw new Error("tally requires an array"); + } + + const counts = {}; + + for (const item of list) { + counts[item] = (counts[item] || 0) + 1; + } + return counts; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index ca763c296..c3b2efd54 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -1,34 +1,17 @@ const tally = require("./tally.js"); -/** - * tally array - * - * In this task, you'll need to implement a function called tally - * that will take a list of items and count the frequency of each item - * in an array - * - * For example: - * - * tally(['a']), target output: { a: 1 } - * tally(['a', 'a', 'a']), target output: { a: 3 } - * tally(['a', 'a', 'b', 'c']), target output: { a : 2, b: 1, c: 1 } - */ -// Acceptance criteria: +test("tally on an empty array returns an empty object", () => { + expect(tally([])).toEqual({}); +}); -// 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 counts duplicate items", () => { + expect(tally(["a", "a", "a"])).toEqual({ a: 3 }); + expect(tally(["a", "a", "b", "c"])).toEqual({ a: 2, b: 1, c: 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"); - -// Given an array with duplicate items -// When passed to tally -// Then it should return counts for each unique item - -// Given an invalid input like a string, a number, or no argument at all -// When passed to tally -// Then it should throw Error("tally requires an array") +test("tally throws for invalid input", () => { + expect(() => tally("banana")).toThrow("tally requires an array"); + expect(() => tally(42)).toThrow("tally requires an array"); + expect(() => tally()).toThrow("tally requires an array"); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..62616fc6d 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,35 @@ function invert(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 } +// Answer: {key: 1} + // b) What is the current return value when invert is called with { a: 1, b: 2 } +// Answer: {key: 2} + // c) What is the target return value when invert is called with {a : 1, b: 2} +// Answer: target is {"1": "a", "2": "b"} + // c) What does Object.entries return? Why is it needed in this program? +// Answer: Object.entries returns an array of [key, value] pairs, like [["a", 1]], ["b", 2], so the loop can grab both at once + // d) Explain why the current return value is different from the target output +// Answer: invertedObj.key = value writes a property literally named "key" on - +// every pass, so each pairs overwrites the same one and only the last value survives + // e) Fix the implementation of invert (and write tests to prove it's fixed!) + +// Answer: use brackets so the value becomes the new key: invertedObj[value] = key \ No newline at end of file diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..8228996f0 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,13 @@ +const invert = require("./invert.js"); + +test("swaps key with values", () => { + expect(invert({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" }); +}); + +test("handles single entry", () => { + expect(invert({ a: 1 })).toEqual({ 1: "a" }); +}); + +test("handles an empty object", () => { + expect(invert({})).toEqual({}); +});