Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ const address = {
postcode: "XYZ 123",
};

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

// The program throws an error because a for...of loop only works with iterable values like arrays or strings.
// The author variable is an object, which is not iterable by default, so JavaScript cannot loop through it.
// 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 +11,7 @@ const author = {
age: 40,
alive: true,
};

for (const value of author) {
console.log(value);
const values = Object.values(author);
for (const item of values) {
console.log(item);
}
13 changes: 9 additions & 4 deletions Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Predict and explain first...

//The title and serves values print correctly because the properties are accessed directly using
// recipe.title and recipe.serves.
// 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 @@ -10,6 +11,10 @@ const recipe = {
ingredients: ["olive oil", "tomatoes", "salt", "pepper"],
};

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
console.log(`${recipe.title} serves ${recipe.serves}`);
for (const ingredients of recipe.ingredients) {
console.log(`*${ingredients}`);
}

//The program prints the recipe title and number of servings.
// Then it loops through the ingredients array and logs each ingredient on a new line so they are displayed individually instead of printing the whole object.
13 changes: 12 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
function contains() {}
function contains(object, propertyName) {
if (typeof object !== "object" || Array.isArray(object) || object === null) {
throw new Error("It is a invalid object");
}
const newArray = Object.keys(object);
for (let item of newArray) {
if (item === propertyName) {
return true;
}
}
return false;
}

module.exports = contains;
31 changes: 31 additions & 0 deletions Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,37 @@ E.g. contains({a: 1, b: 2}, 'c') // returns false
as the object doesn't contains a key of 'c'
*/

test("An empty object should return false", () => {
const object = {};
const propertyName = "a";
expect(contains(object, propertyName)).toEqual(false);
});

test("returns true when the object contains an existing property name", () => {
const object = { a: 1, b: 2, c: 3 };
const propertyName = "a";
expect(contains(object, propertyName)).toEqual(true);
});
test("returns false when the object not contains an existing property name", () => {
const object = { a: 1, b: 2, c: 3 };
const propertyName = "d";
expect(contains(object, propertyName)).toEqual(false);
});
test("Should throw an error for an invalid parameters", () => {
const object = [1, 2, 3, 4, 5, 6, 7];
const propertyName = "a";
expect(() => contains(object, propertyName)).toThrow();
});
Comment on lines +30 to +34
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.

Array is also an object where it indexes are its keys.
Object.hasOwn([1, 2, 3], "a") evaluates to false because "a" is not a key (index) of the array.
However, Object.hasOwn([1, 2, 3], "0") evaluates to true because "0" is a key (index) of the array.

To test if the implemented function can correctly return false when the first argument is an array,
we should specify an actual key of the array.

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.

Thanks for the clarification!

I understand that arrays are objects and that
Object.hasOwn([1, 2, 3], "0") returns true because "0" is a valid index.

However, in my implementation I explicitly treat arrays as invalid inputs:

if (typeof object !== "object" || Array.isArray(object) || object === null)

So the function throws an error before reaching Object.keys().
That’s why the test passes — it never evaluates array indexes like "0".

I’ll update either the test or the implementation depending on the expected behaviour

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.

It's not about your function, it is correctly implemented.

In practice, we prepare tests not just to check our implementation, but also to serve as a guard for future modification. In TDD, the tests will also serve as the spec that define how the function should be implemented.
That's why I keep emphasising the importance of preparing the tests correctly.

test("Should throw an error for an invalid parameters", () => {
const object = null;
const propertyName = "a";
expect(() => contains(object, propertyName)).toThrow();
});
test("Should throw an error for an invalid parameters", () => {
const object = "hi";
const propertyName = "a";
expect(() => contains(object, propertyName)).toThrow();
});
// Acceptance criteria:

// Given a contains function
Expand Down
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(countryCurrencyPairs) {
const countryCurrencyLookup = {};

for (let item of countryCurrencyPairs) {
const [key, value] = [item[0], item[1]];
countryCurrencyLookup[key] = value;
}
return countryCurrencyLookup;
}

module.exports = createLookup;
14 changes: 14 additions & 0 deletions Sprint-2/implement/lookup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ const createLookup = require("./lookup.js");

test.todo("creates a country currency code lookup for multiple codes");

test("An array of arrays representing country code and currency code should return an object where the keys are country code values are currency code", () => {
const countryCurrencyPairs = [
["IND", "INR"],
["US", "USD"],
["UK", "GBR"],
["CA", "CAD"],
];
expect(createLookup(countryCurrencyPairs)).toEqual({
IND: "INR",
US: "USD",
UK: "GBR",
CA: "CAD",
});
});
/*

Create a lookup object of key value pairs from an array of code pairs
Expand Down
7 changes: 5 additions & 2 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ function parseQueryString(queryString) {
}
const keyValuePairs = queryString.split("&");

for (const pair of keyValuePairs) {
const [key, value] = pair.split("=");
for (const item of keyValuePairs) {
const pair = item.split("=");
const key = pair.shift();
const value = pair.join("=");
if (!key) continue;
queryParams[key] = value;
}
Comment thread
cjyuan marked this conversation as resolved.

Expand Down
46 changes: 44 additions & 2 deletions Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,52 @@
// 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("An empty string should return an empty object", () => {
expect(parseQueryString("")).toEqual({});
});
test("parses querystring parameters with an empty value", () => {
expect(parseQueryString("name=")).toEqual({ name: "" });
});
test("handles querystring parameters that have no = sign", () => {
expect(parseQueryString("flag")).toEqual({ flag: "" });
});
test("parses querystring values containing multiple = characters", () => {
expect(parseQueryString("token=a=b=c=d")).toEqual({ token: "a=b=c=d" });
});
test("handles multiple querystring parameters when one value contains =", () => {
expect(parseQueryString("a=1&equation=x=y+1")).toEqual({
a: "1",
equation: "x=y+1",
});
});
test("ignores pairs with missing key (=b)", () => {
expect(parseQueryString("=b")).toEqual({});
});

test("ignores empty pairs (double &&)", () => {
expect(parseQueryString("a=b&&c=d")).toEqual({
a: "b",
c: "d",
});
});
test("ignores empty key and value (=)", () => {
expect(parseQueryString("=")).toEqual({});
});
test("ignores invalid pairs but keeps valid ones", () => {
expect(parseQueryString("a=b&=&c=d")).toEqual({
a: "b",
c: "d",
});
});
test("keeps key with empty value", () => {
expect(parseQueryString("a=")).toEqual({
a: "",
});
});
14 changes: 13 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
function tally() {}
function tally(arr) {
const object = Object.create(null);
if (!Array.isArray(arr)) {
throw new Error("Invalid input: tally expects an array");
}
if (arr.length === 0) {
return object;
}
arr.forEach((ele) => {
object[ele] = (object[ele] || 0) + 1;
});
return object;
}

module.exports = tally;
14 changes: 13 additions & 1 deletion Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ const tally = require("./tally.js");
* tally(['a', 'a', 'a']), target output: { a: 3 }
* tally(['a', 'a', 'b', 'c']), target output: { a : 2, b: 1, c: 1 }
*/
test("throws an error when the input is not an array", () => {
expect(() => tally("hi")).toThrow();
});
test("returns an empty object when the input array is empty", () => {
expect(tally([])).toEqual({});
});
test("returns the count of each unique item in the array", () => {
expect(tally(["a", "b", "a", "c", "b", "a"])).toEqual({ a: 3, b: 2, c: 1 });
});
test("handles special keys like toString", () => {
expect(tally(["toString", "toString"])).toEqual({ "toString": 2 });
});

// Acceptance criteria:

Expand All @@ -23,7 +35,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");


// Given an array with duplicate items
// When passed to tally
Expand Down
11 changes: 9 additions & 2 deletions Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,28 @@
function invert(obj) {
const invertedObj = {};

for (const [key, value] of Object.entries(obj)) {
invertedObj.key = value;
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 }
//{key:1}

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

// 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?
// Object.entries() returns an array of [key, value] pairs from an object. It is used here to loop through the object and access both the key and value in each iteration.

// d) Explain why the current return value is different from the target output
// The return value is different because the program uses dot notation with a variable instead of bracket notation.
// Also, the object needs to be inverted, so the key and value must be swapped when iterating through the entries.

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

test("returns an object with the key and value swapped when given a single key-value pair", () => {
expect(invert({ a: 1 })).toEqual({ 1: "a" });
});

test("returns an object with all keys and values swapped for multiple key-value pairs", () => {
expect(invert({ a: 1, b: 2 })).toEqual({ 1: "a", 2: "b" });
});
Loading