From ea7155c1c70bf0531e545f54de57dbe983fae2ae Mon Sep 17 00:00:00 2001 From: Dawud Vermeulen <127528069+Dave-Vermeulen@users.noreply.github.com> Date: Sat, 9 Aug 2025 10:17:34 +0000 Subject: [PATCH 1/4] sprint 2 files only --- Sprint-2/debug/address.js | 6 ++++-- Sprint-2/debug/author.js | 7 +++---- Sprint-2/debug/recipe.js | 5 ++--- Sprint-2/implement/contains.js | 11 +++++++++-- Sprint-2/implement/lookup.js | 11 +++++++++-- Sprint-2/implement/querystring.js | 10 +++++++--- Sprint-2/implement/tally.js | 13 ++++++++++++- Sprint-2/interpret/invert.js | 26 ++++++++++++++++++-------- 8 files changed, 64 insertions(+), 25 deletions(-) diff --git a/Sprint-2/debug/address.js b/Sprint-2/debug/address.js index 940a6af83..449a8fc9a 100644 --- a/Sprint-2/debug/address.js +++ b/Sprint-2/debug/address.js @@ -1,7 +1,7 @@ // Predict and explain first... // This code should log out the houseNumber from the address object -// but it isn't working... +// but it isn't working.. // Fix anything that isn't working const address = { @@ -12,4 +12,6 @@ const address = { postcode: "XYZ 123", }; -console.log(`My house number is ${address[0]}`); +console.log(`My house number is ${address.houseNumber}`); +console.log(`My house number is ${address['houseNumber']}`); + diff --git a/Sprint-2/debug/author.js b/Sprint-2/debug/author.js index 8c2125977..7bf9d075e 100644 --- a/Sprint-2/debug/author.js +++ b/Sprint-2/debug/author.js @@ -1,5 +1,4 @@ -// Predict and explain first... - +// 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 @@ -11,6 +10,6 @@ const author = { alive: true, }; -for (const value of author) { +for (const value of Object.values(author)) { //for-of is for arrays not objects so use Object.values console.log(value); -} +} \ No newline at end of file diff --git a/Sprint-2/debug/recipe.js b/Sprint-2/debug/recipe.js index 6cbdd22cd..9f1ad62ea 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? +// How can you fix it? const recipe = { title: "bruschetta", @@ -12,4 +11,4 @@ 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..4c18f5d30 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,3 +1,10 @@ -function contains() {} +function contains(obj, property) { -module.exports = contains; + if(obj === null || obj === undefined){ + return false + } + return obj[property] !== undefined; +} + +module.exports = contains; +// work done. \ No newline at end of file diff --git a/Sprint-2/implement/lookup.js b/Sprint-2/implement/lookup.js index a6746e07f..b5311a26d 100644 --- a/Sprint-2/implement/lookup.js +++ b/Sprint-2/implement/lookup.js @@ -1,5 +1,12 @@ -function createLookup() { - // implementation here +function createLookup(pairs) { + const lookup = {}; + for (const pair of pairs){ + if(pair && pair.length === 2){ + lookup[pair[0]] = pair[1]; + } + } + return lookup; } module.exports = createLookup; +// in working order diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 45ec4e5f3..6c4f68fb2 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -1,16 +1,20 @@ function parseQueryString(queryString) { const queryParams = {}; - if (queryString.length === 0) { + if (queryString === "") { //removed the .length method return queryParams; } const keyValuePairs = queryString.split("&"); for (const pair of keyValuePairs) { - const [key, value] = pair.split("="); + //const [key, value] = pair.split("="); //this line is making kak and its so frustrating cause it assigns value ='x' (test case 1) and leaves the rest of the equation. + const parts = pair.split("="); + const key = parts[0]; + const value = parts.length > 1 ? parts.slice(1).join('=') : ''; //clunky but it works to split them and stitch it together again queryParams[key] = value; } + return queryParams; } -module.exports = parseQueryString; +module.exports = parseQueryString; \ No newline at end of file diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index f47321812..a52749997 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -1,3 +1,14 @@ -function tally() {} +function tally(items) { + if (!Array.isArray(items)){ + throw new TypeError('that aint no array bay bay'); //okay so this is the biggie smalls and its working + } + const counts = {}; + + for (const item of items){ + counts[item] = (counts[item] || 0) +1; //simple sexy core logic + } + return counts; +} module.exports = tally; +//no updates. works well \ No newline at end of file diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index bb353fb1f..d509603dc 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; + const numKey = Number(key); + + if (!isNaN(numKey) && String(numKey) === key) { + invertedObj[value] = numKey; + } else { + invertedObj[value] = key; //convert key to number if it is a number + //how do i convert it to a number if it is a number? + //if the key is a number, convert it to a number, otherwise keep it as a string + } } 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 } - +// a) What is the current return value when invert is called with { a : 1 } +// {'1':'a'} +// b) What is the current return value when invert is called with { a: 1, b: 2 } +// { '1': 'a', '2': 'b'} // 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? - +// its a method that takes a obj and returns the [key, value] pairs. // d) Explain why the current return value is different from the target output - +// cause its converted to strings, the key must be a int and the value must be a string. // e) Fix the implementation of invert (and write tests to prove it's fixed!) +// in file invert.test.js \ No newline at end of file From 13b0f1afba32cf4613dc93a21a34601b0ca1e43a Mon Sep 17 00:00:00 2001 From: Dave-Vermeulen Date: Sun, 17 Aug 2025 21:10:39 +0200 Subject: [PATCH 2/4] responding to comments on PR --- Sprint-2/implement/contains.js | 2 +- Sprint-2/implement/contains.test.js | 109 ++++++++++++++------- Sprint-2/implement/lookup.test.js | 125 ++++++++++++++++++------- Sprint-2/implement/querystring.js | 4 +- Sprint-2/implement/querystring.test.js | 31 +++++- Sprint-2/implement/tally.js | 2 +- Sprint-2/implement/tally.test.js | 42 ++++++++- 7 files changed, 239 insertions(+), 76 deletions(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index 4c18f5d30..63676e781 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -3,7 +3,7 @@ function contains(obj, property) { if(obj === null || obj === undefined){ return false } - return obj[property] !== undefined; + return Object.hasOwn(obj, property); } module.exports = contains; diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index 326bdb1f2..e064758e2 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -1,35 +1,74 @@ -const contains = require("./contains.js"); - -/* -Implement a function called contains that checks an object contains a -particular property - -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' -*/ - -// 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 +const contains = require('./contains'); + +describe('contains', () => { + it('should return false for an empty object', () => { + expect(contains({}, 'someProperty')).toBe(false); + }); + + it('should return true if the object contains the property', () => { + expect(contains({ a: 1 }, 'a')).toBe(true); + expect(contains({ name: 'John', age: 30 }, 'name')).toBe(true); + expect(contains({ "1": "one"}, "1")).toBe(true) //number as string property name + }); + + it('should return false if the object does not contain the property', () => { + expect(contains({ a: 1 }, 'b')).toBe(false); + expect(contains({ name: 'John' }, 'age')).toBe(false); + }); + + it('should handle null and undefined objects gracefully', () => { + expect(contains(null, 'a')).toBe(false); + expect(contains(undefined, 'a')).toBe(false); + }); + + it('should handle empty string property names', () => { + expect(contains({"": 1}, "")).toBe(true); + expect(contains({a:1}, "")).toBe(false); + }); + + it('should handle non-object inputs gracefully', () => { + expect(contains(123, 'a')).toBe(false); // Number + expect(contains("hello", 'a')).toBe(false); // String + expect(contains([1, 2, 3], 'a')).toBe(false); // Array + expect(contains(true, 'a')).toBe(false) + expect(contains(NaN, 'a')).toBe(false) + }); +}); +/* +Implement a function called contains that checks an object contains a +particular property + +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' +*/ + +// 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 +test("returns true for existing property", () => { + expect(contains({ d: 9, v: 11}, "v")).toBe(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 + +//enough is enough! + + diff --git a/Sprint-2/implement/lookup.test.js b/Sprint-2/implement/lookup.test.js index 547e06c5a..7e0706746 100644 --- a/Sprint-2/implement/lookup.test.js +++ b/Sprint-2/implement/lookup.test.js @@ -1,35 +1,90 @@ -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' - } -*/ +const createLookup = require("./lookup.js"); + +//test.todo("creates a country currency code lookup for multiple codes"); +//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' + } +*/ +//tbh this was hard to understand and its taken me a while to get it. + + +describe('createLookup', () => { + it('should return an empty object for an empty input array', () => { + expect(createLookup([])).toEqual({}); + }); //the first test passes. could not resist writing the function finished (i think). make more tests to check + + it('should create a lookup object from valid country-currency pairs', () => { + const pairs = [['US', 'USD'], ['CA', 'CAD'], ['GB', 'GBP']]; + const expectedLookup = { + US: 'USD', + CA: 'CAD', + GB: 'GBP', + }; + expect(createLookup(pairs)).toEqual(expectedLookup); + }); + + it('should handle duplicate country codes by using the last occurrence', () => { + const pairs = [['US', 'USD'], ['CA', 'CAD'], ['US', 'PESO']]; + const expectedLookup = { + US: 'PESO', + CA: 'CAD', + }; + expect(createLookup(pairs)).toEqual(expectedLookup); + }); + + it('should handle empty strings as country or currency codes', () => { + const pairs = [['', 'USD'], ['CA', '']]; + const expectedLookup = { + "": 'USD', + CA: '', + }; + expect(createLookup(pairs)).toEqual(expectedLookup); + }); + + it('should handle non-string values gracefully', () => { + const pairs = [[1, 123], [true, false]]; + const expectedLookup = { + 1: 123, + true: false + }; + expect(createLookup(pairs)).toEqual(expectedLookup); + }); + + it('should handle an array of only one pair', () => { + const pairs = [["ZA", "RAND"]] + const expectedLookup = { + ZA: "RAND" + } + expect(createLookup(pairs)).toEqual(expectedLookup) + }) //Randelas + +}); diff --git a/Sprint-2/implement/querystring.js b/Sprint-2/implement/querystring.js index 6c4f68fb2..2ce03a612 100644 --- a/Sprint-2/implement/querystring.js +++ b/Sprint-2/implement/querystring.js @@ -8,8 +8,8 @@ function parseQueryString(queryString) { for (const pair of keyValuePairs) { //const [key, value] = pair.split("="); //this line is making kak and its so frustrating cause it assigns value ='x' (test case 1) and leaves the rest of the equation. const parts = pair.split("="); - const key = parts[0]; - const value = parts.length > 1 ? parts.slice(1).join('=') : ''; //clunky but it works to split them and stitch it together again + const key = decodeURIComponent(parts[0]); + const value = parts.length > 1 ? decodeURIComponent(parts.slice(1).join('=')) : ''; //clunky but it works to split them and stitch it together again queryParams[key] = value; } diff --git a/Sprint-2/implement/querystring.test.js b/Sprint-2/implement/querystring.test.js index 3e218b789..55f12ac51 100644 --- a/Sprint-2/implement/querystring.test.js +++ b/Sprint-2/implement/querystring.test.js @@ -8,5 +8,34 @@ const parseQueryString = require("./querystring.js") test("parses querystring values containing =", () => { expect(parseQueryString("equation=x=y+1")).toEqual({ "equation": "x=y+1", - }); + }); // passes now that we changed the const [key, value] = pair.split("="); }); + it('parses a single key-value pair', () => { + expect(parseQueryString('name=John')).toEqual({ name: 'John' }); + }); + + it('parses multiple key-value pairs', () => { + expect(parseQueryString('name=John&age=30&city=NewYork')).toEqual({ + name: 'John', + age: '30', + city: 'NewYork', + }); + }); + + it('handles empty querystrings', () => { + expect(parseQueryString('')).toEqual({}); + }); //empty test + + it('handles querystrings with only a key (no value)', () => { + expect(parseQueryString('key')).toEqual({ key: '' }); + }); //just-the-tip test + + it('handles querystrings with empty values', () => { + expect(parseQueryString('name=&age=')).toEqual({name:"", age:""}) + }) //tough 1 + + // it('handles querystrings with encoded characters', () => { + // expect(parseQueryString('name=John%20Doe&age=30%2B')).toEqual({name:"John Doe", age:"30+"}) + // }) //this continues to fail + + //up for some work but 1st iteration still works. diff --git a/Sprint-2/implement/tally.js b/Sprint-2/implement/tally.js index a52749997..c018f983f 100644 --- a/Sprint-2/implement/tally.js +++ b/Sprint-2/implement/tally.js @@ -2,7 +2,7 @@ function tally(items) { if (!Array.isArray(items)){ throw new TypeError('that aint no array bay bay'); //okay so this is the biggie smalls and its working } - const counts = {}; + const counts = Object.create(null); for (const item of items){ counts[item] = (counts[item] || 0) +1; //simple sexy core logic diff --git a/Sprint-2/implement/tally.test.js b/Sprint-2/implement/tally.test.js index 2ceffa8dd..e977c2284 100644 --- a/Sprint-2/implement/tally.test.js +++ b/Sprint-2/implement/tally.test.js @@ -23,7 +23,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"); +//test.todo("tally on an empty array returns an empty object"); // Given an array with duplicate items // When passed to tally @@ -32,3 +32,43 @@ test.todo("tally on an empty array returns an empty object"); // Given an invalid input like a string // When passed to tally // Then it should throw an error +describe('tally', () => { + it('should return an empty object for an empty array', () => { + expect(tally([])).toEqual({}); + }); + + it('should count a single item correctly', () => { + expect(tally(['a'])).toEqual({ a: 1 }); + }); + + it('should count multiple occurrences of the same item', () => { + expect(tally(['a', 'a', 'a'])).toEqual({ a: 3 }); + }); + + it('should count multiple unique items correctly', () => { + expect(tally(['a', 'a', 'b', 'c'])).toEqual({ a: 2, b: 1, c: 1 }); + }); + + it('should handle different data types (strings, numbers, booleans)', () => { + expect(tally(['apple', 1, 'banana', 1, true, 'apple'])).toEqual({ apple: 2, 1: 2, banana: 1, true: 1 }); + }); + + it('should handle null and undefined array elements', () => { + expect(tally([null, undefined, null, 'a'])).toEqual({ null: 2, undefined: 1, a: 1 }); + }); + + it('should handle empty string array elements', () => { + expect(tally(["","","test"])).toEqual({"":2, "test":1}) + }) //here are all the test cases. its just basic + + it('should throw an error for non-array input', () => { + expect(() => tally("not an array")).toThrow(TypeError); + expect(() => tally(123)).toThrow(TypeError); + expect(() => tally(null)).toThrow(TypeError); + expect(() => tally(undefined)).toThrow(TypeError); + expect(() => tally({})).toThrow(TypeError); //this took research. we have not thrown errors before. + }); + + //run tests then build the function + // test run and pass +}); From 3da435829e40e28c19c1640bff4a8651fe9e3ea3 Mon Sep 17 00:00:00 2001 From: Dave-Vermeulen Date: Mon, 18 Aug 2025 10:31:40 +0200 Subject: [PATCH 3/4] handle the remaining reviewers comments --- Sprint-2/interpret/invert.js | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/Sprint-2/interpret/invert.js b/Sprint-2/interpret/invert.js index d509603dc..b350d3a05 100644 --- a/Sprint-2/interpret/invert.js +++ b/Sprint-2/interpret/invert.js @@ -10,27 +10,22 @@ function invert(obj) { const invertedObj = {}; for (const [key, value] of Object.entries(obj)) { - const numKey = Number(key); - - if (!isNaN(numKey) && String(numKey) === key) { - invertedObj[value] = numKey; - } else { - invertedObj[value] = key; //convert key to number if it is a number - //how do i convert it to a number if it is a number? - //if the key is a number, convert it to a number, otherwise keep it as a string - } - } + + for (const [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 } -// {'1':'a'} +// {key: '1'} (based on the original) // b) What is the current return value when invert is called with { a: 1, b: 2 } -// { '1': 'a', '2': 'b'} +// For { a: 1 }, it sets invertedObj.key to 1. For { b: 2 }, it overwrites the same property, setting invertedObj.key to 2. output { key: 2 } // c) What is the target return value when invert is called with {a : 1, b: 2} -// {1 : a, 2 : b } +// {'1' : 'a', '2' : 'b' } // c) What does Object.entries return? Why is it needed in this program? // its a method that takes a obj and returns the [key, value] pairs. // d) Explain why the current return value is different from the target output From 2037bb628238bcd839c6487748e276e379684155 Mon Sep 17 00:00:00 2001 From: Dave-Vermeulen Date: Mon, 18 Aug 2025 10:48:26 +0200 Subject: [PATCH 4/4] updated contains function to accommodate reviewers comments --- Sprint-2/implement/contains.js | 2 +- Sprint-2/implement/contains.test.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Sprint-2/implement/contains.js b/Sprint-2/implement/contains.js index 63676e781..735a8b454 100644 --- a/Sprint-2/implement/contains.js +++ b/Sprint-2/implement/contains.js @@ -1,6 +1,6 @@ function contains(obj, property) { - if(obj === null || obj === undefined){ + if(obj === null || typeof obj !== 'object' || Array.isArray(obj)) { return false } return Object.hasOwn(obj, property); diff --git a/Sprint-2/implement/contains.test.js b/Sprint-2/implement/contains.test.js index e064758e2..68bc73223 100644 --- a/Sprint-2/implement/contains.test.js +++ b/Sprint-2/implement/contains.test.js @@ -29,6 +29,7 @@ describe('contains', () => { it('should handle non-object inputs gracefully', () => { expect(contains(123, 'a')).toBe(false); // Number expect(contains("hello", 'a')).toBe(false); // String + expect(contains([1, 2, 3], '1')).toBe(false); // Array numeric string expect(contains([1, 2, 3], 'a')).toBe(false); // Array expect(contains(true, 'a')).toBe(false) expect(contains(NaN, 'a')).toBe(false)