diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..4ddf31650 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,4 +1,8 @@ // Predict and explain first... +// My prediction: the code will show an Error or "undefined", the Object is fine but +// called in a wrong way in the last line. we cant use object[number] to get a value +// withing an Object, that works only with arrays. Instead, we need to write +// the name of the key that we need. // This code should log out the houseNumber from the address object // but it isn't working... @@ -12,4 +16,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..419c6b040 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,4 +1,6 @@ // Predict and explain first... +// Prediction: the code will throw an error +// Explanation: An object can not be used or accessed as an array. // 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 +13,6 @@ const author = { alive: true, }; -for (const value of author) { - console.log(value); +for ( const key in author ) { +console.log(author[key]); } diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..036a2def4 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,15 +1,17 @@ -// Predict and explain first... + // Predict and explain first... + // to separate the ingredients in different lines we need to use the function: join() + // the ingredients is an array not an object, so we can use join() and pass "\n" to put + // new line between every item. -// 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? + // 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? -const recipe = { - title: "bruschetta", - serves: 2, - ingredients: ["olive oil", "tomatoes", "salt", "pepper"], -}; + const recipe = { + title: "bruschetta", + serves: 2, + ingredients: ["olive oil", "tomatoes", "salt", "pepper"], + }; -console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); + console.log(`Title: ${recipe.title} || serves: ${recipe.serves} +ingredients: \n ${recipe.ingredients.join("\n ")}`); diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..a4dc924eb 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,9 @@ -function contains() {} +function contains(obj, find) { + + if(typeof obj !== 'object' || !obj || Array.isArray(obj)) { + return false; + } + return obj.hasOwnProperty(find); +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..b1c430654 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -2,7 +2,7 @@ const contains = require("./contains.js"); /* Implement a function called contains that checks an object contains a -particular property +particular property E.g. contains({a: 1, b: 2}, 'a') // returns true as the object contains a key of 'a' @@ -16,20 +16,47 @@ as the object doesn't contains a key of 'c' // 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 +test.todo("contains on empty object returns false"); +test("When the object contains the property return true, false otherwise", () => { + expect(contains({a: 1, b: 2, c: 3}, "a")).toBe(true); + expect(contains({a: 1, b: 2, c: 3}, "d")).toBe(false); +}) // Given an empty object // When passed to contains // Then it should return false test.todo("contains on empty object returns false"); +test("if contains on empty object returns false", () => { + expect(contains({}, "a")).toBe(false); + expect(contains({}, "Hello")).toBe(false); +}); // Given an object with properties // When passed to contains with an existing property name // Then it should return true +test.todo("if contains on object with existing property returns true"); +test("if contains on object with existing property returns true", () => { + expect(contains({m: 10, s: 20, w: 5}, "s")).toBe(true); + expect(contains({x: 100, y: 200, z: 300}, "z")).toBe(true); + expect(contains({name: "Ahmed", age: 30}, "name")).toBe(true); +}); // Given an object with properties // When passed to contains with a non-existent property name // Then it should return false +test.todo("if contains on object with non-existent property returns false"); +test("if contains on object with non-existent property returns false", () => { + expect(contains({m: 10, s: 20, w: 5}, "x")).toBe(false); + expect(contains({num1: 100, num2: 200, num3: 300}, "num5")).toBe(false); + expect(contains({name: "Ahmed", age: 30}, "address")).toBe(false); +}); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +test.todo("if contains with invalid parameters returns false or throws an error"); +test("if contains with invalid parameters returns false or throws an error", () => { + expect(contains(['a','b','c'], "1")).toBe(false); + expect(contains("string", "a")).toBe(false); + expect(contains(12345, "a")).toBe(false); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..609b1acc9 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,11 @@ -function createLookup() { - // implementation here -} +function createLookup(arrayOfPairs) { + let obj = {}; + + for (let i = 0; i < arrayOfPairs.length; i++) { + const [key, value] = arrayOfPairs[i]; + obj[key] = value; + } + return obj; +}; module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..73bf81150 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,14 @@ const createLookup = require("./lookup.js"); test.todo("creates a country currency code lookup for multiple codes"); +test("creates a country currency code lookup for multiple codes", () => { + expect(createLookup([['US', 'USD'], ['CA', 'CAD'], ['GB', 'GBP'], ['JP', 'JPY']])).toEqual({ + 'US': 'USD', + 'CA': 'CAD', + 'GB': 'GBP', + 'JP': 'JPY' + }); +}); /* @@ -21,7 +29,7 @@ Then - The values are the corresponding currency codes Example -Given: [['US', 'USD'], ['CA', 'CAD']] +Given: [['US', 'USD'], ['CA', 'CAD']] When createLookup(countryCurrencyPairs) is called diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..2d0e478a7 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,16 +1,25 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { - return queryParams; - } - const keyValuePairs = queryString.split("&"); + if (!queryString) return queryParams; + + const pairs = queryString.split("&"); + + for (const pair of pairs) { + const eqIndex = pair.indexOf("="); + + let key, value; + if (eqIndex === -1) { + key = decodeURIComponent(pair); + value = ""; + } else { + key = decodeURIComponent(pair.slice(0, eqIndex)); + value = decodeURIComponent(pair.slice(eqIndex + 1)); + } - for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); queryParams[key] = value; } return queryParams; } -module.exports = parseQueryString; +module.exports = parseQueryString; \ No newline at end of file diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..c8710fd0d 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,38 @@ // 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 querystring values containing =", () => { expect(parseQueryString("equation=x=y+1")).toEqual({ "equation": "x=y+1", }); }); + +test("parses empty querystring", () => { + expect(parseQueryString("")).toEqual({}); +}); + +test("parses single key-value pair", () => { + expect(parseQueryString("name=John")).toEqual({ + "name": "John", + }); +}); + +test("parses key with empty value", () => { + expect(parseQueryString("key=")).toEqual({ + "key": "", + }); +}); + +test("parses empty key with value", () => { + expect(parseQueryString("=value")).toEqual({ + "": "value", + }); +}); + +test("parses key without '=' separator", () => { + expect(parseQueryString("key")).toEqual({ + "key": "", + }); +}); \ No newline at end of file diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..56967e691 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,16 @@ -function tally() {} +function tally(arr) { + let obj = Object.create(null); + if (!Array.isArray(arr)) { + throw new Error("Input must be an array"); + } -module.exports = tally; + for (let i = 0; i < arr.length; i++) { + if (!obj[arr[i]]) + obj[arr[i]] = 1; + else + obj[arr[i]]++; + } + return obj; +} + +module.exports = tally; \ No newline at end of file diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..3f6f6dda4 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -19,16 +19,38 @@ 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.todo("when passed an array of items, it returns an object with counts for each unique item"); +test("when passed an array of items, it returns an object with counts for each unique item", () => { + const input = ['a', 'a', 'b', 'c']; + const expectedOutput = { a: 2, b: 1, c: 1 }; + expect(tally(input)).toEqual(expectedOutput); +}); // 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 expectedOutput = {}; + expect(tally(input)).toEqual(expectedOutput); +}); // Given an array with duplicate items // When passed to tally // Then it should return counts for each unique item +test.todo("tally counts each unique item in an array with duplicates"); +test("tally counts each unique item in an array with duplicates", () => { + const input = ['x', 'y', 'x', 'z', 'y', 'x']; + const expectedOutput = { x: 3, y: 2, z: 1 }; + expect(tally(input)).toEqual(expectedOutput); +}); // Given an invalid input like a string // When passed to tally // Then it should throw an error +test.todo("tally throws an error when passed invalid input"); +test("tally throws an error when passed invalid input", () => { + const input = "invalid input"; + expect(() => tally(input)).toThrow("Input must be an array"); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..1513034a7 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,30 @@ 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 } +// 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 } // 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? +// it returns an array of a given object in [key, value] pairs. // d) Explain why the current return value is different from the target output +// because in the loop, it always assigns the property 'key' to the value, +// so the final result only contains one property 'key' with the last value assigned in the loop. // e) Fix the implementation of invert (and write tests to prove it's fixed!) +// Code fixed. + +console.log(invert({ a: 1 })); // { '1': 'a' } +module.exports = invert; diff --git a/Sprint-2/interpret/invert.test.js b/Sprint-2/interpret/invert.test.js new file mode 100644 index 000000000..c056cf6ad --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,9 @@ +test.todo("check if invert function swaps keys and values"); +test("check if invert function swaps keys and values", () => { + const invert = require("./invert"); + + expect(invert({ a: 1 })).toEqual({ '1': 'a' }); + expect(invert({ a: 1, b: 2 })).toEqual({ '1': 'a', '2': 'b' }); + expect(invert({ x: 10, y: 20 })).toEqual({ '10': 'x', '20': 'y' }); + expect(invert({})).toEqual({}); +}); \ No newline at end of file diff --git a/Sprint-2/stretch/count-words.js b/Sprint-2/stretch/count-words.js index 8e85d19d7..4e8294866 100644 --- a/Sprint-2/stretch/count-words.js +++ b/Sprint-2/stretch/count-words.js @@ -26,3 +26,25 @@ 3. Order the results to find out which word is the most common in the input */ + +function countWords(string) { + let outputObject = Object.create(null); + let splitString = string.split(" "); + + for ( let i = 0; i < splitString.length; i++) { + let word = splitString[i].toLowerCase(); + word = word.replace(/[.,!?]/g, ''); + + if (word.length != 0) { + if (!outputObject[word]) + outputObject[word] = 1; + else + outputObject[word]++; + } + } + + return outputObject; +} +console.log(countWords("Hello,World! Hello World!")); +console.log(countWords("constructor constructor")); +console.log(countWords(" Hello World ")); \ No newline at end of file