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
4 changes: 3 additions & 1 deletion Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// Predict and explain first...
// objects are not arrays and don`t have ordinal numbers (0, 1, 2)
// their data is accessed by keys (names)

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

console.log(`My house number is ${address[0]}`);
console.log(`My house number is ${address.houseNumber}`);
10 changes: 7 additions & 3 deletions Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// Predict and explain first...
// for...of loop
// designed for arrays
// doesn't work with objects
// objects in JS are not iterable

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

for (const value of author) {
console.log(value);
}
for (const value of Object.values(author)) {
console.log (value);
}
3 changes: 2 additions & 1 deletion Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Predict and explain first...
// ${recipe} object inserted in console.log.

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

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
${recipe.ingredients.join(`\n`)}`);
10 changes: 9 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
function contains() {}
function contains(obj, key) {
// check if obj is actually an obj not null or array
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
return false;
}

// use 'in' operator to check if key exists in object
return key in obj;
}

module.exports = contains;
24 changes: 23 additions & 1 deletion Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
const contains = require("./contains.js");

test ("returns false for empty object", () => {
expect(contains({}, 'a')).toBe(false);
});

test("returns true when property exists", () => {
expect(contains({a: 1, b: 2}, 'a')).toBe(true);
expect(contains({name: 'John'}, 'name')).toBe(true);
});

test("returns false when property doesn't exist", () => {
expect(contains({a: 1, b: 2}, 'c')).toBe(false);
expect(contains({x: 10}, 'y')).toBe(false);
});

test("returns false for invalid input (array)", () => {
expect(contains([1, 2, 3], '0')).toBe(false);
});

test("returns false for null or undefined", () => {
expect(contains(null, 'a')).toBe(false);
expect(contains(undefined, 'a')).toBe(false);
});

/*
Implement a function called contains that checks an object contains a
particular property
Expand All @@ -20,7 +43,6 @@ 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");

// Given an object with properties
// When passed to contains with an existing property name
Expand Down
42 changes: 41 additions & 1 deletion Sprint-2/implement/lookup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
function createLookup() {
/**
* creates a lookup object from an array of key-value pairs
*
* @param {Array<Array<string>>} pairs - array of [key, value] pairs
* @returns {Object} - lookup object with keys mapped to values
* @throws {Typeerror} - if input is not a valid array of pairs
*
* @example
* createLookup([['US', 'USD'], ['CA', 'CAD']])
* // Returns: { US: 'USD', CA: 'CAD' }
*/
function createLookup(pairs) {
// implementation here
// 1. validate input
if (!Array.isArray(pairs)) {
throw new TypeError('Input must be an array');
}

// 2. initialize empty result object
const lookup = {};

// 3. iterate through each pair
for (const pair of pairs) {
// 3a. validate each pair: must be an array with 2 elements
if (!Array.isArray(pair) || pair.length !== 2) {
throw new TypeError('Each pair must be an array of exactly 2 elements');
}

// 3b. extract key and value
const [key, value] = pair; // destructing assignment

// 3c. validate key must be a string
if (typeof key !== 'string') {
throw new TypeError('Keys must be stings');
}

// 3d. add to lookup object
lookup[key] = value;
}

// 4. return the constructed lookup object
return lookup;
}

module.exports = createLookup;
54 changes: 52 additions & 2 deletions Sprint-2/implement/lookup.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,59 @@
const createLookup = require("./lookup.js");

test.todo("creates a country currency code lookup for multiple codes");
// happy path: valid input
test("creates a lookup object from valid pairs", () => {
const pairs = [['US', 'USD'], ['CA', 'CAD']];
const result = createLookup(pairs);

/*
expect(result).toEqual({
US: 'USD',
CA: 'CAD'
});
});

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

// single pair
test("handle single pair correctly", () => {
const pairs = [['UK', 'GBP']];
expect(createLookup(pairs)).toEqual({ UK: 'GBP' });
});

// duplicates keys (last value wins)
test("handle duplicates keys by keeping last value", () => {
const pairs = [['US', 'USD'], ['US', 'EUR']];
expect(createLookup(pairs)).toEqual({ US: 'EUR' });
});

// error handling: not an array
test("throws TypeError when input is not an array", () => {
expect(() => createLookup(null)).toThrow(TypeError);
expect(() => createLookup('invalid')).toThrow(TypeError);
expect(() => createLookup({a: 1})).toThrow(TypeError);
});

// error handling: invalid pair (not array)
test("throws TypeError when pair is not an array", () => {
const pairs = [['US', 'USD'], 'invalid'];
expect(() => createLookup(pairs)).toThrow(TypeError);
});

// error handling: invalid pair (wrong length)
test("throws TypeError when pair has wrong length", () => {
const pairs = [['US', 'USD'], ['CA']]; // missing value
expect(() => createLookup(pairs)).toThrow(TypeError);
});

// error handling: non-string key
test("throws TypeError when key is not a string", () => {
const pairs = [[42, 'USD']]; // number as key
expect(() => createLookup(pairs)).toThrow(TypeError);
});

/**
Create a lookup object of key value pairs from an array of code pairs

Acceptance Criteria:
Expand Down
18 changes: 16 additions & 2 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
function parseQueryString(queryString) {
const queryParams = {};
if (queryString.length === 0) {
if (!queryString || queryString.length === 0) {
return queryParams;
}

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

for (const pair of keyValuePairs) {
const [key, value] = pair.split("=");
// divide only by the first sign '='
const firstEqualIndex = pair.indexOf("=");

// If there is no '=',
// consider it a key without a value (or skip it)
if (firstEqualIndex === -1) {
queryParams[pair] = "";
continue;
}

const key = pair.slice(0, firstEqualIndex);
const value = pair.slice(firstEqualIndex +1);

// const [key, value] = pair.split("=");
queryParams[key] = value;
}

Expand Down
19 changes: 19 additions & 0 deletions Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,22 @@ test("parses querystring values containing =", () => {
"equation": "x=y+1",
});
});

test("handles keys without values", () => {
expect(parseQueryString("name")).toEqual({ name: ""});
});

test("handles empty values", () => {
expect(parseQueryString("name=")).toEqual({ name: "" });
});

test("parses simple query string", () => {
expect(parseQueryString("name=John&age=30")).toEqual({
name: "John",
age: "30",
});
});

test("returns empty objects for empty strings", () => {
expect(parseQueryString("")).toEqual({});
});
13 changes: 12 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
function tally() {}
function tally(items) {
if(!Array.isArray(items)) {
throw new TypeError("Input must be an array");
}

const counts = {};
for (const item of items) {
// for no key, initialize 0 and add 1
counts[item] = (counts[item] || 0) + 1;
}
return counts;
}

module.exports = tally;
16 changes: 15 additions & 1 deletion Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
const tally = require("./tally.js");

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

test("tally counts frequency of 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});;
});

test("tally throws TypeError on invalid input", () => {
expect(() => tally("not an array")).toThrow(TypeError);
expect(() => tally(null)).toThrow(TypeError);
});

/**
* tally array
*
Expand All @@ -23,7 +38,6 @@ 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");

// Given an array with duplicate items
// When passed to tally
Expand Down
33 changes: 32 additions & 1 deletion Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,54 @@
// E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"}

function invert(obj) {
// 1. valiadate input
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
throw new TypeError('Input must be a plain object');
}

// 2. initialize empty results object
const invertedObj = {};

// 3. iterate through key-value pairs
for (const [key, value] of Object.entries(obj)) {
invertedObj.key = value;
// 4. swap: value becomes key, bey becomes value
invertedObj[value] = key;
}

return invertedObj;
}

module.exports = invert;

// a) What is the current return value when invert is called with { a : 1 }
// invert({ a: 1 })
// Result: { key: 1 }
// invertedObj.key = value → creates a "key" property (string), not a key variable

// b) What is the current return value when invert is called with { a: 1, b: 2 }
// invert({ a: 1, b: 2 })
// Result: { key: 2 }
// First iteration: { key: 1 }
// Second iteration: { key: 2 } ← overwritten!

// c) What is the target return value when invert is called with {a : 1, b: 2}
// result:
// { "1": "a", "2": "b" }
// Values ​​become keys, keys become values

// c) What does Object.entries return? Why is it needed in this program?
// Object.entries({ a: 1, b: 2 })
// returns: [["a", 1], ["b", 2]]
// Why is this necessary?
// To access both the key and the value simultaneously
// Destructuring is used: const [key, value] = ["a", 1]

// d) Explain why the current return value is different from the target output
// 1. Dot notation instead of bracket notation:
// invertedObj.key = value; // ❌ Creates a "key" property (string)
// invertedObj[key] = value; // ✅ Uses the value of the key variable
// 2. Incorrect key-value order:
// invertedObj[key] = value; // ❌ Key remains the key
// invertedObj[value] = key; // ✅ Value becomes the key

// e) Fix the implementation of invert (and write tests to prove it's fixed!)
29 changes: 29 additions & 0 deletions Sprint-2/interpret/invert.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const invert = require("./invert.js");

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

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

// edge cases
test("handles empty pbject", () => {
expect(invert({})).toEqual({});
});

test("handles duplicate values (last key wins)", () => {
expect(invert({ a:1, b: 1 })).toEqual({ "1": "b" });
});

// error handling
test("throws TypeError for invalid input", () => {
expect(() => invert(null)).toThrow(TypeError);
expect(() => invert([1, 2])).toThrow(TypeError);
expect(() => invert("string")).toThrow(TypeError);
});
Loading