diff --git a/Sprint-1/fix/median.js b/Sprint-1/fix/median.js index b22590bc6..41a5e9c43 100644 --- a/Sprint-1/fix/median.js +++ b/Sprint-1/fix/median.js @@ -1,4 +1,4 @@ -// Fix this implementation + // Start by running the tests for this function // If you're in the Sprint-1 directory, you can run `npm test -- fix` to run the tests in the fix directory @@ -7,8 +7,4 @@ function calculateMedian(list) { const middleIndex = Math.floor(list.length / 2); - const median = list.splice(middleIndex, 1)[0]; - return median; -} - -module.exports = calculateMedian; + const median = list.splice(middleIndex, 1)[0]; \ No newline at end of file diff --git a/Sprint-1/fix/median.test.js b/Sprint-1/fix/median.test.js index 21da654d7..d1fb2d6fa 100644 --- a/Sprint-1/fix/median.test.js +++ b/Sprint-1/fix/median.test.js @@ -1,9 +1,3 @@ -// median.test.js - -// Someone has implemented calculateMedian but it isn't -// passing all the tests... -// Fix the implementation of calculateMedian so it passes all tests - const calculateMedian = require("./median.js"); describe("calculateMedian", () => { @@ -13,7 +7,8 @@ describe("calculateMedian", () => { { input: [1, 2, 3, 4], expected: 2.5 }, { input: [1, 2, 3, 4, 5, 6], expected: 3.5 }, ].forEach(({ input, expected }) => - it(`returns the median for [${input}]`, () => expect(calculateMedian(input)).toEqual(expected)) + it(`returns the median for [${input}]`, () => + expect(calculateMedian(input)).toEqual(expected)) ); [ @@ -21,20 +16,29 @@ describe("calculateMedian", () => { { input: [5, 1, 3, 4, 2], expected: 3 }, { input: [4, 2, 1, 3], expected: 2.5 }, { input: [6, 1, 5, 3, 2, 4], expected: 3.5 }, - { input: [110, 20, 0], expected: 20 }, - { input: [6, -2, 2, 12, 14], expected: 6 }, ].forEach(({ input, expected }) => - it(`returns the correct median for unsorted array [${input}]`, () => expect(calculateMedian(input)).toEqual(expected)) + it(`returns the correct median for unsorted array [${input}]`, () => + expect(calculateMedian(input)).toEqual(expected)) ); - it("doesn't modify the input array [3, 1, 2]", () => { - const list = [3, 1, 2]; + it("doesn't modify the input array [1, 2, 3]", () => { + const list = [1, 2, 3]; calculateMedian(list); - expect(list).toEqual([3, 1, 2]); + + expect(list).toEqual([1, 2, 3]); }); - [ 'not an array', 123, null, undefined, {}, [], ["apple", null, undefined] ].forEach(val => - it(`returns null for non-numeric array (${val})`, () => expect(calculateMedian(val)).toBe(null)) + [ + "not an array", + 123, + null, + undefined, + {}, + [], + ["apple", null, undefined], + ].forEach((val) => + it(`returns null for non-numeric array (${val})`, () => + expect(calculateMedian(val)).toBe(null)) ); [ @@ -45,6 +49,7 @@ describe("calculateMedian", () => { { input: [3, "apple", 1, null, 2, undefined, 4], expected: 2.5 }, { input: ["banana", 5, 3, "apple", 1, 4, 2], expected: 3 }, ].forEach(({ input, expected }) => - it(`filters out non-numeric values and calculates the median for [${input}]`, () => expect(calculateMedian(input)).toEqual(expected)) + it(`filters out non-numeric values and calculates the median for [${input}]`, () => + expect(calculateMedian(input)).toEqual(expected)) ); }); diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..a40612037 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,5 +1,5 @@ // Predict and explain first... - +// address is object so you can not reach the values by index you can use address.houseNumber or address[houseNumber] // 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..cd5b676b4 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,5 +1,5 @@ // Predict and explain first... - +//objects are not iterable in JavaScript. // 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 +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..a0b6b3777 100644 --- a/Sprint-2/debug/recipe.js +++ b/Sprint-2/debug/recipe.js @@ -1,4 +1,5 @@ // Predict and explain first... +//${recipe} will not give us the ingredients // This program should log out the title, how many it serves and the ingredients. // Each ingredient should be logged on a new line @@ -11,5 +12,7 @@ const recipe = { }; console.log(`${recipe.title} serves ${recipe.serves} - ingredients: -${recipe}`); + ingredients:`); +for (const ingredient of recipe.ingredients) { + console.log(ingredient); +} diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index cd779308a..e822b5ce9 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,29 @@ -function contains() {} +function isObject(value) { + return typeof value === "object" && value !== null && !Array.isArray(value); +} +function contains(obj, prop) { + if(!isObject(obj)) throw new Error ("Invalid input"); + + // return Object.keys(obj).includes(prop); + return Object.hasOwn(obj, prop); +} + + console.log( contains({ a: 1, b: 2 }, 'toString')); module.exports = contains; + + +//Does the following function call returns what you expect? + + // contains({a: 1, b: 2}, 'toString'); +// returns false for inherited properties + + +//How to determine if an object's property is its own property or an inherited property in JavaScript? +// by using hasown method. + +//Does your test correctly check whether the function can detect the given object is an array? +//Yes, the test correctly checks that the function can detect when the input is an array. +//it("throws an error when the first parameter is not an object",()=>{ + // expect(()=> contains([],"a")).toThrow("Invalid input") + // }); \ No newline at end of file diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..78c5528b9 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -20,7 +20,6 @@ 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"); // Given an object with properties // When passed to contains with an existing property name @@ -33,3 +32,31 @@ test.todo("contains on empty object returns false"); // Given invalid parameters like an array // When passed to contains // Then it should return false or throw an error +describe("contains", () => { + it("throws an error when the first parameter is not an object",()=>{ + expect(()=> contains([],"a")).toThrow("Invalid input") + }); + + it("contains an empty object returns false", () => { + expect(contains({}, "a")).toBe(false); + }); + + [ + { input: { a: 1, b: 2 }, prop: "a", expected: true }, + { input: { a: 1, b: 2 }, prop: "b", expected: true }, + { input: { a: 1, b: 2, c: 3 }, prop: "c", expected: true }, + ].forEach(({ input, prop, expected }) => { + it(`returns true when the property exists in the given object , for object ${JSON.stringify(input)} with property "${prop}"`, () => + expect(contains(input, prop)).toBe(expected)); + }); + + + [ + { input: { a: 1, b: 2 }, prop: "c", expected: false }, + { input: { a: 1, b: 2 }, prop: "e", expected: false }, + { input: { a: 1, b: 2, c: 3 }, prop: "w", expected: false }, + ].forEach(({ input, prop, expected }) => { + it(`returns false when the property non-existent in the given object , for object ${JSON.stringify(input)} with property "${prop}"`, () => + expect(contains(input, prop)).toBe(expected)); + }); +}); diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..ef14def58 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,10 @@ -function createLookup() { - // implementation here +function createLookup(myArray) { + if (!Array.isArray(myArray)) return "Invalid input"; + const myObject = {}; + for (let i = 0; i < myArray.length; i++) { + myObject[myArray[i][0]] = myArray[i][1]; + } + return myObject; } module.exports = createLookup; diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..3bad8c29c 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,6 +1,33 @@ const createLookup = require("./lookup.js"); - -test.todo("creates a country currency code lookup for multiple codes"); +describe("createLookup", () => { + it("creates an object from country/currency pairs", () => { + expect( + createLookup([ + ["US", "USD"], + ["CA", "CAD"], + ]) + ).toEqual({ US: "USD", CA: "CAD" }); + }); + + it("returns an empty object for an empty array", () => { + expect(createLookup([])).toEqual({}); + }); + + it("returns 'Invalid input' for non-array inputs", () => { + expect(createLookup("CYF")).toBe("Invalid input"); + expect(createLookup(123)).toBe("Invalid input"); + expect(createLookup(null)).toBe("Invalid input"); + }); + + it("overwrites repeated keys with the last value", () => { + expect( + createLookup([ + ["US", "USDxx"], + ["US", "USD"], + ]) + ).toEqual({ US: "USD" }); + }); +}); /* diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..4347e6d4a 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -6,11 +6,16 @@ function parseQueryString(queryString) { const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + const [keyBeforeDecoding,...rest] = pair.split("="); + const valueBeforeDecoding=rest.join("="); + const key=decodeURIComponent(keyBeforeDecoding); + const value=decodeURIComponent(valueBeforeDecoding); + queryParams[key] = value; } return queryParams; } +console.log(parseQueryString("tags%5B%5D=hello%20world ")); module.exports = parseQueryString; diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..97a2a5a7e 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -5,8 +5,24 @@ const parseQueryString = require("./querystring.js") +test("returns empty object for empty querystring", () => { + expect(parseQueryString("")).toEqual({}); +}); test("parses querystring values containing =", () => { expect(parseQueryString("equation=x=y+1")).toEqual({ "equation": "x=y+1", }); }); +test("handles key with no value", () => { + expect(parseQueryString("flag")).toEqual({ + flag: "", + }); +}); +test("parses multiple pairs when one value contains '='", () => { + expect(parseQueryString("user=ahmad&equation=x=y+1&city=Manchester")).toEqual({ + user: "ahmad", equation: "x=y+1", city: "Manchester", + }); +}); +test("Handling URL-encoded query string",()=>{ +expect(parseQueryString("tags%5B%5D=hello%20world")).toEqual({"tags[]":"hello world"}) +}) \ No newline at end of file diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..a306879ca 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,12 @@ -function tally() {} +function tally(myArray) { + if (!Array.isArray(myArray)) throw new Error("Invalid input"); + if (myArray.length === 0) return {}; + const myObject =Object.create(null); + for (const item of myArray) { + myObject[item] = (myObject[item] || 0) + 1; + } + return myObject; +} module.exports = tally; + console.log(tally(["toString", "toString"])); \ No newline at end of file diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..ebe72898d 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -14,6 +14,21 @@ const tally = require("./tally.js"); * tally(['a', 'a', 'b', 'c']), target output: { a : 2, b: 1, c: 1 } */ +describe("tally",()=>{ + + it("returns an object containing the count for each unique item when given array of function",()=>{ + expect(tally(['a'])).toEqual({a:1}), + expect(tally(['a','a','a'])).toEqual({a:3}), + expect(tally(['a','a','b','c'])).toEqual({a:2, b:1, c:1}) + }) + + it("returns an empty object for an empty array", () => { + expect(tally([])).toEqual({}); + }); + it("throw an error for invalid input",()=>{ + expect(()=>tally("CYF")).toThrow("Invalid input") + }) +}) // Acceptance criteria: // Given a function called tally @@ -23,7 +38,7 @@ const tally = require("./tally.js"); // 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 diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..5e7b229b5 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -6,24 +6,42 @@ // E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"} -function invert(obj) { - const invertedObj = {}; +// function invert(obj) { +// const invertedObj = {}; - for (const [key, value] of Object.entries(obj)) { - invertedObj.key = value; - } +// for (const [key, value] of Object.entries(obj)) { +// invertedObj.key = value; +// } - return invertedObj; -} +// return invertedObj; +// } +// console.log(invert({a:1, b:2})) // a) What is the current return value when invert is called with { a : 1 } +//{key:1} // b) What is the current return value when invert is called with { a: 1, b: 2 } +//{key: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? +//It turns an object into an array of [key, value] pairs. We need it so we can access the key and value for each pair in the loop. // d) Explain why the current return value is different from the target output +// because we use dot notation which sets a property called key that mean we are not using the variable key so it will overwrites the same "key" property each time with a new value // e) Fix the implementation of invert (and write tests to prove it's fixed!) + +function invert(obj) { + const invertedObj = {}; + + for (const [key, value] of Object.entries(obj)) { + invertedObj[value] = key; + } + + return invertedObj; +} + +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..a27d49db0 --- /dev/null +++ b/Sprint-2/interpret/invert.test.js @@ -0,0 +1,9 @@ +const invert = require("./invert.js"); + +test("inverts a two-key object", () => { + expect(invert({ a: 1, b: 2 })).toEqual({ 1: "a", 2: "b" }); +}); + +test("inverts a single-key object", () => { + expect(invert({ a: 1 })).toEqual({ 1: "a" }); +});