Skip to content

London | ITP-May-2025 | Seddiq Azam | Module-Data-Groups | Sprint 2 #578

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 37 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
34a63f3
created new prep directory: prep/mean.js
sedazam Jun 28, 2025
54c512a
modified: prep/mean.js
sedazam Jun 28, 2025
21cada7
deleted: prep/mean.js
sedazam Jun 30, 2025
e2e39ca
modified: removed [0] and replaced it with address.houseNumber Spri…
sedazam Jul 5, 2025
fc5ec4a
modified: changed author.values[author] Sprint-2/debug/author.js
sedazam Jul 5, 2025
05faf71
modified: changed recipe.ingredients.join("\n") Sprint-2/debug/reci…
sedazam Jul 5, 2025
f6fee7b
modified: Added comments to Sprint-2/debug/address.js
sedazam Jul 6, 2025
12d95e5
modified: Changed the `Object.values()` Sprint-2/debug/author.js
sedazam Jul 6, 2025
d6f0baf
modified: Sprint-2/implement/contains.js
sedazam Jul 8, 2025
fd6ad45
new file: Sprint-2/package.json
sedazam Jul 8, 2025
282676a
modified: Sprint-3/package.json
sedazam Jul 8, 2025
330f67a
modified: Sprint-2/implement/contains.js
sedazam Jul 8, 2025
fd4ba76
Merge branch 'CodeYourFuture:main' into Sprint-2
sedazam Jul 8, 2025
75cdab9
sedazam Jul 8, 2025
0da6bcb
modified: Sprint-2/implement/lookup.js
sedazam Jul 9, 2025
fc94a75
modified: Sprint-2/implement/lookup.js
sedazam Jul 9, 2025
1df5c85
modified: Fixed to handle values without = sign Sprint-2/implement/…
sedazam Jul 9, 2025
83e6497
modified: Sprint-2/implement/tally.js
sedazam Jul 9, 2025
1f5ee6f
Fix invert function to correctly swap keys and values in the object
sedazam Jul 10, 2025
3d6bdf4
Implement countWords function to count word occurrences in a string
sedazam Jul 10, 2025
2e3464a
Refactor calculateMode function by splitting it into getFrequencies a…
sedazam Jul 10, 2025
f9a944e
Fix totalTill function to correctly calculate total amount in pounds …
sedazam Jul 10, 2025
1734c42
undo changes to Sprint 3 package.json file
sedazam Jul 10, 2025
4619e3d
undo the changes to package.json file
sedazam Jul 10, 2025
ca5f265
undo changes to package.json file
sedazam Jul 10, 2025
8b4abbd
Update package.json
sedazam Jul 10, 2025
2be54ee
Update package.json
sedazam Jul 10, 2025
afbdad0
Implement feature X to enhance user experience and fix bug Y in module Z
sedazam Jul 10, 2025
bee93a4
Refactor contains function for improved input validation
sedazam Jul 19, 2025
ab5f179
Refactor test syntax for consistency and clarity
sedazam Jul 19, 2025
f865aa4
Refactor tally function to use Object.create(null) for result initial…
sedazam Jul 19, 2025
b924a9f
Refactor countWords function for improved readability and efficiency
sedazam Jul 19, 2025
e893a83
Refactor countWords function to improve punctuation handling and case…
sedazam Jul 19, 2025
2a152c6
Fix totalTill function to return formatted currency and update expect…
sedazam Jul 19, 2025
61584ba
Fix totalTill function to correctly calculate total value and handle …
sedazam Jul 19, 2025
4213201
Fix totalTill function to handle unrecognized coins and update total …
sedazam Jul 19, 2025
a2a063e
Merge branch 'main' into Sprint-2
sedazam Jul 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
// but it isn't working...
// Fix anything that isn't working

// The code is trying to log the `houseNumber` property from the `address` object.
// However, the `address` object is not defined in the provided code snippet.
// To fix this, we need to define the `address` object first and then log the
// `houseNumber` property correctly.
// The code is also using a template literal to log the value, which is correct.
// Let's define the `address` object and log the `houseNumber` property.
const address = {
houseNumber: 42,
street: "Imaginary Road",
Expand All @@ -12,4 +18,4 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
console.log(`My house number is ${address.houseNumber}`);
11 changes: 10 additions & 1 deletion Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// Predict and explain first...

// This code is trying to log out the values of the properties in the `author` object.
// However, the `for...of` loop is designed to iterate over iterable objects like arrays
// or strings, not over objects. Since `author` is an object, the loop will not work as intended,
// and it will throw a TypeError because objects are not iterable by default.

// 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 @@ -11,6 +16,10 @@ const author = {
alive: true,
};

for (const value of author) {
// The correct way to iterate over the values of an object is to use `Object.values()`
// which returns an array of the object's values, and then use `for...of` to
// iterate over that array.
// Here is the corrected code:
for (const value of Object.values(author)) {
console.log(value);
}
7 changes: 6 additions & 1 deletion Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// Predict and explain first...

// This code is intended to log out a recipe object in a specific format.
// However, it currently does not log the ingredients correctly.
// The issue is that it tries to log the entire recipe object directly, which will not format the ingredients as intended.
// Instead, it should iterate over the ingredients array and log each ingredient on a new line.

// 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?
Expand All @@ -12,4 +17,4 @@ const recipe = {

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
${recipe.ingredients.join("\n")}`);
10 changes: 9 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
function contains() {}
function contains(obj, property) {
// Safety check: handle invalid inputs
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
return false; // Return false for non-object types, null, undefined, or arrays
}

// Check if the object has the specified property
return obj.hasOwnProperty(property); // Use hasOwnProperty to check for the property
}

module.exports = contains;
25 changes: 24 additions & 1 deletion Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,42 @@ as the object doesn't contains a key of 'c'
// When passed an object and a property name
// Then it should return true if the object contains the property, false otherwise

test("contains a property in an object should return true or false", () => {
expect(contains({ a: 1, b: 2 }, "a")).toBe(true);
expect(contains({ a: 1, b: 2 }, "b")).toBe(true);
expect(contains({ a: 1, b: 2 }, "c")).toBe(false);
});

// Given an empty object
// When passed to contains
// Then it should return false
test.todo("contains on empty object returns false");
test("contains an empty object should return false", () => {
expect(contains({}, "a")).toBe(false);
});

// Given an object with properties
// When passed to contains with an existing property name
// Then it should return true

test("contains an existing property in an object should return true", () => {
expect(contains({ a: 1, b: 2 }, "a")).toBe(true);
expect(contains({ a: 1, b: 2 }, "b")).toBe(true);
});

// Given an object with properties
// When passed to contains with a non-existent property name
// Then it should return false

test("contains a non-existent property in an object should return false", () => {
expect(contains({ a: 1, b: 2 }, "c")).toBe(false);
});

// Given invalid parameters like an array
// When passed to contains
// Then it should return false or throw an error
test("contains with invalid parameters should return false", () => {
expect(contains(null, "a")).toBe(false);
expect(contains(undefined, "a")).toBe(false);
expect(contains([], "a")).toBe(false);
expect(contains("string", "a")).toBe(false);
});
20 changes: 18 additions & 2 deletions Sprint-2/implement/lookup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
function createLookup() {
// implementation here
function createLookup(countryCurrencyPairs) {
const result = {};

for (let i = 0; i < countryCurrencyPairs.length; i++) {
const pair = countryCurrencyPairs[i];
const [countryCode, currencyCode] = pair;

result[countryCode] = currencyCode;
}

return result;
}

console.log(
createLookup([
["US", "USD"],
["CA", "CAD"],
])
);

module.exports = createLookup;
13 changes: 11 additions & 2 deletions Sprint-2/implement/lookup.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
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
Expand Down Expand Up @@ -33,3 +31,14 @@ It should return:
'CA': 'CAD'
}
*/
test("given an array of country-currency pairs, createLookup should return an object with country codes as keys and currency codes as values", () => {
expect(
createLookup([
["US", "USD"],
["CA", "CAD"],
])
).toEqual({
US: "USD",
CA: "CAD",
});
});
17 changes: 13 additions & 4 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ function parseQueryString(queryString) {
}
const keyValuePairs = queryString.split("&");

for (const pair of keyValuePairs) {
const [key, value] = pair.split("=");
queryParams[key] = value;
for (let i = 0; i < keyValuePairs.length; i++) {
const pair = keyValuePairs[i];
const firstEqualIndex = pair.indexOf("=");

if (firstEqualIndex === -1) {
// If there is no equal sign, treat the whole pair as a key with an empty value
queryParams[pair] = "";
} else {
const key = pair.slice(0, firstEqualIndex);
const value = pair.slice(firstEqualIndex + 1);
queryParams[key] = value;
Comment on lines +14 to +18
Copy link
Contributor

Choose a reason for hiding this comment

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

Please note that in real querystring, both key and value are percent-encoded or URL encoded in the URL. For example, the string "5%" will be encoded as "5%25". So to get the actual value of "5%25" (whether it is a key or value in the querystring), you should call a function to decode it.
May I suggest looking up any of these terms, and "How to decode URL encoded string in JS"?

Copy link
Author

Choose a reason for hiding this comment

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

Character Encoded As Reason
% %25 Starts an encoded sequence, must be escaped itself
(space) %20 Spaces are not allowed in URLs
& %26 Separates query parameters
= %3D Assigns values to keys
? %3F Starts a query string
! %21 Reserved character, often escaped
/ %2F Used in paths, escaped in values
# %23 Starts a URL fragment
" %22 Double quotes must be escaped
' %27 Single quotes can cause parsing issues
: %3A Used in protocols (e.g., http:)
+ %2B May be interpreted as space in form data

Copy link
Contributor

Choose a reason for hiding this comment

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

There are built-in functions you can use to decode the percent-encoded key and value at lines 14 and 18.

}
}

return queryParams;
}

console.log(parseQueryString("equation=x=y+1")); // { equation: 'x=y+1' }
module.exports = parseQueryString;
8 changes: 6 additions & 2 deletions Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@
// 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 with no values", () => {
expect(parseQueryString("")).toEqual({});
});
17 changes: 16 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
function tally() {}
function tally(inputArray) {
if (!Array.isArray(inputArray)) {
throw new Error("Input must be an array");
}

const result = Object.create(null);

for (const element of inputArray) {
if (result[element]) {
result[element] += 1;
} else {
result[element] = 1;
}
}
return result;
}
console.log(tally(["toString", "toString", "toString"]));
module.exports = tally;
18 changes: 17 additions & 1 deletion Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,32 @@ const tally = require("./tally.js");
// Given a function called tally
// When passed an array of items
// Then it should return an object containing the count for each unique item
test("tally should return an object with counts for each unique item", () => {
const input = ["a", "b", "a", "c"];
const expectedOutput = { a: 2, b: 1, c: 1 };
expect(tally(input)).toEqual(expectedOutput);
});

// 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 should return an empty object for an empty array", () => {
expect(tally([])).toEqual({});
});

// Given an array with duplicate items
// When passed to tally
// Then it should return counts for each unique item
test("tally should return counts for duplicate items", () => {
const input = ["a", "a", "b", "c"];
const expectedOutput = { a: 2, b: 1, c: 1 };
expect(tally(input)).toEqual(expectedOutput);
});

// Given an invalid input like a string
// When passed to tally
// Then it should throw an error

test("tally should throw an error for invalid input", () => {
expect(() => tally("not an array")).toThrow("Input must be an array");
});
37 changes: 36 additions & 1 deletion Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,55 @@ function invert(obj) {
const invertedObj = {};

for (const [key, value] of Object.entries(obj)) {
invertedObj.key = value;
invertedObj[value] = key;
}

return invertedObj;
}

// Test cases to verify the functionality of invert
console.log(invert({ a: 1 })); // { '1': 'a' }
console.log(invert({ a: 1, b: 2 })); // { '1': 'a', '2': 'b' }
console.log(invert({ x: 10, y: 20 })); // { '10': 'x', '20': 'y' }
console.log(invert({ name: "Alice", age: 30 })); // { 'Alice': 'name', '30': 'age' }

// a) What is the current return value when invert is called with { a : 1 }

/* { key: 1 }. This is because the code is using `invertedObj.key`.
it uses dot notation with the literal word "key".
The key 'key' is being set to the value of 1.
If we use `invertedObj.Smith = 1`, it will return { Smith: 1 }
/*

// b) What is the current return value when invert is called with { a: 1, b: 2 }

/* { key: 2 }
This is because the loop iterates over the entries of the object,
and the last entry processed is 'b: 2'.
The key 'key' is set to the value of 2, overwriting the previous value
of 1 from the first iteration.
*/

// c) What is the target return value when invert is called with {a : 1, b: 2}

/* { '1': 'a', '2': 'b' }
This is the expected output where the keys and values are swapped.
*/

// c) What does Object.entries return? Why is it needed in this program?

/* [["a", 1], ["b", 2]]
Object.entries returns an array of key-value pairs from the object.
It is needed in this program to iterate over the object's properties
and their values, allowing us to swap them in the inverted object.
*/

// d) Explain why the current return value is different from the target output

/* The current return value is different from the target output
because the code is incorrectly using `invertedObj.key` instead of using the variable `key` to set the property name dynamically.
This means it always sets the property 'key' to the last value processed in the loop,
instead of creating properties with the original values as keys and the original keys as values.
*/

// e) Fix the implementation of invert (and write tests to prove it's fixed!)
31 changes: 31 additions & 0 deletions Sprint-2/stretch/count-words.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,34 @@

3. Order the results to find out which word is the most common in the input
*/

const countWords = (str) => {
if (str.trim().length === 0) {
return 0;
}
const words = str.split(" ");
const counts = Object.create(null);

for (const eachWord of words) {
if (eachWord in counts) {
counts[eachWord]++;
} else {
counts[eachWord] = 1;
}
}

return counts;
};

console.log(countWords("you and me and you")); // { you: 2, and: 2, me: 1 }
console.log(countWords("hello world hello")); // { hello: 2, world: 1 }
console.log(countWords("this is a test this is only a test")); // { this: 2, is: 2, a: 2, test: 2, only: 1 }
console.log(countWords("")); // 0
console.log(countWords(" ")); // 0
console.log(countWords("one two three one two")); // { one: 2, two: 2, three: 1 }
console.log(countWords("apple banana apple orange banana apple")); // { apple: 3, banana: 2, orange: 1 }
console.log(countWords("The quick brown fox jumps over the lazy dog")); // { The: 1, quick: 1, brown: 1, fox: 1, jumps: 1,
// over: 1, the: 1, lazy: 1, dog: 1 }

console.log(countWords("0 1 2 3 4 5 6 6 6 6 7 7 7 7 7 7 8 9 9 9 0 0")); // { '0': 2, '1': 1, '2': 1, '3': 1, '4': 1, '5': 1, '6': 1, '7': 1, '8': 1, '9': 1 }
console.log(JSON.stringify(countWords("constructor constructor constructor"))); // { constructor: 3 }
21 changes: 16 additions & 5 deletions Sprint-2/stretch/mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,26 @@
// refactor calculateMode by splitting up the code
// into smaller functions using the stages above

function calculateMode(list) {
function getFrequencies(list) {
// track frequency of each value
let freqs = new Map();
const frequencies = new Map();

for (let num of list) {
if (typeof num !== "number") {
continue;
}

freqs.set(num, (freqs.get(num) || 0) + 1);
frequencies.set(num, (frequencies.get(num) || 0) + 1);
}
return frequencies;
}

// Find the value with the highest frequency
// Find the value with the highest frequency
function getMostFrequentValue(frequencies) {
let maxFreq = 0;
let mode;
for (let [num, freq] of freqs) {

for (let [num, freq] of frequencies) {
if (freq > maxFreq) {
mode = num;
maxFreq = freq;
Expand All @@ -33,4 +37,11 @@ function calculateMode(list) {
return maxFreq === 0 ? NaN : mode;
}

function calculateMode(list) {
const frequencies = getFrequencies(list);
return getMostFrequentValue(frequencies);
}

console.log(calculateMode([1, 2, 2, 3, 4, 4, 4, 5])); // Output: 4
console.log(calculateMode([1, 1, 2, "2", 3, 3])); // Output: 1 (or 2 or 3, since they
module.exports = calculateMode;
Loading