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
6 changes: 3 additions & 3 deletions Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
// 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

// Create an object that holds address details
const address = {
houseNumber: 42,
street: "Imaginary Road",
Expand All @@ -12,4 +11,5 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
// Access the houseNumber property using dot notation
console.log(`My house number is ${address.houseNumber}`);
22 changes: 20 additions & 2 deletions Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,8 +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

// Create an object that holds author details
const author = {
firstName: "Zadie",
lastName: "Smith",
Expand All @@ -11,6 +11,24 @@ const author = {
alive: true,
};

for (const value of author) {
// Use Object.values() to get an array of all values, then loop over them
for (const value of Object.values(author)) {
console.log(value);
}

/*
## Explanation of the Fix

- Object.values(author) is a built-in function that takes an object and returns an array containing only the values. In our case it returns: ["Zadie", "Smith", "writer", 40, true]
- Since the result is now an array, for...of works correctly and goes through each value one by one.

## How to Test in the Terminal
node author.js

Expected output:
Zadie
Smith
writer
40
true
*/
30 changes: 26 additions & 4 deletions Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,36 @@

// 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?

// Create an object that holds recipe details
const recipe = {
title: "bruschetta",
serves: 2,
ingredients: ["olive oil", "tomatoes", "salt", "pepper"],
};

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
// Log the title and how many it serves
console.log(`${recipe.title} serves ${recipe.serves}`);

// Log each ingredient on a new line using a loop
for (const ingredient of recipe.ingredients) {
console.log(ingredient);
}
Comment on lines +17 to +19
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 code works.

Here is an alternative worth exploring:
Since ingredient values are separated by '\n' in the output, we could also use
Array.prototype.join() to construct the equivalent string and then output the resulting string.


/*
## Brief Explanation

- recipe.ingredients is an array (a numbered list) containing four items.
- for...of works on arrays, so it goes through each item one by one.
- Each time through the loop, the variable ingredient holds the current item, and console.log prints it on its own line.

## How to Test
node recipe.js

Expected output:
bruschetta serves 2
olive oil
tomatoes
salt
pepper
*/
11 changes: 10 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
function contains() {}
// Check if an object contains a specific property
function contains(object, propertyName) {
// If the input is not a valid object or is an array, return false
if (typeof object !== "object" || object === null || Array.isArray(object)) {
return false;
}

// Use hasOwnProperty to check if the key exists in the object
return object.hasOwnProperty(propertyName);
}

module.exports = contains;
13 changes: 12 additions & 1 deletion Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,27 @@ 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({}, "a")).toBe(false);
});

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

// Given an object with properties
// When passed to contains with a non-existent property name
// Then it should return false
test("contains returns false for a non-existent property", () => {
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 returns false when passed an array instead of an object", () => {
expect(contains(["a", "b"], "0")).toBe(false);
});
Comment on lines +44 to +46
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.

This test does not yet confirm that the function correctly returns false when the first argument is an array.
This is because contains(["a", "b"], "a") could also return false simply because "a" is not a key of the array.

Arrays are objects, with their indices acting as keys. A proper test should use a valid
key to ensure the function returns false specifically because the input is an array, not because the key is missing.

15 changes: 13 additions & 2 deletions Sprint-2/implement/lookup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
function createLookup() {
// implementation here
// Take an array of pairs and return a lookup object
function createLookup(pairs) {
// Create an empty object to store the results
const lookup = {};

// Loop through each pair in the array
for (const pair of pairs) {
// pair[0] is the key (country code), pair[1] is the value (currency code)
lookup[pair[0]] = pair[1];
}

// Return the completed lookup object
return lookup;
}

module.exports = createLookup;
28 changes: 26 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,29 @@ It should return:
'CA': 'CAD'
}
*/

// Given multiple country-currency pairs
// When createLookup is called
// Then it should return an object with country codes as keys and currency codes as values
test("creates a country currency code lookup for multiple codes", () => {
const pairs = [
["US", "USD"],
["CA", "CAD"],
["GB", "GBP"],
];
expect(createLookup(pairs)).toEqual({ US: "USD", CA: "CAD", GB: "GBP" });
});

// Given a single country-currency pair
// When createLookup is called
// Then it should return an object with that one key-value pair
test("creates a lookup for a single country-currency pair", () => {
expect(createLookup([["EG", "EGP"]])).toEqual({ EG: "EGP" });
});

// Given an empty array
// When createLookup is called
// Then it should return an empty object
test("returns an empty object for an empty array", () => {
expect(createLookup([])).toEqual({});
});
26 changes: 24 additions & 2 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
// Parse a query string into an object of key-value pairs
function parseQueryString(queryString) {
// Create an empty object to store the results
const queryParams = {};

// If the input is empty, return the empty object
if (queryString.length === 0) {
return queryParams;
}

// Split the string by "&" to get each key=value pair
const keyValuePairs = queryString.split("&");

for (const pair of keyValuePairs) {
const [key, value] = pair.split("=");
queryParams[key] = value;
// Skip empty pairs caused by double ampersands like "key1=value1&&key2=value2"
if (pair === "") {
continue;
}

// Find the position of the FIRST "=" only
const firstEqualIndex = pair.indexOf("=");

// If there is no "=", the whole pair is the key with an empty value
if (firstEqualIndex === -1) {
queryParams[pair] = "";
} else {
// Everything before the first "=" is the key
const key = pair.slice(0, firstEqualIndex);
// Everything after the first "=" is the value (may contain more "=" signs)
const value = pair.slice(firstEqualIndex + 1);
queryParams[key] = value;
}
Comment on lines +20 to +32
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.

Does your function return the value you expect from the following function call?

parseQueryString("key1=value1&&key2=value2")

}

return queryParams;
Expand Down
21 changes: 20 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
function tally() {}
// Count the frequency of each item in an array
function tally(items) {
// If the input is not an array, throw an error
if (!Array.isArray(items)) {
throw new Error("Input must be an array");
}

// Create an empty object to store the counts
const counts = Object.create(null);

// Loop through each item in the array
for (const item of items) {
// If this item already exists in counts, add 1 to it
// If it does not exist yet, start at 1
counts[item] = (counts[item] || 0) + 1;
}

// Return the object with all the counts
return counts;
}

module.exports = tally;
17 changes: 16 additions & 1 deletion Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,27 @@ 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 correctly", () => {
expect(tally(["a", "a", "b", "c", "b"])).toEqual({ a: 2, b: 2, c: 1 });
});

// Given an invalid input like a string
// When passed to tally
// Then it should throw an error
test("tally throws an error when passed a string", () => {
expect(() => tally("hello")).toThrow();
});

// Given an array containing items named "toString"
// When passed to tally
// Then it should count them correctly without prototype interference
test("tally correctly counts items named toString", () => {
expect(tally(["toString", "toString"])).toEqual({ toString: 2 });
});
44 changes: 30 additions & 14 deletions Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,45 @@
// 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"}
// E.g. invert({x: 10, y: 20}), target output: {"10": "x", "20": "y"}

/*
--- Answers to the Interpret Questions ---

a) What is the return value of invert({a: 1}) in the old broken code?
Answer: It returned { key: 1 } because dot notation (.key) creates a literal string key named "key".

b) What is the return value of invert({a: 1, b: 2}) in the old broken code?
Answer: It returned { key: 2 } because the literal property "key" is overwritten in the second loop iteration.

c) What is the target return value of invert({a: 1, b: 2})?
Answer: The target return value is { "1": "a", "2": "b" }.

c-continued) What does Object.entries do? Why is it needed here?
Answer: Object.entries(obj) converts the object into an array of key-value pairs, like [["a", 1], ["b", 2]]. It is needed because we cannot use a 'for...of' loop directly on a standard object.

d) Why is the current return value different from the target?
Answer: Because the old code used invertedObj.key (a hardcoded key name) instead of assigning the dynamic value as the new key.

e) How can you fix it?
Answer: By using bracket notation to set the 'value' as the new dynamic key, and assigning the 'key' as its value: invertedObj[value] = key;
*/

// Swap keys and values in an object
function invert(obj) {
// Create an empty object to store the swapped pairs
const invertedObj = {};

// Loop through each [key, value] pair in the original object
for (const [key, value] of Object.entries(obj)) {
invertedObj.key = value;
// Use the VALUE as the new key, and the KEY as the new value
invertedObj[value] = key;
}

// Return the inverted object
return invertedObj;
}

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

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

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

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

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

// e) Fix the implementation of invert (and write tests to prove it's fixed!)
module.exports = invert;
Loading
Loading