Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0ada3c7
fix: correct address property access in console log
Grajales-K Mar 17, 2026
51fbc93
fix: iterate over author object values instead of object directly
Grajales-K Mar 17, 2026
2af5e52
feat: add for of to print the ingredients logged on a new line
Grajales-K Mar 18, 2026
431ad8d
test: enhance contains function tests for various scenarios
Grajales-K Mar 18, 2026
12b176a
feat: implement contains function to check property existence in objects
Grajales-K Mar 18, 2026
a6db5d0
test: add tests for createLookup function to validate country and cur…
Grajales-K Mar 18, 2026
45eff04
feat: implement createLooKup function to map countries to currencies
Grajales-K Mar 18, 2026
8e727fa
fix: correct implementation of invert function to return expected key…
Grajales-K Mar 18, 2026
bbadd05
feat: simplify ingredient logging by joining array elements
Grajales-K Mar 26, 2026
5954a24
fix: replace property check with Object.hasOwn for better accuracy
Grajales-K Mar 26, 2026
32d596c
test: add test for inherited properties in contains function
Grajales-K Mar 26, 2026
5fb163b
test: add additional tests for non-object parameters in contains func…
Grajales-K Mar 26, 2026
7de3692
fix: enhance query string parsing to handle empty and malformed pairs
Grajales-K Mar 27, 2026
9f2cfaa
fix: improve query string parsing to decode keys and values correctly
Grajales-K Mar 30, 2026
f576b00
test: add tests for query string parsing edge cases
Grajales-K Mar 30, 2026
c8745ab
test: update query string parsing test to cover additional value formats
Grajales-K Mar 31, 2026
f1acefa
test: add unit tests for tally function to validate item counting and…
Grajales-K Mar 31, 2026
65beafc
feat: implement tally function to count items in an array with error …
Grajales-K Mar 31, 2026
9ed9f13
refactor: split calculateMode into smaller functions for improved rea…
Grajales-K Mar 31, 2026
0a6a351
fix: correct coin value calculation and format total output in totalT…
Grajales-K Mar 31, 2026
dace1b2
test: add unit tests for totalTill function to validate coin calculat…
Grajales-K Mar 31, 2026
e100de6
fix: update comments in invert function to clarify return values
Grajales-K Mar 31, 2026
2a4bad9
fix: correct variable name for decoded key in query string parsing
Grajales-K Apr 1, 2026
cbe948e
fix: use Object.create(null) for counts in tally function to avoid pr…
Grajales-K Apr 1, 2026
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
2 changes: 1 addition & 1 deletion Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
console.log(`My house number is ${address["houseNumber"]}`);
6 changes: 5 additions & 1 deletion Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const author = {
alive: true,
};

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


// It did work because an object is not iterable as an array, but we can use Object.values()
// to get an array of the property values and then iterate over that array.
13 changes: 9 additions & 4 deletions Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@
// Each ingredient should be logged on a new line
// How can you fix it?

// it won't work because the last line is calling the object property recipe and we
// need the ingredientes, the console.log is trying to log the whole array of ingredients
// as a string, but we want to log each ingredient on a new line. We can fix this by
// using a for loop to iterate over the ingredients array and log each ingredient separately.

const recipe = {
title: "bruschetta",
title: "Bruschetta",
serves: 2,
ingredients: ["olive oil", "tomatoes", "salt", "pepper"],
};

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);

console.log(`Recipe: ${recipe.title} \nserves: ${recipe.serves}`);
console.log(recipe.ingredients.join("\n"));
9 changes: 8 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
function contains() {}
function contains(obj, property) {
if (typeof obj !== "object" || obj === null || Array.isArray(obj)){
return false;
}

return Object.hasOwn(obj, property);
}


module.exports = contains;
35 changes: 33 additions & 2 deletions Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,51 @@ as the object doesn't contains a key of 'c'
// Given a contains function
// When passed an object and a property name
// Then it should return true if the object contains the property, false otherwise
test("return true when an object has the property name", () => {
expect(contains({a: 1, b: 2}, "a")).toBe(true);
})

// Given an empty object
// When passed to contains
// Then it should return false
test.todo("contains on empty object returns false");
test("returns false on empty object ", () => {
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("return true when pass an object that contains the property name", () => {
expect(contains({name: "John", age: 30}, "name")).toBe(true);
})

// Given an object with properties
// When passed to contains with a non-existent property name
// Then it should return false
test("return false when pass an object that does not contain the property name", () => {
expect(contains({id: 12, status: "active"}, "age")).toBe(false);
})

// Given invalid parameters like an array
// When passed to contains
// When: Checking for a valid array property like 'length' or an index
// Then it should return false or throw an error
test("return false when pass an array instead of an object", () => {
expect(contains(["Banana"], "length")).toBe(false);
expect(contains(["Apple"], "0")).toBe(false);
})

// Given a value that is null, undefined, or a primitive (like 123)
// When passed to the contains function
// Then it should return false to prevent errors and ensure only real objects are processed.
test("should return false for null and other non-objects", () => {
expect(contains(null, "prop")).toBe(false);
expect(contains(undefined, "prop")).toBe(false);
expect(contains(123, "toFixed")).toBe(false);
});

// Given: A standard object created with literal notation {}.
// When: It is passed to the 'contains' function looking for a built-in property like "toString".
// Then: It should return false because the object is not the "owner" of that property.
test("Should return false for inherited properties like 'toString'", () => {
expect(contains({}, "toString")).toBe(false);
});
8 changes: 6 additions & 2 deletions Sprint-2/implement/lookup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
function createLookup() {
// implementation here
function createLookup(pairs) {
const result = {};
for(const [country, currency] of pairs) {
result[country] = currency;
}
return result
}

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

test.todo("creates a country currency code lookup for multiple codes");

/*

Expand Down Expand Up @@ -33,3 +32,20 @@ It should return:
'CA': 'CAD'
}
*/

test("return an object with country codes as keys and currency codes as values", () => {
expect( createLookup([ ["MX", "MXN"], ["JP", "JPY"] ])).toEqual({ MX: "MXN", JP: "JPY"});
expect( createLookup([ ["AU", "AUD"], ["CH", "CHF"] ])).toEqual({ AU: "AUD", CH: "CHF" });
});

describe("return an object with country codes as keys and currency codes as values", () => {
[
{ input: [["US", "USD"],["CA", "CAD"]], expected: { US: "USD", CA: "CAD" } },
{ input: [["GB", "GBP"],["FR", "EUR"]], expected: { GB: "GBP", FR: "EUR" } },
{ input: [["JP", "JPY"],["AU", "AUD"]], expected: { JP: "JPY", AU: "AUD" } },
].forEach(({ input, expected }) => {
it(`should convert ${JSON.stringify(input)} into ${JSON.stringify(expected)}`, () => {
expect(createLookup(input)).toEqual(expected);
});
});
});
20 changes: 15 additions & 5 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
function parseQueryString(queryString) {
const queryParams = {};
if (queryString.length === 0) {
if (!queryString || queryString.trim() === "" ) {
return queryParams;
}
const keyValuePairs = queryString.split("&");

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

const [key, ...rest] = pair.split("=");

const value = rest.join("=");

if (key) {

const decodedKey = decodeURIComponent(key.replace(/\+/g, " "));
const decodedValue = decodeURIComponent(value.replace(/\+/g, " "));
queryParams[decodedKey] = decodedValue;
}

}
return queryParams;
}

Expand Down
51 changes: 48 additions & 3 deletions Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,53 @@

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

test("parses querystring values containing =", () => {
expect(parseQueryString("equation=x=y+1")).toEqual({
"equation": "x=y+1",

// Given a querystring with a value that contains an = symbol ( + URL encoded as %2B and spaces encoded as + )
// When passed to parseQueryString
// Then it should treat everything after the first = as the value
test('parses querystring values containing (=, +, " ")', () => {
expect(parseQueryString("equation=x=y%2B1")).toEqual({
equation: "x=y+1",
});
});

// Given an querystring string with spaces encoded as (%20 and +)
// when passed to parseQueryString
// then it should return the string with real spaces in both cases
test("decodes spaces correctly from both %20 and +", () => {
expect(parseQueryString("name=John%20Doe&city=New+York")).toEqual({
"name": "John Doe",
"city": "New York",
});
});


// Given a URL-encoded key like like %5B%5D = []
// When passed to parseQueryString
// Then it should decode the key to 'tags[]'
test("Handles URL-Encoded keys", () =>{
expect(parseQueryString("tags%5B%5D=javascript")).toEqual({
"tags[]": "javascript",
});
})

// Given a string with multiple ampersands like a=1&&b=2&c=3&&
// When passed to parseQueryString
// Then it should ignore the empty segments and return valid pairs
test("Should ignore empty ampersand", () => {
expect(parseQueryString("a=1&&b=2")).toEqual({
a: "1",
b: "2",

});
})

// Given a key without a value like 'flag' or 'empty='
// When passed to parseQueryString
// Then it should return the key with an empty string as value
test("handles keys with no values", () => {
expect(parseQueryString("empty=&flag")).toEqual({
"empty": "",
"flag": ""
});
});
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) {
const counts = Object.create(null);

if (!Array.isArray(items)) {
throw new Error("Input must be an array");
}

for (const item of items) {
counts[item] = (counts[item] || 0) + 1;
}
return counts;
}

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("Should return an object with the count of each unique item in the 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("Should return an empty object when passed an empty array", () => {
expect(tally([])).toEqual({});
});

// Given an array with duplicate items
// When passed to tally
// Then it should return counts for each unique item
test("Should return counts for each unique item in an array with duplicate items", () => {
expect(tally(['x', 'y', 'x', 'z', 'y', 'x'])).toEqual({ x: 3, y: 2, z: 1 });
});

// Given an invalid input like a string
// When passed to tally
// Then it should throw an error
test("Should throw and error when passed and invalid input like a string", () => {
expect(() => tally('not an array')).toThrow('Input must be an array');
});
45 changes: 38 additions & 7 deletions Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,55 @@

// 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;
}

// a) What is the current return value when invert is called with { a : 1 }
console.log(invert({ a: 1, b: 2 })); // Expected output: { '1': 'a', '2': 'b' }



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

b) What is the current return value when invert is called with { a: 1, b: 2 }
*the first pair is { key: 1 }
*and the second pair the value is { key: '2' } that means the first pair is
being overwritten by the second pair, so the final return value is { key: '2' }

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

d) What does Object.entries return? Why is it needed in this program?
*Return value: It returns an array of arrays, where each sub-array is a [key, value]
pair (e.g., [['a', 1]]).
*Why it's needed: Objects are not iterables by default. We need Object.entries
to convert the object into an array so we can use the for...of loop to go through each pair.

e) Explain why the current return value is different from the target output
*Literal Property Creation: The current code uses dot notation (invertedObj.key),
which creates a property literally named "key" instead of using the value stored
inside the key variable.

*Overwriting: Because it keeps targetting the same literal property name ("key"),
each iteration overwrites the previous one. This is why the final object only contains
the last value processed.

*Target vs. Current: The target output requires bracket notation (invertedObj[value])
to dynamically assign the new keys based on the input's values.

// 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}
f) Fix the implementation of invert (and write tests to prove it's fixed!)
expect({ a: 1, b: 2 })toEqual({ '1': 'a', '2': 'b' } ) now is working as expected

// 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!)
30 changes: 20 additions & 10 deletions Sprint-2/stretch/mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@
// refactor calculateMode by splitting up the code
// into smaller functions using the stages above

function calculateMode(list) {
// track frequency of each value
let freqs = new Map();

for (let num of list) {
if (typeof num !== "number") {
continue;
}
// track frequency of each value
function getFrequency(list) {
let frequency = new Map();

freqs.set(num, (freqs.get(num) || 0) + 1);
for (let num of list) {
if (typeof num !== "number") continue;
frequency.set(num, (frequency.get(num) || 0) + 1);
}
return frequency;
}


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

for (let [num, freq] of frequency) {
if (freq > maxFreq) {
mode = num;
maxFreq = freq;
Expand All @@ -33,4 +36,11 @@ function calculateMode(list) {
return maxFreq === 0 ? NaN : mode;
}

// The refactored calculateMode using the stages above
function calculateMode(list) {
const frequency = getFrequency(list);
return getHighestFrequency(frequency);
}


module.exports = calculateMode;
Loading
Loading