-
-
Notifications
You must be signed in to change notification settings - Fork 283
Cape Town | 2026-ITP-Jan | Isaac Abodunrin | Sprint 2 | Grouping data: Objects #1143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 57 commits
3d7370b
fdbd966
a28e904
06339fc
4426ed9
4b1ac0d
b6c0807
aa0f82d
cc952dd
8596f7f
ebbad7e
0940103
1c72bab
88d95b1
c0f7bb1
cb55db5
81f9946
1c5c121
b22a517
7689546
ceebce1
b688460
fdc221d
c68b85c
49e36bb
7ad5748
34d7c95
47113d8
8aefebd
ff9eac0
2743b71
672d459
e6cde3f
c837ff1
273dc37
eb1dee9
340015c
420bc1f
8919eac
8d56810
bf09c40
7e6ad11
21df2c2
50e80fd
a9a06c3
774cce1
9873f85
cd4b7b5
0c144f3
a62b0eb
35e98cc
a2eb57f
2921a9e
7172685
7c8f06d
a005979
e5aee85
b212457
f6e13a9
b058443
c685c0b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,11 @@ | ||
| function contains() {} | ||
| function contains(object, property) { | ||
| if (typeof object !== "object" || object === null || Array.isArray(object)) { | ||
| throw new TypeError( | ||
| "First argument must be an object in the form { key: value }" | ||
| ); | ||
| } | ||
|
|
||
| return object.hasOwnProperty(property); | ||
| } | ||
|
|
||
| module.exports = contains; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,35 +1,70 @@ | ||
| const contains = require("./contains.js"); | ||
|
|
||
| /* | ||
| Implement a function called contains that checks an object contains a | ||
| particular property | ||
| describe("contains", () => { | ||
| // Case 1: Should return true if the property exists in object. | ||
| test("should return true when object contains passed property name", () => { | ||
| const objsWithValidProps = [ | ||
| [{ a: 1, b: 2 }, "b"], | ||
| [{ name: "John", age: 30 }, "name"], | ||
| [{ nested: { key: "value" } }, "nested"], | ||
| [{ id: 123, status: "active", language: "JavaScript" }, "status"], | ||
| [{ data: [], items: null }, "data"], | ||
| ]; | ||
|
|
||
| E.g. contains({a: 1, b: 2}, 'a') // returns true | ||
| as the object contains a key of 'a' | ||
| objsWithValidProps.forEach(([obj, prop]) => { | ||
| expect(contains(obj, prop)).toEqual(true); | ||
| }); | ||
| }); | ||
|
|
||
| E.g. contains({a: 1, b: 2}, 'c') // returns false | ||
| as the object doesn't contains a key of 'c' | ||
| */ | ||
| // Case 2: Should return false if the object does not contain the given property. | ||
| test("should return false when object does not contain passed property name", () => { | ||
| const objsWithoutProps = [ | ||
| [{ a: 1, b: 2 }, "c"], | ||
| [{ name: "John", age: 30 }, "email"], | ||
| [{ nested: { key: "value" } }, "nonexistent"], | ||
| [{ id: 123, status: "active", language: "JavaScript" }, "description"], | ||
| [{ data: [], items: null }, "nonexistent"], | ||
| ]; | ||
|
|
||
| // Acceptance criteria: | ||
| objsWithoutProps.forEach(([obj, prop]) => { | ||
| expect(contains(obj, prop)).toEqual(false); | ||
| }); | ||
| }); | ||
|
|
||
| // 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 | ||
| // Case 3: Should return false if the object is empty. | ||
| test("should return false when object is empty", () => { | ||
| expect(contains({}, "anyProperty")).toEqual(false); | ||
| }); | ||
|
|
||
| // Given an empty object | ||
| // When passed to contains | ||
| // Then it should return false | ||
| test.todo("contains on empty object returns false"); | ||
| // Case 4: Should return false for properties that only exist in the prototype chain | ||
| test("should return false for properties in prototype chain", () => { | ||
| const objsWithProtoProps = [ | ||
| [{ a: 1 }, "toString"], | ||
| [{ name: "John", age: 30 }, "hasOwnProperty"], | ||
| [{ nested: { key: "value" } }, "isPrototypeOf"], | ||
| ]; | ||
|
|
||
| // Given an object with properties | ||
| // When passed to contains with an existing property name | ||
| // Then it should return true | ||
| objsWithProtoProps.forEach(([obj, prop]) => { | ||
| expect(contains(obj, prop)).toEqual(false); | ||
| }); | ||
| }); | ||
|
|
||
| // Given an object with properties | ||
| // When passed to contains with a non-existent property name | ||
| // Then it should return false | ||
| // Case 5: Should throw an error if a non-object is passed | ||
| test("should throw error when non-object is passed", () => { | ||
| const nonObjects = [ | ||
| null, | ||
| undefined, | ||
| 42, | ||
| "The Curse", | ||
| true, | ||
| Infinity, | ||
| ["string"], | ||
| ]; | ||
|
|
||
| // Given invalid parameters like an array | ||
| // When passed to contains | ||
| // Then it should return false or throw an error | ||
| nonObjects.forEach((nonObj) => { | ||
| expect(() => contains(nonObj, "prop")).toThrow( | ||
| "First argument must be an object in the form { key: value }" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your function implementation is correct. However, we should write tests not only to verify our current implementation, but also to ensure that future changes do not alter the function's expected behavior. This test cannot yet confirm the function can correctly return Arrays are objects, with their indices acting as keys. A proper test should use a valid key to ensure the function returns
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks CJ, you're absolutely right. I incorrectly thought that hasOwnProperty() was available only to {} objects, but it's available to all object types, including arrays. So something like To fix this, I've gone ahead and created a separate test case for arrays using |
||
| ); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,35 @@ | ||
| function createLookup() { | ||
| // implementation here | ||
| function createLookup(countryCurrencyPairs) { | ||
| let countryCurrencyObj = {}; | ||
|
|
||
| for (const countryCurrencyPair of countryCurrencyPairs) { | ||
| if (!Array.isArray(countryCurrencyPair)) { | ||
| throw new Error("Country-currency pairs must be in array format"); | ||
| } | ||
|
|
||
| if (countryCurrencyPair.length !== 2) { | ||
| throw new Error( | ||
| "Country-currency pairs must contain exactly two elements: country and currency" | ||
| ); | ||
| } | ||
|
|
||
| let [country, currency] = countryCurrencyPair; | ||
|
|
||
| if (typeof country !== "string" || typeof currency !== "string") { | ||
| throw new Error("Country-currency pairs must be in string format"); | ||
| } | ||
|
|
||
| if (country.trim() === "" || currency.trim() === "") { | ||
| throw new Error("Country and currency codes cannot be empty"); | ||
| } | ||
|
|
||
| if (countryCurrencyObj[country]) { | ||
| throw new Error(`Duplicate country code found: ${country}`); | ||
| } | ||
|
|
||
| countryCurrencyObj[country] = currency; | ||
| } | ||
|
|
||
| return countryCurrencyObj; | ||
| } | ||
|
|
||
| module.exports = createLookup; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,35 +1,112 @@ | ||
| const createLookup = require("./lookup.js"); | ||
|
|
||
| test.todo("creates a country currency code lookup for multiple codes"); | ||
| describe("createLookup", () => { | ||
| // Case 1: Returns an empty object if no country-currency pairs are provided | ||
| test("returns an empty object if no country-currency pairs are provided", () => { | ||
| const countryCurrencyPairs = []; | ||
| const currencyObj = createLookup(countryCurrencyPairs); | ||
|
|
||
| /* | ||
| expect(currencyObj).toEqual({}); | ||
| }); | ||
|
|
||
| Create a lookup object of key value pairs from an array of code pairs | ||
| // Case 2: Returns country currency code lookup for a single country-currency pair | ||
| test("creates a country currency code lookup for a single code pair", () => { | ||
| const countryCurrencyPairs = [["US", "USD"]]; | ||
| const currencyObj = createLookup(countryCurrencyPairs); | ||
|
|
||
| Acceptance Criteria: | ||
| expect(currencyObj).toEqual({ | ||
| US: "USD", | ||
| }); | ||
| }); | ||
|
|
||
| Given | ||
| - An array of arrays representing country code and currency code pairs | ||
| e.g. [['US', 'USD'], ['CA', 'CAD']] | ||
| // Case 3: Returns country currency codes lookup for multiple country-currency pairs | ||
| test("creates a country currency code lookup for multiple codes", () => { | ||
| const countryCurrencyPairs = [ | ||
| ["US", "USD"], | ||
| ["CA", "CAD"], | ||
| ["GB", "GBP"], | ||
| ["ZA", "ZAR"], | ||
| ["NG", "NGN"], | ||
| ]; | ||
|
|
||
| When | ||
| - createLookup function is called with the country-currency array as an argument | ||
| const inputCurrencyPairObj = createLookup(countryCurrencyPairs); | ||
| const outputCurrencyPairObj = { | ||
| US: "USD", | ||
| CA: "CAD", | ||
| GB: "GBP", | ||
| ZA: "ZAR", | ||
| NG: "NGN", | ||
| }; | ||
|
|
||
| Then | ||
| - It should return an object where: | ||
| - The keys are the country codes | ||
| - The values are the corresponding currency codes | ||
| expect(inputCurrencyPairObj).toEqual(outputCurrencyPairObj); | ||
| }); | ||
|
|
||
| Example | ||
| Given: [['US', 'USD'], ['CA', 'CAD']] | ||
| // Case 4: Throws an error if a country-currency pair is not an array | ||
| test("throws an error if a country-currency pair is not an array", () => { | ||
| const countryCurrencyPairs = [ | ||
| ["US", "USD"], | ||
| ["CA", "CAD"], | ||
| "GB-GBP", | ||
| ["ZA", "ZAR"], | ||
| ["NG", "NGN"], | ||
| ]; | ||
|
|
||
| When | ||
| createLookup(countryCurrencyPairs) is called | ||
| expect(() => createLookup(countryCurrencyPairs)).toThrow( | ||
| "Country-currency pairs must be in array format" | ||
| ); | ||
| }); | ||
|
|
||
| Then | ||
| It should return: | ||
| { | ||
| 'US': 'USD', | ||
| 'CA': 'CAD' | ||
| } | ||
| */ | ||
| // Case 5: Throws an error if a country-currency pair is missing a country or currency | ||
| test("throws an error if a country-currency pair is missing a country or currency", () => { | ||
| const countryCurrencyPairs = [ | ||
| ["US", ""], | ||
| ["", "CAD"], | ||
| [" ", "GBP"], | ||
| ]; | ||
|
|
||
| for (const currencyPair of countryCurrencyPairs) { | ||
| expect(() => createLookup([currencyPair])).toThrow( | ||
| "Country and currency codes cannot be empty" | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| // Case 6: Throws an error if a country-currency pair contains more than two elements | ||
| test("throws an error if a country-currency pair contains more than two elements", () => { | ||
| const countryCurrencyPairs = [["GB", "GBP", "ZAR"]]; | ||
|
|
||
| expect(() => createLookup(countryCurrencyPairs)).toThrow( | ||
| "Country-currency pairs must contain exactly two elements: country and currency" | ||
| ); | ||
| }); | ||
|
|
||
| // Case 7: Throws and error if a country-currency pair is duplicated | ||
| test("throws an error if a country-currency pair is duplicated", () => { | ||
| const countryCurrencyPairs = [ | ||
| ["US", "USD"], | ||
| ["CA", "CAD"], | ||
| ["US", "USD"], | ||
| ]; | ||
|
|
||
| expect(() => createLookup(countryCurrencyPairs)).toThrow( | ||
| "Duplicate country code found: US" | ||
| ); | ||
| }); | ||
|
|
||
| // Case 8: Throws an error if non-string values are used as country or currency codes | ||
| test("throws an error if non-string values are used as country or currency codes", () => { | ||
| const countryCurrencyPairs = [ | ||
| [{ name: "United States" }, "USD"], | ||
| ["CA", Infinity], | ||
| ["GB", 1.21], | ||
| [undefined, "ZAR"], | ||
| ["NG", null], | ||
| ]; | ||
|
|
||
| for (const currencyPair of countryCurrencyPairs) { | ||
| expect(() => createLookup([currencyPair])).toThrow( | ||
| "Country-currency pairs must be in string format" | ||
| ); | ||
| } | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea!