diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..c59fff5f0 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,6 +1,6 @@ // Predict and explain first... -// This code should log out the houseNumber from the address object +// 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..87bc6a919 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -10,7 +10,7 @@ const author = { age: 40, alive: true, }; - -for (const value of author) { - console.log(value); +//An object is not directly iterable, if we want to log out the values we can use a for in loop +for (const value in author) { + console.log(`${author[value]}`); } diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..88fd8176a 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -11,5 +11,7 @@ const recipe = { }; console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); + ingredients:`); +for (const value of Object.values(recipe.ingredients)) { + console.log(value); +} diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..d5ce4ce4d 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,12 @@ -function contains() {} - +function contains(object, property) { + if (Array.isArray(object)) { + throw new Error("Expected an object, received an array"); + } + const obj = Object.keys(object); + if (obj.length === 0) { + return false; + } + return obj.includes(property); +} +console.log(contains({ 1: "o", 2: "k" }, 1)); module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..71b8b25d8 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -8,7 +8,7 @@ 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: @@ -17,19 +17,65 @@ as the object doesn't contains a key of 'c' // When passed an object and a property name // Then it should return true if the object contains the property, false otherwise +test( + "when passed an object and a property name, should return true if the object " + + "contains the property", + () => { + const obj1 = { + name: "dami", + age: 34, + height: 5.3, + location: "London", + }; + const propertyNameCheck1 = "name"; + const expected1 = true; + const result1 = contains(obj1, propertyNameCheck1); + expect(result1).toBe(expected1); + } +); + // Given an empty object // When passed to contains // Then it should return false -test.todo("contains on empty object returns false"); +test("given an empty object, should return false", () => { + const obj2 = {}; + const propertyNameCheck2 = "a"; + const expected2 = false; + const result2 = contains(obj2, propertyNameCheck2); + expect(result2).toBe(expected2); +}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true +//this test requirement is covered on line 21 // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test( + "when passed an object and a property name, should return false if the object " + + "does not contain the property.", + () => { + const obj3 = { + title: "Things fall apart", + year: 1958, + publisher: "pan-macmillan", + }; + const propertyNameCheck3 = "location"; + const expected3 = false; + const result3 = contains(obj3, propertyNameCheck3); + expect(result3).toBe(expected3); + } +); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test("when passed a data type other than an object, throw an error", () => { + const arr = []; + const propertyNameCheck4 = "name"; + expect(() => { + contains(arr, propertyNameCheck4); + }).toThrow("Expected an object, received an array"); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..b00652a36 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,28 @@ -function createLookup() { - // implementation here -} +function createLookup(arr) { + if (!Array.isArray(arr)) { + throw new Error("invalid data type entered"); + } + if (arr.length === 0) { + throw new Error("country and currency code not entered"); + } + + const objectLookup = {}; + for (const pair of arr) { + if (!Array.isArray(pair)) { + throw new Error("Each item must be an array."); + } + if (pair.length !== 2) { + throw new Error( + "Each inner array must contain exactly two elements: a key and a value" + ); + } + const [country, currency] = pair; + if (country === currency) { + throw new Error("Country code and currency code cannot be the same"); + } + objectLookup[country] = currency; + } + return objectLookup; +} module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..92b5bbf2b 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,7 +1,5 @@ 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 @@ -33,3 +31,48 @@ It should return: 'CA': 'CAD' } */ + +test("when passed an empty array, throw an error", () => { + expect(() => { + createLookup([]); + }).toThrow("country and currency code not entered"); +}); + +test("when given an array of arrays containing two elements, returns an object of key-value pairs", () => { + const input = [ + ["US", "USD"], + ["CA", "CAD"], + ["NG", "NIG"], + ]; + const expected = { + US: "USD", + CA: "CAD", + NG: "NIG", + }; + const result = createLookup(input); + expect(result).toEqual(expected); +}); + +test.each(["hello", 123, true, null, undefined, {}])( + "throws an error when input is %p", + (input) => { + expect(() => { + createLookup(input); + }).toThrow("invalid data type entered"); + } +); + +test("when given an array containing more than 2 elements, throw error", () => { + const input = [["US", "USD", "U"]]; + expect(() => { + createLookup(input); + }).toThrow( + "Each inner array must contain exactly two elements: a key and a value" + ); +}); + +test("when given an array, if country code !=== currency code, throw an error", () => { + expect(() => { + createLookup([["US", "US"]]); + }).toThrow("Country code and currency code cannot be the same"); +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..fa69f9af4 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,10 +6,19 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); - queryParams[key] = value; + const indexOfFirst = pair.indexOf("="); + if (indexOfFirst === -1) { + queryParams[pair] = ""; + } else { + let key = decodeURIComponent( + pair.slice(0, indexOfFirst).replaceAll("+", " ") + ); + let value = decodeURIComponent( + pair.slice(indexOfFirst + 1).replaceAll("+", " ") + ); + queryParams[key] = value; + } } - return queryParams; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 328b8df61..a2f400262 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({ @@ -36,13 +36,3 @@ test("should replace '+' by ' '", () => { "full name": "John Doe", }); }); - -// 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", - }); -}); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..29b90544d 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,13 @@ -function tally() {} - +function tally(arr) { + if (!Array.isArray(arr)) { + throw new Error('Invalid data type entered"'); + } + if (arr.length === 0) { + return {}; + } + return arr.reduce((acc, cur) => { + acc[cur] = (acc[cur] || 0) + 1; + return acc; + }, {}); +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..75fe70a70 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -19,16 +19,42 @@ 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("", () => { + const input = ["hey", "hi", "hello", "hi", "word", "picnic", "book"]; + const expected = { + hey: 1, + hi: 2, + hello: 1, + word: 1, + picnic: 1, + book: 1, + }; + const result = tally(input); + expect(result).toEqual(expected); +}); // 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", () => { + const input = []; + const expected = {}; + const result = tally(input); + expect(result).toEqual(expected); +}); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item +//Test on line 22 covers this // Given an invalid input like a string // When passed to tally // Then it should throw an error +test.each([undefined, null, {}, "hello", 123, true])( + "throws when input is %p", + (input) => { + expect(() => { + tally(input).toThrow("Invalid data type entered"); + }); + } +); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..30fc6f16a 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) { - const invertedObj = {}; - - for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; + if (typeof obj !== "object" || obj === null || Array.isArray(obj)) { + throw new Error("Enter a valid object"); + } + if (Object.keys(obj).length === 0) { + return {}; } + const invertedObj = {}; + for (let [key, value] of Object.entries(obj)) { + invertedObj[value] = key; + } return invertedObj; } - +module.exports = invert; // a) What is the current return value when invert is called with { a : 1 } +// The current value is: { key: 1 } // b) What is the current return value when invert is called with { a: 1, b: 2 } +// The current value is: { key: 2 } // c) What is the target return value when invert is called with {a : 1, b: 2} +// The target return value should be: +//{ "1" : "a", "2" : "b"} // c) What does Object.entries return? Why is it needed in this program? +//Object.entries returns an array of a key-value pair. we need object.entries +// because we cannot directly loop over an object, so by using the method +// it turns it into an array we can loop over // d) Explain why the current return value is different from the target output +//The current return uses a dot notation with the variable 'key', when we use a +// dot notation with a variable it doesn't get the value of the variable but +// instead uses the literal word as the key. when we change it to a bracket notation +// it reads the variable and uses that for the key, we however need to swap the return around and return value = key. // 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..26dead034 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,60 @@ +const invertObj = require("./invert.js"); + +describe("invertObj", () => { + test("when given an object, should swap the keys and values in the object", () => { + expect(invertObj({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" }); + }); + + test("when passed an object with one key-value pair, should swap the key and value", () => { + expect(invertObj({ a: 1 })).toEqual({ 1: "a" }); + }); + + test("when passed an empty object, should return an empty object", () => { + expect(invertObj({})).toEqual({}); + }); + + test("when passed string values, should swap the keys and values", () => { + expect(invertObj({ first: "apple", second: "banana" })).toEqual({ + apple: "first", + banana: "second", + }); + }); + + test("when passed boolean values, should swap the keys and values", () => { + expect(invertObj({ yes: true, no: false })).toEqual({ + true: "yes", + false: "no", + }); + }); + + test("when passed a null value, should use 'null' as the key", () => { + expect(invertObj({ a: null })).toEqual({ + null: "a", + }); + }); + + test("when passed an undefined value, should use 'undefined' as the key", () => { + expect(invertObj({ a: undefined })).toEqual({ + undefined: "a", + }); + }); + + //error + test.each([null, undefined, [], "cat", 4, true])( + "Throws an error when input is %p", + (input) => { + expect(() => { + invertObj(input); + }).toThrow("Enter a valid object"); + } + ); + + test("when value is swapped and keys are identical", () => { + const input = { a: 1, b: 1 }; + const expected = { + 1: "b", + }; + const result = invertObj(input); + expect(result).toEqual(expected); + }); +});