Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
b88166f
Fix console log to correctly display house number from address object
moses77-boop Nov 5, 2026
7158a9b
Remove commented-out code and explanations from address.js
moses77-boop Nov 5, 2026
16a2b1a
Add explanation for for...of loop issue in author.js
moses77-boop Nov 5, 2026
b124b4d
Fix for...of loop to correctly iterate over author object properties
moses77-boop Nov 5, 2026
fa4aee7
Remove unnecessary comments and explanations from author.js
moses77-boop Nov 5, 2026
ee40cba
refactor console log to correctly display ingredients list in recipe.js
moses77-boop Nov 5, 2026
f4617bf
Remove unnecessary comments from recipe.js
moses77-boop Nov 5, 2026
106c55f
Implement test for contains function on empty object
moses77-boop Nov 5, 2026
9f62098
Implement contains function to return false
moses77-boop Nov 5, 2026
51be35c
Add test for contains function with existing property
moses77-boop Nov 5, 2026
f2a26fc
Implement contains function to check for property existence in object
moses77-boop Nov 5, 2026
96cdc69
Add tests for contains function with non-existent property and error …
moses77-boop Nov 5, 2026
990790e
Add test for contains function to throw error for string input
moses77-boop Nov 5, 2026
8ea5fab
Add test for contains function to throw error for number input
moses77-boop Nov 5, 2026
e586a0b
Add test for contains function to throw error for null input
moses77-boop Nov 5, 2026
9b77ec8
Add test for contains function to throw error when no argument is given
moses77-boop Nov 5, 2026
3ede084
Remove unnecessary comments from contains test file
moses77-boop Nov 5, 2026
26939b1
Add validation to contains function to ensure input is an object
moses77-boop Nov 5, 2026
c6044d5
Implement test for country-currency pairs
moses77-boop Nov 5, 2026
8c357a8
Implement createLookup function to map country codes to currency codes
moses77-boop Nov 5, 2026
df599c5
Refactor createLookup function to use an object instead of an array f…
moses77-boop Nov 5, 2026
6440839
Remove unnecessary comments from lookup test file
moses77-boop Nov 5, 2026
dc9fbcd
Implement test for tally function on an empty array
moses77-boop Nov 5, 2026
cbe89f1
Implement return statement in tally function
moses77-boop Nov 5, 2026
0df2538
Add test for tally function to count duplicate items
moses77-boop Nov 5, 2026
c08d7ad
Add test for tally function to verify counts for unique items
moses77-boop Nov 5, 2026
f82e064
Implement tally function to count occurrences of items in an array
moses77-boop Nov 5, 2026
b6f88e8
Add test for tally function to handle invalid input
moses77-boop Nov 5, 2026
b8fe136
Add input validation to tally function to ensure an array is provided
moses77-boop Nov 5, 2026
625a080
Remove unnecessary comments from tally test file for clarity
moses77-boop Nov 5, 2026
86acb17
Refactor parseQueryString to use indexOf and slice for key-value extr…
moses77-boop Nov 5, 2026
dff6f8c
Fix parseQueryString to skip empty key-value pairs in query string
moses77-boop Nov 5, 2026
50c1037
Refactor parseQueryString to handle key-value extraction with conditi…
moses77-boop Nov 5, 2026
53bcc81
Enhance parseQueryString to decode keys and values for improved query…
moses77-boop Nov 5, 2026
40770d8
Refactor parseQueryString to support multiple values for the same key
moses77-boop Nov 5, 2026
89aa7c8
add edge case test for malformed percent-encoding
moses77-boop Nov 5, 2026
55bcc8e
Refactor decode function to handle errors and replace plus signs with…
moses77-boop Nov 5, 2026
01bf866
Update comment to clarify current return value for invert function
moses77-boop Nov 6, 2026
3e4344a
Update comment to clarify current return value for invert function wi…
moses77-boop Nov 6, 2026
b3d7030
Update comment to clarify target return value for invert function wit…
moses77-boop Nov 6, 2026
4ebef47
Update comments to clarify the purpose of Object.entries in the inver…
moses77-boop Nov 6, 2026
cffb378
Update comments to clarify the reason for the current return value in…
moses77-boop Nov 6, 2026
f6adbf3
Fix invert function to correctly swap keys and values; add initial te…
moses77-boop Nov 6, 2026
c5db3de
Add invert function and initial test case for key-value swapping
moses77-boop Nov 6, 2026
f0daded
Add test case for multiple key-value pairs in invert function
moses77-boop Nov 6, 2026
a45c5ea
Update test case for multiple key-value pairs in invert function
moses77-boop Nov 6, 2026
41a07a1
Add test case for returning an empty object in invert function
moses77-boop Nov 6, 2026
1d46b22
Remove unnecessary comments
moses77-boop Nov 6, 2026
69b6210
Update error message tests in contains function for consistency
moses77-boop Nov 8, 2026
78a1f82
Add test case for handling items named like built-in object propertie…
moses77-boop Nov 8, 2026
12a783e
Refactor tally function to simplify item counting logic
moses77-boop Nov 8, 2026
6373435
Update comments in invert function to clarify return values
moses77-boop Nov 8, 2026
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: 1 addition & 7 deletions Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
// Predict and explain first...

// This code should log out the houseNumber from the address object
// but it isn't working...
// Fix anything that isn't working

const address = {
houseNumber: 42,
street: "Imaginary Road",
Expand All @@ -12,4 +6,4 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
console.log(`My house number is ${address.houseNumber}`);
9 changes: 4 additions & 5 deletions Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// 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
// explanation:
// The code could not be executed because the for...of loop could not access object "author".
// for...loops are used in arrays, strings or with certain array methods.

const author = {
firstName: "Zadie",
Expand All @@ -11,6 +10,6 @@ const author = {
alive: true,
};

for (const value of author) {
for (const value of Object.values(author)) {
console.log(value);
}
8 changes: 1 addition & 7 deletions Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
// 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?

const recipe = {
title: "bruschetta",
serves: 2,
Expand All @@ -12,4 +6,4 @@ const recipe = {

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
${recipe.ingredients.join("\n")}`);
7 changes: 6 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
function contains() {}
function contains(object, property) {
if (typeof object !== "object" || object === null || Array.isArray(object)) {
throw new Error("contains requires an object");
}
return Object.hasOwn(object, property);
}

module.exports = contains;
45 changes: 30 additions & 15 deletions Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
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'

E.g. contains([1, 2, 3], 'a') throws Error("contains requires an object")
as an array isn't an object
*/

// Acceptance criteria:

// Given a contains function
Expand All @@ -23,18 +9,47 @@ as an array isn't an object
// Given an empty object
// When passed to contains
// Then it should return false
test.todo("contains on empty object returns false");
test("contains on empty object returns false", () => {
expect(contains({}, "name")).toBe(false);
});

// Given an object with properties
// When passed to contains with an existing property name
// Then it should return true
test("contains on object with existing property returns true", () => {
expect(contains({ name: "Moses", age: 30 }, "name")).toBe(true);
});

// Given an object with properties
// When passed to contains with a non-existent property name
// Then it should return false
test("contains on object with non-existent property returns false", () => {
expect(contains({ name: "Moses", age: 30 }, "city")).toBe(false);
});

// Given a value that isn't an object - an array, a string, a number,
// null, or no argument at all
// When passed to contains
// Then it should throw Error("contains requires an object")
// (careful: typeof [] and typeof null are both "object")
test("contains throws an error for an array", () => {
expect(() => contains([1, 2, 3], "0")).toThrow("contains requires an object");
});

test("contains throws an error for a string", () => {
expect(() => contains("Manchester", "0")).toThrow(
"contains requires an object"
);
});
Comment on lines +35 to +43

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your current function implementation is good, but our tests (in practice) should not only verify our current implementation, but also ensure that future changes do not alter the function's expected behavior.

Arrays are objects, with their indices acting as keys.

Object.hasOwn([1, 2, 3], 'name')        // evaluates to false
Object.hasOwn([1, 2, 3], '0')           // evaluates to true

A proper test should use a valid key to guarantee the function returns false specifically because the first argument is an array, not because the key is missing.

The same also apply to string.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thank you for the feedback!

I have refactored my code.


test("contains throws an error for a number", () => {
expect(() => contains(30, "name")).toThrow("contains requires an object");
});

test("contains throws an error for null", () => {
expect(() => contains(null, "name")).toThrow("contains requires an object");
});

test("contains throws an error when no argument is given", () => {
expect(() => contains()).toThrow("contains requires an object");
});
12 changes: 10 additions & 2 deletions Sprint-2/implement/lookup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
function createLookup() {
// implementation here
function createLookup(pairs) {
const lookup = {};

for (const pair of pairs) {
const countryCode = pair[0];
const currencyCode = pair[1];

lookup[countryCode] = currencyCode;
}
return lookup;
}

module.exports = createLookup;
42 changes: 9 additions & 33 deletions Sprint-2/implement/lookup.test.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,11 @@
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'
}
*/
test("creates a country currency code lookup for multiple codes", () => {
const pairs = [
["US", "USD"],
["CA", "CAD"],
];
const result = createLookup(pairs);

expect(result).toEqual({ US: "USD", CA: "CAD" });
});
30 changes: 28 additions & 2 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
function decode(text) {
const withSpaces = text.replace(/\+/g, " ");
try {
return decodeURIComponent(withSpaces);
} catch {
return withSpaces;
}
}

function parseQueryString(queryString) {
const queryParams = {};
if (queryString.length === 0) {
Expand All @@ -6,8 +15,25 @@ function parseQueryString(queryString) {
const keyValuePairs = queryString.split("&");

for (const pair of keyValuePairs) {
const [key, value] = pair.split("=");
queryParams[key] = value;
if (pair === "") continue;

const index = pair.indexOf("=");
const separator = index !== -1;
const rawKey = separator ? pair.slice(0, index) : pair;
const rawValue = separator ? pair.slice(index + 1) : "";

const key = decode(rawKey);
const value = decode(rawValue);

if (key in queryParams) {
if (Array.isArray(queryParams[key])) {
queryParams[key].push(value);
} else {
queryParams[key] = [queryParams[key], value];
}
} else {
queryParams[key] = value;
}
}

return queryParams;
Expand Down
7 changes: 6 additions & 1 deletion Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Below are some test cases the implementation doesn't handle well.
// Fix the implementation for these tests, 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("should parse values containing '='", () => {
expect(parseQueryString("equation=a=b-2")).toEqual({
Expand Down Expand Up @@ -46,3 +46,8 @@ test("should store values of a key in an array when the key has 2 or more values
foo: "bar",
});
});

// Edge cases
test("should not crash on malformed percent-encoding", () => {
expect(parseQueryString("a=%E0%A4%A")).toEqual({ a: "%E0%A4%A" });
});
Comment on lines +50 to +53

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice edge case consideration.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thank you for the feedback!

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(items) {
if (!Array.isArray(items)) {
throw new Error("tally requires an array");
}

const counts = {};

for (const item of items) {
if (Object.hasOwn(counts, item)) {
counts[item] += 1;
} else {
counts[item] = 1;
}
}
Comment on lines +6 to +14

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When using a regular object as a dictionary or map, we should consider whether a key might match the name of an inherited property.

For example, does the following function call returns the value you expect?

tally(["toString", "toString"]);

Suggestion:

  • Look up an approach to create an empty object with no inherited properties, or
  • use Object.hasOwn(), or
  • use a Map object

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thank you for the feedback!

I have refactored my code.

return counts;
}

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

/**
* tally array
*
* In this task, you'll need to implement a function called tally
* that will take a list of items and count the frequency of each item
* in an array
*
* For example:
*
* tally(['a']), target output: { a: 1 }
* tally(['a', 'a', 'a']), target output: { a: 3 }
* tally(['a', 'a', 'b', 'c']), target output: { a : 2, b: 1, c: 1 }
*/

// Acceptance criteria:

// Given a function called tally
Expand All @@ -23,12 +9,30 @@ 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("tally on an empty array returns an empty object", () => {
expect(tally([])).toEqual({});
});

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

// Given an invalid input like a string, a number, or no argument at all
// When passed to tally
// Then it should throw Error("tally requires an array")
test("tally throws an error when given an invalid input", () => {
expect(() => tally("hello")).toThrow(new Error("tally requires an array"));
expect(() => tally(30)).toThrow(new Error("tally requires an array"));
expect(() => tally()).toThrow(new Error("tally requires an array"));
});

// Tally handles items named like built-in object properties
test("tally handles items named like built-in object properties", () => {
expect(tally(["toString", "toString"])).toEqual({ toString: 2 });
});
19 changes: 10 additions & 9 deletions Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
// Let's define how invert should work

// Given an object
// When invert is passed this object
// Then it should swap the keys and values in the object

// E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"}

function invert(obj) {
const invertedObj = {};

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

return invertedObj;
}

module.exports = invert;

// a) What is the current return value when invert is called with { a : 1 }
// Current return value is an object, { key: 1 }

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

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

// c) What does Object.entries return? Why is it needed in this program?
// Object.entries returns an array of object [key, value] pairs.
// It converts an object into an array, so the loop works, since plain objects can't be used with loops.

// d) Explain why the current return value is different from the target output
// It differs due to the fact that "invertedObj.key" uses the wrong notation, "key", instead of the variable's value.
// Also, adding brackets to the literal name, [key], would only copy the object not fix it.

// e) Fix the implementation of invert (and write tests to prove it's fixed!)
13 changes: 13 additions & 0 deletions Sprint-2/interpret/invert.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const invert = require("./invert");

test("swaps the key and value of a single pair", () => {
expect(invert({ a: 1 })).toEqual({ 1: "a" });
});

test("swaps every pair when there are multiple pairs", () => {
expect(invert({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" });
});

test("returns an empty object when given an empty object", () => {
expect(invert({})).toEqual({});
});
Loading