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
9 changes: 8 additions & 1 deletion Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Predict and explain first...

// I guess the bug could be related to how to access property in an object.
// address[0] may not be able to access houseNumber as a key
// if we change address[0] to address['houseNumber'] or address.houseNumber, it may resolve the problem.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would it not be able to access via address[0]?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

address[0] can't access houseNumber because address is an object. When address[0] is read, it is looking for a key named 0 inside the address, which leads to "undefined" since the key 0 doesn't exist.


// 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 +16,7 @@ const address = {
postcode: "XYZ 123",
};

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

// After trying both address["houseNumber"] and address.houseNumber,
// console.log can successfully print out the statement with right number accordingly.
11 changes: 8 additions & 3 deletions Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Predict and explain first...

// I guess the problem could be induced by console.log(value), as each value is a key: value pair, but we only need the values.

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

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we wanted to console.log each property at a time?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we wanted to print each property line by line, we could get the properties in an array by Object.keys(author), map each property, and print them out one by one.

Object.keys(author).map((key) => console.log(key));


// After running the code, I got TypeError: author is not iterable
// so, the actual problem is an object cannot be iterated in for loop.
// To print out all the values in author, I attempted Object.values(author)
// which return all the values in an array [ 'Zadie', 'Smith', 'writer', 40, true ]
9 changes: 8 additions & 1 deletion Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Predict and explain first...

// For the first glance, I guess I have to do a looping in recipe.ingredients
// Before any changes, ingredients: ${recipe} would be print out the whole object.
// for print out each ingredient on a new line, I may have to use \n

// 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 @@ -12,4 +16,7 @@ const recipe = {

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

// After the first attempt, I realized that I can't use a for loop inside ${}
// so, I tried map function to get each ingredient and added \n between them.
7 changes: 6 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
function contains() {}
function contains(obj, targetProperty) {
if (typeof obj !== "object" || Array.isArray(obj) || obj === null) {
throw new Error("Input should be an object");
}
return Object.keys(obj).includes(targetProperty);
}

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

// Given an object with properties
// When passed to contains with an existing property name
// Then it should return true
test("contains on an object with properties, passed an existing property name, returns true", () => {
const obj = { a: 1, b: 2 };
expect(contains(obj, "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 on an object properties, passed a non-existing property name, returns 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("Given invalid parameters returns an error", () => {
const errorMessage = "Input should be an object";
expect(() => contains([{ a: 1 }, { b: 2 }], "a")).toThrow(errorMessage);
expect(() => contains(null, "a")).toThrow(errorMessage);
expect(() => contains(undefined, "a")).toThrow(errorMessage);
expect(() => contains(true, "a")).toThrow(errorMessage);
expect(() => contains(false, "a")).toThrow(errorMessage);
});
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
function createLookup(arr) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking really good

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah~

if (!Array.isArray(arr)) {
throw new Error("Input should be an array of arrays of code pairs");
}

const validArray = arr.every(
(element) => Array.isArray(element) && element.length === 2
);
if (!validArray) {
throw new Error("Input should be an array of arrays of code pairs");
}

return Object.fromEntries(arr);
}

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

test.todo("creates a country currency code lookup for multiple codes");
const errorMessage = "Input should be an array of arrays of code pairs";

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

test("Given invalid parameter, return error message", () => {
expect(() => createLookup("Invalid input")).toThrow(errorMessage);
expect(() => createLookup({ a: "b" })).toThrow(errorMessage);
expect(() => createLookup(123)).toThrow(errorMessage);
expect(() => createLookup(null)).toThrow(errorMessage);
expect(() => createLookup(undefined)).toThrow(errorMessage);
expect(() => createLookup(true)).toThrow(errorMessage);
});

test("creates a country currency code lookup for multiple codes", () => {
const validInput = [
["US", "USD"],
["CA", "CAD"],
["JP", "JPY"],
];
const target = {
US: "USD",
CA: "CAD",
JP: "JPY",
};
expect(createLookup(validInput)).toStrictEqual(target);
});

test("Given an array with empty arrays, returns error message", () => {
const emptyArrays = [[], []];
expect(() => createLookup(emptyArrays)).toThrow(errorMessage);
const emptyArrays2 = [[], [], ["JP", "JPY"]];
expect(() => createLookup(emptyArrays2)).toThrow(errorMessage);
});

test("Given an array with objects, return error message", () => {
const objectsArray = [{ a: "b" }, { c: "d" }];
expect(() => createLookup(objectsArray)).toThrow(errorMessage);
});

test("Given an array mixed with valid arrays and invalid input, returns error message", () => {
const mixInput = [["US", "USD"], { CA: "CAD" }, ["JP", "JPY"]];
expect(() => createLookup(mixInput).toThrow(errorMessage));
const mixInput2 = [["US", "USD"], undefined, ["JP", "JPY"]];
expect(() => createLookup(mixInput2).toThrow(errorMessage));
const mixInput3 = [["US", "USD"], null, ["JP", "JPY"]];
expect(() => createLookup(mixInput3).toThrow(errorMessage));
});

test("Given an array with arrays but some are displaced, return error message", () => {
const missFormatInput = [["US", "USD"], ["CA"], ["JP", "JPY", "CAD"]];
expect(() => createLookup(missFormatInput)).toThrow(errorMessage);
});

test("Given an array with array of non-strings, return accordingly", () => {
const nonStringInput = [
[undefined, true],
["CA", "CAD"],
["JP", null],
[123, 456],
];
const target = {
undefined: true,
CA: "CAD",
JP: null,
123: 456,
};
expect(createLookup(nonStringInput)).toStrictEqual(target);
});

/*

Expand Down
20 changes: 19 additions & 1 deletion Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,28 @@ function parseQueryString(queryString) {
if (queryString.length === 0) {
return queryParams;
}

if (queryString[0] === "?") {
queryString = queryString.slice(1);
}

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

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

const indexToSlice = pair.indexOf("=");

let key;
let value;

if (indexToSlice === -1) {
key = pair;
value = "";
} else {
key = decodeURIComponent(pair.slice(0, indexToSlice));
value = decodeURIComponent(pair.slice(indexToSlice + 1));
}
queryParams[key] = value;
}

Expand Down
42 changes: 40 additions & 2 deletions Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,48 @@
// 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("Single Key with No Value", () => {
expect(parseQueryString("key=")).toEqual({ key: "" });
});

test("Key Without = Sign", () => {
expect(parseQueryString("aloneKey")).toEqual({ aloneKey: "" });
});

test("Multiple Parameters", () => {
expect(parseQueryString("name=Brian&age=35&country=UK")).toEqual({
name: "Brian",
age: "35",
country: "UK",
});
});

test("Duplicate Keys", () => {
expect(parseQueryString("tag=js&tag=node&tag=react")).toEqual({
tag: "react",
});
});

test("Values Containing Special Characters", () => {
expect(parseQueryString("query=a%20b%26c%3Dd")).toEqual({ query: "a b&c=d" });
expect(parseQueryString("a=1&b=2&")).toEqual({ a: "1", b: "2" });
});

test("Starting with ?", () => {
expect(parseQueryString("?foo=bar")).toEqual({ foo: "bar" });
});

test("Mixed encoded and plain parts", () => {
expect(parseQueryString("message=Hello%20World%21&debug=true")).toEqual({
message: "Hello World!",
debug: "true",
});
});
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() {}
function tally(arr) {
if (!Array.isArray(arr)) {
throw new Error("Input should be an array");
}
if (arr.length === 0) {
return {};
}

arr = arr.flat();

let result = {};
for (element of arr) {
const key =
typeof element === "object" ? JSON.stringify(element) : String(element);

result[key] = (result[key] || 0) + 1;
}

return result;
}

module.exports = tally;
61 changes: 60 additions & 1 deletion Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,71 @@ 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("Given an array with duplicate item, it should return counts for each unique item", () => {
expect(tally(["a", "a", "a"])).toEqual({ a: 3 });
expect(tally(["a", "a", "b", "c"])).toEqual({ a: 2, b: 1, c: 1 });
expect(tally(["a", "a", "b", "c", "a", "a", "b", "c"])).toEqual({
a: 4,
b: 2,
c: 2,
});
});

// Given an invalid input like a string
// When passed to tally
// Then it should throw an error
const errorMessage = "Input should be an array";
test("Given a non array input, it should return an error message", () => {
expect(() => tally({ a: "b" })).toThrow(errorMessage);
expect(() => tally("a")).toThrow(errorMessage);
expect(() => tally(123)).toThrow(errorMessage);
expect(() => tally(null)).toThrow(errorMessage);
expect(() => tally(undefined)).toThrow(errorMessage);
expect(() => tally(true)).toThrow(errorMessage);
});

test("Given an array with numbers, it should return counts for each number", () => {
expect(tally([1, 1, 1])).toEqual({ 1: 3 });
expect(tally([12, 12, 12])).toEqual({ 12: 3 });
expect(tally([1, 12, 123])).toEqual({ 1: 1, 12: 1, 123: 1 });
expect(tally([1, 12, 123, 1, 12, 123])).toEqual({ 1: 2, 12: 2, 123: 2 });
});

test("Given an array with mixed items, it should return counts for each item", () => {
expect(
tally(["a", 1, true, null, undefined, "a", 1, true, null, undefined])
).toEqual({
a: 2,
1: 2,
true: 2,
null: 2,
undefined: 2,
});
});

test("Given an array with arrays, it should return counts for each item", () => {
expect(tally([1, [1, 1]])).toEqual({ 1: 3 });
expect(tally(["a", ["a", "a"]])).toEqual({ a: 3 });
});

test("Given an array with objects, it should return counts for each object", () => {
expect(
tally([
{ a: 1, b: 2, c: 3 },
{ a: 1, b: 2, c: 3 },
])
).toEqual({ '{"a":1,"b":2,"c":3}': 2 });
});

test("Given an array mixed with arrays and objects, it should return counts for each item", () => {
expect(
tally([{ a: 1, b: 2, c: 3 }, "a", { a: 1, b: 2, c: 3 }, ["a", "a"]])
).toEqual({ '{"a":1,"b":2,"c":3}': 2, a: 3 });
});
Loading