Skip to content
Open
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
13 changes: 11 additions & 2 deletions Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Predict and explain first...

// With the code as written, running the file will print: My house number is undefined
// Why is this happening?
// Because the address object does not have a property with the key 0 - which is what address[0] is trying to access. The houseNumber is a property of the address object, so it should be accessed using dot notation (address.houseNumber) instead of bracket notation with an index (address[0]).
// 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 +14,11 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
console.log(`My house number is ${address.houseNumber}`);
// debug info
console.log("address object:", address);
console.log(
"address.houseNumber (type):",
typeof address.houseNumber,
address.houseNumber
);
3 changes: 2 additions & 1 deletion Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Predict and explain first...
// Running the file will result in a TypeError: author is not iterable. This is because the for...of loop is designed to work with iterable objects (like arrays, strings, etc.), but 'author' is a plain JavaScript object, which is not iterable by default. To iterate over the properties of an object, we can use a for...in loop or Object.values() method instead.

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

for (const value of author) {
for (const value of Object.values(author)) {
console.log(value);
}
13 changes: 10 additions & 3 deletions Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
// Predict and explain first...
// When you run the file as shown, the program will print three lines:
// bruschetta serves 2
// ingredients:
// [object Object]
// This is because when you try to log an object (like the recipe object) in a string, it gets converted to the string "[object Object]". To fix this, we need to access the ingredients property of the recipe object and format it correctly. We can use the join method to join the ingredients array into a string with each ingredient on a new line. Here's how you can fix it:

// This program should log out the title, how many it serves and the ingredients.
// Each ingredient should be logged on a new line
Expand All @@ -10,6 +15,8 @@ const recipe = {
ingredients: ["olive oil", "tomatoes", "salt", "pepper"],
};

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
console.log(`${recipe.title} serves ${recipe.serves}`);
console.log("ingredients:");
for (const ing of recipe.ingredients) {
console.log(`- ${ing}`);
}
12 changes: 11 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
function contains() {}
function contains(obj, prop) {
// Reject null/undefined, arrays, and non-objects
if (obj === null || obj === undefined) return false;
if (Array.isArray(obj)) return false;
if (typeof obj !== "object") return false;

// prop must be provided
if (prop === undefined || prop === null) return false;

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

module.exports = contains;
20 changes: 17 additions & 3 deletions Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,30 @@ as the object doesn't contains a key of 'c'
// 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({}, "anyKey")).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 return true", () => {
const obj = { a: 1, b: 2 };
expect(contains(obj, "a")).toBe(true);
expect(contains(obj, "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 on object with non-existent property return false", () => {
const obj = { a: 1, b: 2 };
expect(contains(obj, "c")).toBe(false);
});

// Given invalid parameters like an array
// When passed to contains
// Then it should return false or throw an error
test("contains on invalid parameters returns false", () => {
expect(contains([], "a")).toBe(false);
expect(contains(null, "a")).toBe(false);
expect(contains(undefined, "a")).toBe(false);
});
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
// Create a lookup object from an array of [key, value] pairs
function createLookup(pairs) {
if (!Array.isArray(pairs))
throw new TypeError("Input must be an array of pairs");
return pairs.reduce((acc, pair) => {
if (!Array.isArray(pair) || pair.length < 2) return acc;
const [key, value] = pair;
acc[key] = value;
return acc;
}, {});
}

module.exports = createLookup;
9 changes: 8 additions & 1 deletion Sprint-2/implement/lookup.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
const createLookup = require("./lookup.js");

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

/*

Expand Down
8 changes: 7 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
function tally() {}
function tally(items) {
if (!Array.isArray(items)) throw new Error("Invalid input");
return items.reduce((acc, it) => {
acc[it] = (acc[it] || 0) + 1;
return acc;
}, {});
}

module.exports = tally;
17 changes: 17 additions & 0 deletions Sprint-2/interpret/invert.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const invert = require("./invert.js");

test("inverts single key", () => {
expect(invert({ a: 1 })).toEqual({ 1: "a" });
});

test("inverts multiple keys", () => {
expect(invert({ a: 1, b: 2 })).toEqual({ 1: "a", 2: "b" });
});

test("later keys win when values are duplicated", () => {
expect(invert({ a: 1, b: 1 })).toEqual({ 1: "b" });
});

test("empty object returns empty object", () => {
expect(invert({})).toEqual({});
});
47 changes: 27 additions & 20 deletions Sprint-2/stretch/till.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
// totalTill takes an object representing coins in a till
// Given an object of coins (e.g. { '1p': 10, '50p': 4 })
// it returns the total amount as a formatted pounds string (e.g. '£4.40').

// Given an object of coins
// When this till object is passed to totalTill
// Then it should return the total amount in pounds
/*
a) What is the target output when totalTill is called with the till object?
- The target output is "£4.40". (1p×10 = 10p, 5p×6 = 30p, 50p×4 = 200p, 20p×10 = 200p; total = 440p → £4.40)

b) Why do we need to use Object.entries inside the for...of loop in this function?
- Object.entries(till) yields pairs [key, value], so we get the coin label (e.g. "50p") and the quantity
in one step. That makes the loop concise and avoids extra lookups like till[key]. It also restricts
iteration to the object's own enumerable properties.

c) What does coin * quantity evaluate to inside the for...of loop?
- As written (if you don't convert the coin label), coin is a string like "1p" and coin * quantity
evaluates to NaN. You must parse the numeric pence value from the coin label before multiplying.

d) Write a test for this function to check it works and then fix the implementation of totalTill
- Example Jest tests live in `Sprint-2/stretch/till.test.js` and assert the sample till returns
"£4.40" and an empty till returns "£0.00". The implementation should parse labels (strip trailing
'p') and accumulate pence, then format as pounds to two decimal places.
*/

function totalTill(till) {
let total = 0;

for (const [coin, quantity] of Object.entries(till)) {
total += coin * quantity;
// Convert a label like '50p' -> 50 (pence)
const pence = parseInt(String(coin).replace(/p$/i, ""), 10);
if (Number.isFinite(pence) && typeof quantity === "number") {
total += pence * quantity;
}
}

return `£${total / 100}`;
return `£${(total / 100).toFixed(2)}`;
}

const till = {
"1p": 10,
"5p": 6,
"50p": 4,
"20p": 10,
};
const totalAmount = totalTill(till);

// a) What is the target output when totalTill is called with the till object

// b) Why do we need to use Object.entries inside the for...of loop in this function?

// c) What does coin * quantity evaluate to inside the for...of loop?

// d) Write a test for this function to check it works and then fix the implementation of totalTill
module.exports = totalTill;
10 changes: 10 additions & 0 deletions Sprint-2/stretch/till.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const totalTill = require("./till");

test("returns correct total for sample till", () => {
const till = { "1p": 10, "5p": 6, "50p": 4, "20p": 10 };
expect(totalTill(till)).toBe("£4.40");
});

test("returns £0.00 for empty till", () => {
expect(totalTill({})).toBe("£0.00");
});
Loading