Skip to content
8 changes: 5 additions & 3 deletions Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// 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
// but it isn't working.
// The bug was that the code tried to access the object using index [0].
// Objects use property names instead of numeric indexes.

const address = {
houseNumber: 42,
Expand All @@ -12,4 +13,5 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
// Correctly access the houseNumber property
console.log(`My house number is ${address.houseNumber}`);
6 changes: 4 additions & 2 deletions Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// 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
// It was not working because plain objects are not iterable with for...of.
// To loop through the values, we first convert them into an array using Object.values().

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

for (const value of author) {
// Loop through all the values in the object
for (const value of Object.values(author)) {
console.log(value);
}
16 changes: 11 additions & 5 deletions Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
// 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?
// Each ingredient should be logged on a new line.
// The original code tried to print the entire recipe object,
// which resulted in "[object Object]" instead of the ingredients.

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

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
// Print title and serving size
console.log(`${recipe.title} serves ${recipe.serves}`);
console.log("ingredients:");

// Loop through the ingredients array and print each one
for (const ingredient of recipe.ingredients) {
console.log(ingredient);
}
26 changes: 25 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
function contains() {}
/**
* contains()
*
* Checks whether an object contains a specific property.
*
* @param {object} obj - The object to check.
* @param {string} propertyName - The property name we want to check.
* @returns {boolean} True if the property exists, otherwise false.
*/

function contains(obj, propertyName) {
// Validate that obj is actually an object
// and not null or an array
if (obj === null || typeof obj !== "object" || Array.isArray(obj)) {
return false;
}

// Validate propertyName
if (typeof propertyName !== "string" || propertyName.length === 0) {
return false;
}

// Check if the object has the property as its own key
return Object.prototype.hasOwnProperty.call(obj, propertyName);
}

module.exports = contains;
42 changes: 19 additions & 23 deletions Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,28 @@ 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({a: 1, b: 2}, "a") // returns true
E.g. contains({a: 1, b: 2}, "c") // returns false
*/

// Acceptance criteria:

// Given a contains function
// When passed an object and a property name
// Then it should return true if the object contains the property, false otherwise
describe("contains()", () => {
test("returns false for an empty object", () => {
expect(contains({}, "a")).toBe(false);
});

// Given an empty object
// When passed to contains
// Then it should return false
test.todo("contains on empty object returns false");
test("returns true when the property exists", () => {
expect(contains({ a: 1, b: 2 }, "a")).toBe(true);
});

// Given an object with properties
// When passed to contains with an existing property name
// Then it should return true
test("returns false when the property does not exist", () => {
expect(contains({ a: 1, b: 2 }, "c")).toBe(false);
});

// Given an object with properties
// When passed to contains with a non-existent property name
// Then it should return false
test("returns false when given an array", () => {
expect(contains(["a", "b"], "0")).toBe(false);
});

// Given invalid parameters like an array
// When passed to contains
// Then it should return false or throw an error
test("returns false when given null", () => {
expect(contains(null, "a")).toBe(false);
});
});
36 changes: 34 additions & 2 deletions Sprint-2/implement/lookup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
function createLookup() {
// implementation here
/**
* createLookup()
*
* Converts an array of [key, value] pairs into a lookup object.
*
* Example:
* [['US', 'USD'], ['CA', 'CAD']]
*
* Returns:
* { US: 'USD', CA: 'CAD' }
*/

function createLookup(pairs) {
// Ensure the input is an array
if (!Array.isArray(pairs)) {
throw new Error("Expected an array of pairs");
}

const lookup = {};

// Loop through each pair
for (const pair of pairs) {
// Validate that each pair has exactly two values
if (!Array.isArray(pair) || pair.length !== 2) {
throw new Error("Each item must be a [key, value] pair");
}

const [key, value] = pair;

// Add to lookup object
lookup[key] = value;
}

return lookup;
}

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

expect(createLookup(pairs)).toEqual({
US: "USD",
CA: "CAD",
});
});

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

test("overwrites duplicate keys with the last value", () => {
const pairs = [
["US", "USD"],
["US", "USN"],
];

expect(createLookup(pairs)).toEqual({
US: "USN",
});
});

test("throws an error when input is not an array", () => {
expect(() => createLookup("invalid")).toThrow();
});
});
35 changes: 33 additions & 2 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,43 @@
/**
* parseQueryString()
*
* Parses a query string into an object of key-value pairs.
*
* Example:
* parseQueryString("name=Richard&city=Sheffield")
* returns { name: "Richard", city: "Sheffield" }
*/

function parseQueryString(queryString) {
const queryParams = {};
if (queryString.length === 0) {

// Return an empty object if the input is an empty string
if (typeof queryString !== "string" || queryString.length === 0) {
return queryParams;
}

// Split the full query string into key-value pairs
const keyValuePairs = queryString.split("&");

for (const pair of keyValuePairs) {
const [key, value] = pair.split("=");
// Skip empty pairs, for example from a trailing "&"
if (pair === "") {
continue;
}

// Find the position of the first "="
const separatorIndex = pair.indexOf("=");

// If there is no "=" sign, treat it as a key with an empty value
if (separatorIndex === -1) {
queryParams[pair] = "";
continue;
}

// Extract the key and everything after the first "=" as the value
const key = pair.slice(0, separatorIndex);
const value = pair.slice(separatorIndex + 1);

queryParams[key] = value;
}

Expand Down
48 changes: 40 additions & 8 deletions Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
// In the prep, we implemented a function to parse query strings.
// Unfortunately, it contains several bugs!
// 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")
describe("parseQueryString()", () => {
test("parses querystring values containing =", () => {
expect(parseQueryString("equation=x=y+1")).toEqual({
equation: "x=y+1",
});
});

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

test("parses a single key-value pair", () => {
expect(parseQueryString("name=Richard")).toEqual({
name: "Richard",
});
});

test("parses multiple key-value pairs", () => {
expect(parseQueryString("name=Richard&city=Sheffield")).toEqual({
name: "Richard",
city: "Sheffield",
});
});

test("handles a key with an empty value", () => {
expect(parseQueryString("name=")).toEqual({
name: "",
});
});

test("handles a key with no equals sign", () => {
expect(parseQueryString("name")).toEqual({
name: "",
});
});

test("parses querystring values containing =", () => {
expect(parseQueryString("equation=x=y+1")).toEqual({
"equation": "x=y+1",
test("ignores an empty trailing pair", () => {
expect(parseQueryString("name=Richard&")).toEqual({
name: "Richard",
});
});
});
32 changes: 31 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
function tally() {}
/**
* tally()
*
* Counts how many times each item appears in an array.
*
* Example:
* tally(['a','a','b','c'])
* returns { a: 2, b: 1, c: 1 }
*/

function tally(items) {
// Validate input
if (!Array.isArray(items)) {
throw new Error("Expected an array");
}

const counts = {};

// Loop through each item in the array
for (const item of items) {
// If the item already exists in the object, increase the count
if (counts[item]) {
counts[item] += 1;
} else {
// Otherwise initialise it
counts[item] = 1;
}
}

return counts;
}

module.exports = tally;
Loading
Loading