Skip to content

London | 25-ITP-May | Hendrine Zeraua | Sprint 2| Data Groups #690

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
12 changes: 11 additions & 1 deletion Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// Predict and explain first...

// The code will print undefined because address[0] is incorrect. To access a value in an object,
// we need to use either dot notation (address.houseNumber) or bracket notation with
// the key name (address["houseNumber"]). Using a number like 0 won’t work unless
// the object has a property with that exact key.

// This code should log out the houseNumber from the address object
// but it isn't working...
// Fix anything that isn't working
Expand All @@ -12,4 +17,9 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
// console.log(`My house number is ${address[0]}`);
// Fixed code:
console.log(`My house number is ${address.houseNumber}`);



17 changes: 16 additions & 1 deletion Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
// Predict and explain first...

//My prediction:
//The code will throw a TypeError because author is a regular object, and the for...of loop
// only works with iterables like arrays, strings, etc.
// Since objects are not iterable by default, trying to do for (const value of author) will fail.
// To loop through all values in the object, we need to convert it into
// something iterable — like using Object.values(author), which returns an array of all values.

// 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

Expand All @@ -10,7 +17,15 @@ const author = {
age: 40,
alive: true,
};
// Broken code
// for (const value of author) {
// console.log(value);
// }
// This will cause a TypeError: author is not iterable.

//Fixed code

for (const value of author) {
for (const value of Object.values(author)) {
console.log(value);
}

20 changes: 19 additions & 1 deletion Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
// Predict and explain first...

bruschetta serves 2
ingredients:
[object Object]

// The first part, ${recipe.title} serves ${recipe.serves}, works fine because it's using string
// and number values. But the last part, ${recipe}, tries to print the whole recipe object.
// In JavaScript, when you put an object inside a string like that, it turns into [object Object].
// That's why you see that instead of the actual list of ingredients.


// 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?
// To fix it, we should print the ingredients properly. Since ingredients is an array,
// we can join the items with \n so each one appears on a new line.

const recipe = {
title: "bruschetta",
serves: 2,
ingredients: ["olive oil", "tomatoes", "salt", "pepper"],
};

// console.log(`${recipe.title} serves ${recipe.serves}
// ingredients:
// ${recipe}`);


// Here's the fixed code:
console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
${recipe.ingredients.join('\n')}`);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does join change the type of values of recipe ingredients?

13 changes: 10 additions & 3 deletions Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
function contains() {}

module.exports = contains;
function contains(obj, key) {
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {
return false; // invalid input — not a plain object
}

return Object.prototype.hasOwnProperty.call(obj, key);
}

module.exports = contains;

49 changes: 48 additions & 1 deletion Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const contains = require("./contains.js");
// const contains = require("./contains.js");

/*
Implement a function called contains that checks an object contains a
Expand All @@ -22,6 +22,7 @@ as the object doesn't contains a key of 'c'
// 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
Expand All @@ -33,3 +34,49 @@ 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

const contains = require('./contains');

describe('contains', () => {
// Acceptance Criterion 1:
test('returns true when object contains the given key', () => {
expect(contains({ a: 1, b: 2 }, 'a')).toBe(true);
});

// Acceptance Criterion 2:
test('returns false when object does not contain the given key', () => {
expect(contains({ a: 1, b: 2 }, 'c')).toBe(false);
});

// Acceptance Criterion 3:
test('returns false when object is empty', () => {
expect(contains({}, 'a')).toBe(false);
});

// Edge Case 1: Array (invalid input)
test('returns false when passed an array instead of an object', () => {
expect(contains([], 'length')).toBe(false);
});

// Edge Case 2: Null
test('returns false when passed null', () => {
expect(contains(null, 'a')).toBe(false);
});

// Edge Case 3: Number (invalid type)
test('returns false when passed a number instead of an object', () => {
expect(contains(123, 'a')).toBe(false);
});

// Edge Case 4: Object without prototype (no hasOwnProperty)
test('returns false when object is created with null prototype and does not contain the key', () => {
const obj = Object.create(null);
expect(contains(obj, 'missing')).toBe(false);
});

test('returns true when object is created with null prototype and contains the key', () => {
const obj = Object.create(null);
obj.test = 42;
expect(contains(obj, 'test')).toBe(true);
});
});
10 changes: 8 additions & 2 deletions Sprint-2/implement/lookup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
function createLookup() {
// implementation here
function createLookup(pairs) {
const lookup = {};

for (const [country, currency] of pairs) {
lookup[country] = currency;
}

return lookup;
}

module.exports = createLookup;
68 changes: 68 additions & 0 deletions Sprint-2/implement/lookup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,71 @@ It should return:
'CA': 'CAD'
}
*/

// Standard input

test("creates a country currency code lookup for multiple codes", () => {
const input = [['US', 'USD'], ['CA', 'CAD'], ['GB', 'GBP']];
const expectedOutput = { US: 'USD', CA: 'CAD', GB: 'GBP' };
expect(createLookup(input)).toEqual(expectedOutput);
});

// Empty input

test("returns an empty object when given an empty array", () => {
expect(createLookup([])).toEqual({});
});

// Duplicate Country Code

test("uses the last currency code when duplicate country codes are provided", () => {
const input = [['US', 'USD'], ['US', 'USN']];
const expectedOutput = { US: 'USN' };
expect(createLookup(input)).toEqual(expectedOutput);
});

// Handles lowercase or mixed-case country codes

test("treats lowercase country codes as valid keys", () => {
const input = [['us', 'USD'], ['ca', 'CAD']];
const expectedOutput = { us: 'USD', ca: 'CAD' };
expect(createLookup(input)).toEqual(expectedOutput);
});

// Handles special characters in codes

test("handles country codes with special characters", () => {
const input = [['U$1', 'U$D'], ['C@1', 'C@D']];
const expectedOutput = { 'U$1': 'U$D', 'C@1': 'C@D' };
expect(createLookup(input)).toEqual(expectedOutput);
});

// Handles very large input

test("correctly handles a large number of entries", () => {
const input = Array.from({ length: 1000 }, (_, i) => [`C${i}`, `CUR${i}`]);
const expectedOutput = Object.fromEntries(input);
expect(createLookup(input)).toEqual(expectedOutput);
});

/*

I implemented the createLookup function to convert an array of country–currency code pairs into a JavaScript object.
The function uses a basic for...of loop with array destructuring to assign each
country code as a key and the corresponding currency code as its value. I also wrote a set of
Jest tests to verify the function’s correctness across several cases, including:

- Standard inputs
- Empty input
- Duplicate country codes
- Lowercase codes
- Special characters
- A large dataset of 1000 entries

The function passed all tests, confirming it works as expected for a variety of valid inputs.

*/




10 changes: 8 additions & 2 deletions Sprint-2/implement/querystring.js
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job!

In the real world, URLs are usually encoded with percent encoding to represent special characters. In URL, & can be used as a separator (key=value&key=value...) or special character within a key or value. if we were to use & as a special character, we need to encode it.

Can you refactor the code so that parseQueryString("a%25b=c%26d") results in { "a%b": "c&d" }?
FYI, %25 is same as %, %26 is same as &

Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ function parseQueryString(queryString) {
if (queryString.length === 0) {
return queryParams;
}

const keyValuePairs = queryString.split("&");

for (const pair of keyValuePairs) {
const [key, value] = pair.split("=");
const indexOfEquals = pair.indexOf("=");
if (indexOfEquals === -1) {
queryParams[pair] = "";
continue;
}
const key = pair.slice(0, indexOfEquals);
const value = pair.slice(indexOfEquals + 1);
queryParams[key] = value;
}

return queryParams;
}

module.exports = parseQueryString;
45 changes: 44 additions & 1 deletion Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,51 @@

const parseQueryString = require("./querystring.js")

// Values containing equal signs and plus signs
test("parses querystring values containing =", () => {
expect(parseQueryString("equation=x=y+1")).toEqual({
"equation": "x=y+1",
equation: "x=y+1",
});
});

// Empty string
test("returns empty object for empty query string", () => {
expect(parseQueryString("")).toEqual({});
});

// Multiple key-value pairs
test("parses multiple key-value pairs", () => {
expect(parseQueryString("name=alice&age=30")).toEqual({
name: "alice",
age: "30",
});
});

// Key with no value
test("handles keys with no value", () => {
expect(parseQueryString("empty=")).toEqual({
empty: "",
});
});

// Key without =
test("treats standalone key as having empty value", () => {
expect(parseQueryString("justkey")).toEqual({
justkey: "",
});
});

/*
I reviewed and fixed the parseQueryString function to correctly handle query string edge cases.
Originally, the function incorrectly split key-value pairs on every =,
which caused values containing = (like "x=y+1") to be truncated. I updated the implementation to split only on
the first = using indexOf and slice, which preserves the full value even when = appears within it.
I also added logic to support:
- Empty query strings ("" → {})
- Keys with no values (e.g., "foo=" → { foo: "" })
- Standalone keys (e.g., "foo" → { foo: "" })
- Multiple key-value pairs
*/



19 changes: 16 additions & 3 deletions Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
function tally() {}

module.exports = tally;
function tally(items) {
if (!Array.isArray(items)) {
throw new Error("Input must be an array");
}

const counts = {};

for (const item of items) {
counts[item] = (counts[item] || 0) + 1;
}

return counts;
}

module.exports = tally;

35 changes: 35 additions & 0 deletions Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
const tally = require("./tally.js");

// Empty array
test("tally on an empty array returns an empty object", () => {
expect(tally([])).toEqual({});
});

// Array with duplicates
test("counts items correctly in an array with duplicates", () => {
expect(tally(['a', 'a', 'b', 'c'])).toEqual({
a: 2,
b: 1,
c: 1,
});
});

// Invalid input (like a string)
test("throws an error if input is not an array", () => {
expect(() => tally("not an array")).toThrow("Input must be an array");
});

/*
I implemented a function called tally that counts the frequency of each item in an array and
returns the result as an object. The function uses a for...of loop and an object to track counts.
It supports:
- Regular arrays (e.g., ['a', 'b', 'a'] → { a: 2, b: 1 })
- Empty arrays (returns {})
- Type validation to ensure the input is an array — otherwise, it throws an error

I also wrote a set of tests using Jest to confirm the function handles:
- Counting multiple repeated items
- Empty input
- Invalid input types (e.g., passing a string)
This ensures the function behaves reliably and fails safely when given unexpected input.
*/


/**
* tally array
*
Expand Down
Loading