diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..39e846388 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,9 +1,8 @@ // Predict and explain first... - +// The code will log undefined because object properties are not accessed by index but through keys // This code should log out the houseNumber from the address object // but it isn't working... // Fix anything that isn't working - const address = { houseNumber: 42, street: "Imaginary Road", @@ -12,4 +11,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..72540739d 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,5 +1,6 @@ -// Predict and explain first... - +//Predict and explain first... +// The loop method used is for arrays and not for object literals. +// for object literals the "in" key word is used to access the keys. // 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 +12,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..1845b14e7 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,15 +1,17 @@ // Predict and explain first... - +// The program cant log the ingredient because the object literal was not reference inside the console.log() // 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"], }; -console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); +console.log(`${recipe.title} serves ${recipe.serves}`); + +for (const part of recipe.ingredients) { + console.log(part); +} diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..e87db47fb 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,13 @@ -function contains() {} +function contains(elements, itemKey) { + if (!elements) return false; + if (typeof elements !== "object" || Array.isArray(elements)) { + return false; + } + if (Object.hasOwn(elements, itemKey)) { + return true; + } + + return false; +} module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..486d359fc 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -13,23 +13,53 @@ as the object doesn't contains a key of 'c' // 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 invalid parameters like an array -// When passed to contains -// Then it should return false or throw an error +describe("contains()", () => { + // Given an object with properties + // When passed to contains with an existing property name + // Then it should return true + + [{ elements: { a: 1, b: 2 }, itemKey: "a", expected: true }].forEach( + ({ elements, itemKey, expected }) => + it(` should return ${expected} for key ${itemKey} in ${elements}`, () => { + expect(contains(elements, itemKey)).toEqual(expected); + }) + ); + + // Given an empty object + // When passed to contains + // Then it should return false + + [{ elements: {}, itemKey: "c", expected: false }].forEach( + ({ elements, itemKey, expected }) => + it(`contains on empty object returns false`, () => { + expect(contains(elements, itemKey)).toEqual(expected); + }) + ); + + // Given an object with properties + // When passed to contains with a non-existent property name + // Then it should return false + + [{ elements: { a: 1, b: 2 }, itemKey: "c", expected: false }].forEach( + ({ elements, itemKey, expected }) => + it(`When passed to contains with non-existing property name, should return false`, () => { + expect(contains(elements, itemKey)).toEqual(expected); + }) + ); + + // Given invalid parameters like an array + // When passed to contains + // Then it should return false or throw an error + + [ + { + elements: [3, 4, 5, "kofoworola", "Evan"], + itemKey: 4, + expected: false, + }, + ].forEach(({ elements, itemKey, expected }) => + it(`Given invalid parameters it should return false`, () => { + expect(contains(elements, itemKey)).toBe(expected); + }) + ); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..d306c4f93 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,14 @@ -function createLookup() { - // implementation here +function createLookup(elements) { + if (!Array.isArray(elements) || !elements.every(Array.isArray)) { + return "Invalid input"; + } + if (elements.length === 0) return "Invalid input"; + const lookup = {}; + + for (const value of elements) { + lookup[value[0]] = value[1]; + } + return lookup; } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..4889d921d 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,29 @@ const createLookup = require("./lookup.js"); -test.todo("creates a country currency code lookup for multiple codes"); +describe("createLookup", () => { + [ + { + input: [ + ["US", "USD"], + ["CA", "CAD"], + ["NJA", "NGN"], + ["RSA", "RND"], + ["UK", "GBP"], + ], + expected: { US: "USD", CA: "CAD", NJA: "NGN", RSA: "RND", UK: "GBP" }, + }, + ].forEach(({ input, expected }) => + it(`Should return an object lookup of currency representing country code as keys`, () => { + expect(createLookup(input)).toEqual(expected); + }) + ); + [{ input: ["US", "USD", "CA", "CAD"], expected: "Invalid input" }].forEach( + ({ input, expected }) => + it(`should return "Invalid input" for any input not following the input format`, () => { + expect(createLookup(input)).toEqual(expected); + }) + ); +}); /* diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..c0255faea 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -3,13 +3,22 @@ function parseQueryString(queryString) { if (queryString.length === 0) { return queryParams; } + if (typeof queryString !== "string") return "invalid input"; + const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + const index = pair.indexOf("="); + + if (index === -1) continue; + + const key = pair.slice(0, index); + const value = pair.slice(index + 1); + + if (key === "" || value.trim() === "") continue; + queryParams[key] = value; } - return queryParams; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..1f4639c16 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -3,10 +3,41 @@ // 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", + equation: "x=y+1", }); }); + +test("parses querystring values containing duplicate keys", () => { + expect(parseQueryString("a=3&a=4&a=1")).toEqual({ + a: "3", + a: "4", + a: "1", + }); +}); + +test("parses querystring values containing url encoding cases ", () => { + expect(parseQueryString("city=New%20York")).toEqual({ + city: "New%20York", + }); +}); + +test("parses querystring values containing invalid type", () => { + expect(parseQueryString({ "a=1": undefined })).toEqual("invalid input"); +}); + +test("parses querystring values containing multiple strings", () => { + expect(parseQueryString("user=alex&age=16&city=London")).toEqual({ + user: "alex", + age: "16", + city: "London", + }); +}); + +test("parses querystring values not containing =& strings in the correct order", () => { + expect(parseQueryString("key1&key2&key3")).toEqual({}); + expect(parseQueryString("key1=&key2&key3")).toEqual({}); +}); diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..6e3220013 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,20 @@ -function tally() {} +function tally(elements) { + if (!Array.isArray(elements)) { + throw new Error("Invalid input "); + } + if (elements.length === 0) { + return {}; + } + const frequency = Object.create(null); + for (let i = 0; i < elements.length; i++) { + const item = elements[i]; + if (frequency[item] === undefined) { + frequency[item] = 1; + } else { + frequency[item]++; + } + } + return frequency; +} module.exports = tally; diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..e91c8cfc9 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -15,20 +15,38 @@ const tally = require("./tally.js"); */ // Acceptance criteria: +test("should return an object containing the count for each unique item", () => { + // Given a function called tally + // When passed an array of items + // Then it should return an object containing the count for each unique item -// Given a function called tally -// When passed an array of items -// Then it should return an object containing the count for each unique item + expect(tally(["a", "b", "c", "d", 2, "edak"])).toEqual({ + a: 1, + b: 1, + c: 1, + d: 1, + 2: 1, + edak: 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 on an empty array returns an empty object", () => { + // Given an empty array + // When passed to tally + // Then it should return an empty object + expect(tally([])).toEqual({}); +}); -// Given an array with duplicate items -// When passed to tally -// Then it should return counts for each unique item +test("it should return counts for each unique item", () => { + // Given an array with duplicate items + // When passed to tally + // Then it should return counts for each unique item + 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("it should throw an error for invalid input", () => { + // Given an invalid input like a string + // When passed to tally + // Then it should throw an error + expect(() => tally("ggga")).toThrow("Invalid input"); +}); diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..6720fdf66 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,20 +10,34 @@ 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 } -// b) What is the current return value when invert is called with { a: 1, b: 2 } +//const firstCall = invert({ a: 1 }); +//firstCall={key:1} +// b) What is the current return value when invert is called with { a: 1, b: 2 } +// const secondCall = invert({ a: 1, b: 2 }); +//secondCall={key:2} // c) What is the target return value when invert is called with {a : 1, b: 2} +// target value is {1:"a",2:"b"} + // c) What does Object.entries return? Why is it needed in this program? +//Object.entries() returns an array whose elements are arrays corresponding +// to the enumerable string-keyed property key-value pairs found directly upon object. +// It is needed in this program because we want both the object keys and values // d) Explain why the current return value is different from the target output +//current about is different from the target about because ofter the loop destructuring +// the key and value is assigned the array element of the objects the property // 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..3e93e9a5a --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,14 @@ + +const invert=require('./invert') + +test("should swap the keys and values in the object", () => { + expect(invert({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" }); +}); + +test("should swap the keys and values in the object", () => { + expect(invert({ a: 1 })).toEqual({ 1: "a" }); +}); + +test("should swap the keys and values in the object", () => { + expect(invert({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" }); +}); diff --git a/Sprint-2/stretch/count-words.js b/Sprint-2/stretch/count-words.js index 8e85d19d7..fab372e18 100644 --- a/Sprint-2/stretch/count-words.js +++ b/Sprint-2/stretch/count-words.js @@ -26,3 +26,22 @@ 3. Order the results to find out which word is the most common in the input */ +function countWords(words) { + const wordArr = words.toLowerCase().split(/\s+/); + + const filteredWord = wordArr + .map((ele) => ele.replace(/[.,!?']/g, "")) + .filter(Boolean); + + const wordCount = Object.create(null); + + for (let word of filteredWord) { + if (wordCount[word] === undefined) { + wordCount[word] = 1; + } else { + wordCount[word]++; + } + } + + return wordCount; +} diff --git a/Sprint-2/stretch/mode.js b/Sprint-2/stretch/mode.js index 3f7609d79..98e71636c 100644 --- a/Sprint-2/stretch/mode.js +++ b/Sprint-2/stretch/mode.js @@ -8,22 +8,26 @@ // refactor calculateMode by splitting up the code // into smaller functions using the stages above -function calculateMode(list) { +function modeFrequency(list) { // track frequency of each value - let freqs = new Map(); - + let frequency = new Map(); for (let num of list) { if (typeof num !== "number") { continue; } - freqs.set(num, (freqs.get(num) || 0) + 1); + frequency.set(num, (frequency.get(num) || 0) + 1); } + return frequency; +} - // Find the value with the highest frequency +// Find the value with the highest frequency +function calculateMode(list) { + + let valueFrequency=modeFrequency(list); let maxFreq = 0; let mode; - for (let [num, freq] of freqs) { + for (let [num, freq] of valueFrequency) { if (freq > maxFreq) { mode = num; maxFreq = freq; diff --git a/Sprint-2/stretch/till.js b/Sprint-2/stretch/till.js index 6a08532e7..88d9f0f17 100644 --- a/Sprint-2/stretch/till.js +++ b/Sprint-2/stretch/till.js @@ -8,12 +8,12 @@ function totalTill(till) { let total = 0; for (const [coin, quantity] of Object.entries(till)) { - total += coin * quantity; + total += Number(coin.slice(0,-1)) * quantity; } return `£${total / 100}`; } - +module.exports=totalTill const till = { "1p": 10, "5p": 6, @@ -21,11 +21,12 @@ const till = { "20p": 10, }; const totalAmount = totalTill(till); - +console.log(totalAmount) // a) What is the target output when totalTill is called with the till object - +// target output should be £4.4 // b) Why do we need to use Object.entries inside the for...of loop in this function? +// object.entries convert the till object to an iterable arrays element // c) What does coin * quantity evaluate to inside the for...of loop? - +// it evaluate to the total amount for each coin till // d) Write a test for this function to check it works and then fix the implementation of totalTill diff --git a/Sprint-2/stretch/till.test.js b/Sprint-2/stretch/till.test.js new file mode 100644 index 000000000..710998e45 --- /dev/null +++ b/Sprint-2/stretch/till.test.js @@ -0,0 +1,12 @@ +const till = require("./till"); + +test("should return total till when a till object is passed", () => { + expect( + till({ + "1p": 10, + "5p": 6, + "50p": 4, + "20p": 10, + }) + ).toEqual("£4.4"); +});