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
7 changes: 6 additions & 1 deletion Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
console.log(`My house number is ${address.houseNumber}`);


/* BUG: address[0] is undefined because objects aren't indexed by number.
FIX: useed the property name: address.houseNumber
console.log(`My house number is ${address.houseNumber}`);
10 changes: 9 additions & 1 deletion Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ const author = {
alive: true,
};

for (const value of author) {
for (const value of Object.values(author)) {
console.log(value);
}


/* BUG: Objects are not iterable, so `for...of author` throws TypeError.
FIX: Used Object.values(author) to loop over the property values.
for (const value of Object.values(author)) {
console.log(value);
}
*/
8 changes: 7 additions & 1 deletion Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ const recipe = {

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
${recipe.ingredients.join("\n")}`);



// BUG: ${recipe} logs out the whole object as "[object Object]".
// FIX: print title/serves from their properties, and ingredients with join("\n")
// so each item is on its own line.
8 changes: 7 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
function contains() {}
function contains(obj, prop) {
if (obj === null || typeof obj !== "object" || Array.isArray(obj)) {
throw new Error("contains requires an object");
}
return Object.hasOwn(obj, prop);
}

module.exports = contains;

34 changes: 33 additions & 1 deletion Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,50 @@ 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({}, "a")).toBe(false);
});

// Given an object with properties
// When passed to contains with an existing property name
// Then it should return true
test("returns true when property exists", () => {
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("returns false when property does not exist", () => {
expect(contains({ a: 1, b: 2 }, "c")).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("throws an error when given an array", () => {
expect(() => contains([3, 4, 5], "a")).toThrow(
new Error("contains requires an object")
);
});

test("throws an error when given a null", () => {
expect(() => contains(null, "a")).toThrow(
new Error("contains requires an object")
);
});

test("throws an error when given a string", () => {
expect(() => contains("hellow", "a")).toThrow(
new Error("contains requires an object")
);
});

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

function createLookup(pairs) {
const lookup = {};
for (const [country, currency] of pairs) {
lookup[country] = currency;
}
return lookup;
}

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", () => {
expect(
createLookup([
["US", "USD"],
["CA", "CAD"],
])
).toEqual({ US: "USD", CA: "CAD" });
});

/*

Expand Down
34 changes: 32 additions & 2 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,46 @@
function parseQueryString(queryString) {
const queryParams = {};

if (queryString.length === 0) {
return queryParams;
}

const keyValuePairs = queryString.split("&");

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

const eqIndex = pair.indexOf("=");
let key;
let value;

if (eqIndex === -1) {
key = pair;
value = "";
} else {
key = pair.slice(0, eqIndex);
value = pair.slice(eqIndex + 1);
}

key = decodeURIComponent(key.replaceAll("+", " "));
value = decodeURIComponent(value.replaceAll("+", " "));

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

return queryParams;
}

module.exports = parseQueryString;


11 changes: 10 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
function tally() {}
function tally(items) {
if (!Array.isArray(items)) {
throw new Error("tally requires an array");
}
const counts = {};
for (const item of items) {
counts[item] = (counts[item] ?? 0) + 1;
}
return counts;
}

module.exports = tally;
19 changes: 18 additions & 1 deletion Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,29 @@ 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 on an array with duplicate items, return counts for each unique item", () => {
expect(tally(["a", "a", "c", "b", "c", "b", "c", "e"])).toEqual({ a: 2, c: 3, b: 2, e: 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 on a string, thows an error", () => {
expect(() => tally("hello")).toThrow(new Error("tally requires an array"));
});

test("tally on a number, thows an error", () => {
expect(() => tally(10)).toThrow(new Error("tally requires an array"));
});

test("tally on no argument, thows an error", () => {
expect(() => tally()).toThrow(new Error("tally requires an array"));
});

38 changes: 30 additions & 8 deletions Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,46 @@

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

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

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

return invertedObj;
}

// a) What is the current return value when invert is called with { a : 1 }
/*
a) Current return value for invert({ a: 1 }) with the OLD bug:
{ key: 1 }

b) Current return value for invert({ a: 1, b: 2 }) with the OLD bug:
{ key: 2 }
(each loop overwrites the same property named "key"; last value wins)

c) Target return value for invert({ a: 1, b: 2 }):
{ "1": "a", "2": "b" }

Object.entries returns an array of [key, value] pairs for the object.
It is needed so the loop can read both the key and the value of each property.

d) invertedObj.key always writes to a property literally named "key".
It ignores the loop variable `key`, so keys and values are never swapped,
and earlier entries are overwritten. The result is a new object with only
that one "key" property — not the original object, and not an inverted map.

e) Fixed by using invertedObj[value] = key (bracket notation with the value
as the new key). Tests in invert.test.js prove the fix.
*/







// 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!)
21 changes: 21 additions & 0 deletions Sprint-2/stretch/count-words.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,24 @@

3. Order the results to find out which word is the most common in the input
*/

function countWords(str) {
const counts = {};
if (str.trim() === "") {
return counts;
}

const words = str
.toLowerCase()
.replaceAll(/[.,!?]/g, "")
.split(/\s+/)
.filter((w) => w.length > 0);

for (const word of words) {
counts[word] = (counts[word] ?? 0) + 1;
}

return counts;
}

module.exports = countWords;
24 changes: 15 additions & 9 deletions Sprint-2/stretch/mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@

// The tests must still pass after your refactor. Run them again to check.

function calculateMode(list) {
// check the input is a non-empty array of numbers
function assertValidNumbers(list) {
if (!Array.isArray(list)) {
throw new Error("calculateMode requires an array of numbers");
}
Expand All @@ -25,25 +24,32 @@ function calculateMode(list) {
throw new Error("calculateMode requires an array of numbers");
}
}
}

// track frequency of each value
let freqs = new Map();

for (let num of list) {
function trackFrequencies(list) {
const freqs = new Map();
for (const num of list) {
freqs.set(num, (freqs.get(num) || 0) + 1);
}
return freqs;
}

// Find the value with the highest frequency
function findMode(freqs) {
let maxFreq = 0;
let mode;
for (let [num, freq] of freqs) {
for (const [num, freq] of freqs) {
if (freq > maxFreq) {
mode = num;
maxFreq = freq;
}
}

return mode;
}

function calculateMode(list) {
assertValidNumbers(list);
const freqs = trackFrequencies(list);
return findMode(freqs);
}

module.exports = calculateMode;
Loading
Loading