Skip to content

London | ITP-May-2025 | Adnaan Abo | Module-Data-Groups | Sprint 2 #617

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
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,4 +1,11 @@
// Predict and explain first...
// This code is intended to log the house number from the address object.
// The expected output is: "My house number is 42"
// However, if there are any issues with the code, it may not log correctly.
// The code uses bracket notation to access the houseNumber property,
// but it incorrectly uses an index (0) instead of the property name.
// The correct way to access the property is by using the property name as a string.


// This code should log out the houseNumber from the address object
// but it isn't working...
Expand All @@ -12,4 +19,4 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
console.log(`My house number is ${address["houseNumber"]}`);
8 changes: 7 additions & 1 deletion Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
// Predict and explain first...
// The code below is intended to log out all the property values of the `author` object.
// However, it will not work as expected because the `for...of` loop is designed
// to iterate over iterable objects like arrays or strings, not over objects.
// To fix this, we can use `Object.values()` to get an array of the object's
// property values, which can then be iterated over with a `for...of` loop


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

for (const value of author) {
for (const value of Object.values (author)) {
console.log(value);
}
11 changes: 9 additions & 2 deletions Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
// 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
// However, it does not work as expected.
// It logs the title and serves correctly, but the ingredients are not displayed properly.
// The issue is that the ingredients are being logged as an object instead of each ingredient on a new line.
// To fix this, we need to iterate over the ingredients array and log each ingredient separately


// 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 @@ -11,5 +18,5 @@ const recipe = {
};

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
ingredients:
- ${recipe.ingredients.join("\n- ")}`);
14 changes: 13 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
function contains() {}
function contains(obj, key) {
/**
* Checks if the given array contains the specified value.
* @param {Array} arr - The array to check.
* @param {*} value - The value to search for.
* @returns {boolean} - Returns true if the value is found, otherwise false.
*/
if (obj === null || typeof obj !== "object" || Array.isArray(obj)) {
throw new TypeError("First argument must be a non-null object");
}

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

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 on object with existing property returns true", () => {
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 on object with non-existent property returns false", () => {
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 on invalid parameters throws error", () => {
expect(() => contains([], 'a')).toThrow(TypeError);
});
17 changes: 14 additions & 3 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(pairs) {
/**
* Creates a lookup object from an array of [key, value] pairs.
* @param {Array<Array<string>>} pairs - An array of [countryCode, currencyCode] pairs.
* @returns {Object} - A lookup object mapping country codes to currency codes.
*/
if (!Array.isArray(pairs)) {
throw new TypeError("Argument must be an array of pairs");
}

return pairs.reduce((lookup, [country, currency]) => {
lookup[country] = currency;
return lookup;
}, {});
}
module.exports = createLookup;
15 changes: 14 additions & 1 deletion Sprint-2/implement/lookup.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
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 input = [
["US", "USD"],
["CA", "CAD"],
["JP", "JPY"],
];
const expected = {
US: "USD",
CA: "CAD",
JP: "JPY",
};

expect(createLookup(input)).toEqual(expected);
});

/*

Expand Down
26 changes: 22 additions & 4 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,34 @@
function parseQueryString(queryString) {
const queryParams = {};
if (queryString.length === 0) {
if (typeof queryString !== "string" || 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 [rawKey, ...rest] = pair.split("=");
if (rawKey === "") continue; // skip empty keys

const key = decodeURIComponent(rawKey.replace(/\+/g, " "));
const value = decodeURIComponent(rest.join("=").replace(/\+/g, " "));



if (Object.prototype.hasOwnProperty.call(queryParams, key)) {
// already exists, convert to array or push
if (Array.isArray(queryParams[key])) {
queryParams[key].push(value);
} else {
queryParams[key] = [queryParams[key], value];
}
} else {
queryParams[key] = value;
}
}

return queryParams;
}

module.exports = parseQueryString;
85 changes: 84 additions & 1 deletion Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,91 @@

const parseQueryString = require("./querystring.js")

// Test for parsing query strings with special characters
test("parses querystring values containing =", () => {
expect(parseQueryString("equation=x=y+1")).toEqual({
"equation": "x=y+1",
equation: "x=y 1", // + is space
});
});

// Additional test cases for the query string parser
test("parses multiple key-value pairs", () => {
expect(parseQueryString("a=1&b=2")).toEqual({ a: "1", b: "2" });
});

// Test for empty query string
test("handles empty query string", () => {
expect(parseQueryString("")).toEqual({});
});

// Test for single key-value pair
test("handles string without equals (key only)", () => {
expect(parseQueryString("flag")).toEqual({ flag: "" });
});

// Test for key with empty value
test("handles key with empty value", () => {
expect(parseQueryString("key=")).toEqual({ key: "" });
});

// Test for empty key
test("handles empty key with value", () => {
expect(parseQueryString("=value")).toEqual({});
});

// Test for multiple equals in value
test("handles multiple equals in value", () => {
expect(parseQueryString("token=abc=123=xyz")).toEqual({
token: "abc=123=xyz",
});
});

// Test for URL encoded characters
test("decodes URL encoded characters", () => {
expect(parseQueryString("name=John%20Doe&city=New%20York")).toEqual({
name: "John Doe",
city: "New York",
});
});

// Test for repeated keys
test("handles repeated keys as arrays", () => {
expect(parseQueryString("color=red&color=blue&color=green")).toEqual({
color: ["red", "blue", "green"],
});
});

// Test for empty query string
test("handles trailing ampersand", () => {
expect(parseQueryString("a=1&b=2&")).toEqual({ a: "1", b: "2" });
});

// Test for empty key-value pairs
test("handles multiple empty entries", () => {
expect(parseQueryString("a=1&&b=2&&&")).toEqual({ a: "1", b: "2" });
});

// Test for non-string input (null)
test("handles non-string input (null)", () => {
expect(parseQueryString(null)).toEqual({});
});

// Test for non-string input (undefined)
test("handles non-string input (undefined)", () => {
expect(parseQueryString(undefined)).toEqual({});
});

// Test for whitespace handling
test("handles plus signs as spaces", () => {
expect(parseQueryString("q=search+term")).toEqual({
q: "search term",
});
});

// Test for numeric and boolean-looking values
test("handles numeric and boolean-looking values", () => {
expect(parseQueryString("age=30&valid=false")).toEqual({
age: "30",
valid: "false",
});
});
18 changes: 17 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
function tally() {}
function tally(items) {
if (!Array.isArray(items)) {
throw new Error("Input must be an array");
}

const result = {};

for (const item of items) {
if (result[item]) {
result[item]++;
} else {
result[item] = 1;
}
}

return result;
}

module.exports = tally;
15 changes: 14 additions & 1 deletion Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,29 @@ const tally = require("./tally.js");
// Given a function called tally
// When passed an array of items
// Then it should return an object containing the count for each unique item
test("tally counts unique items in an array", () => {
expect(tally(['a'])).toEqual({ a: 1 });
expect(tally(['a', 'a', 'a'])).toEqual({ a: 3 });
expect(tally(['a', 'a', 'b', 'c'])).toEqual({ a: 2, b: 1, c: 1 });
});

// 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 duplicates correctly", () => {
expect(tally(['apple', 'banana', 'apple'])).toEqual({ apple: 2, banana: 1 });
});

// Given an invalid input like a string
// When passed to tally
// Then it should throw an error
test("tally throws error for non-array input", () => {
expect(() => tally("not an array")).toThrow("Input must be an array");
});
8 changes: 7 additions & 1 deletion Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
// Let's define how invert should work
// Invert is a function that takes an object and returns a new object
// where the keys and values are swapped.
// For example, if invert is called with { a: 1, b: 2 },
// the target output should be { "1": "a", "2": "b" }

// Given an object
// When invert is passed this object
// Then it should swap the keys and values in the object
// For example, invert({ a: 1, b: 2 }) should return { "1": "a", "2": "b" }

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

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

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

return invertedObj;
}

module.exports = invert;
// 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 }
Expand Down
21 changes: 21 additions & 0 deletions Sprint-2/interpret/inverts.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const invert = require("./invert");

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

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

test("inverts string values", () => {
expect(invert({ a: "x", b: "y" })).toEqual({ x: "a", y: "b" });
});

test("overwrites keys when values are not unique", () => {
expect(invert({ a: 1, b: 1 })).toEqual({ 1: "b" }); // last one wins
});

test("inverts an empty object", () => {
expect(invert({})).toEqual({});
});
Loading