Skip to content

CYF London | Fatma Arslantas | Module-Data-Groups | Week 2 #199

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 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
10 changes: 9 additions & 1 deletion Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,12 @@ const address = {
postcode: "XYZ 123",
};

console.log(`My house number is ${address[0]}`);
// 〰️ Correct way to access the houseNumber property
console.log(`My house number is ${address.houseNumber}`);

// 〰️ The issue with the code is that it's trying to access address[0], which is not correct because address is an object, and objects in JavaScript don't use numeric indices like arrays. Instead, we should access the houseNumber property directly using the correct key.

// 〰️ We need to use the correct property key to access the value. Since the key for the house number in the address object is "houseNumber", we should use address.houseNumber to access it.

// 〰️ address.houseNumber accesses the houseNumber property of the address object.
// 〰️ The result will correctly log: "My house number is 42"
19 changes: 18 additions & 1 deletion Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ const author = {
alive: true,
};

for (const value of author) {
// 〰️ 1.Using for...in:
for (const key in author) {
console.log(author[key]);
}

// 〰️ 2.Using Object.values():
/*
for (const value of Object.values(author)) {
console.log(value);
}
*/

// 〰️ The issue here is that JavaScript objects are not iterable using the for...of loop. This loop is designed to work with iterable objects like arrays, strings, maps, and sets, but not plain objects.
// 〰️ Trying to use for...of, result will be TypeError or no output.

// 〰️ How we can fix it:
// 〰️ We can use the for...in loop or Object.values():
// 〰️ 1. Using for...in: This loop iterates over the keys of the object.
// 〰️ 2. Using Object.values(): This returns an array of property values, allowing you to use for...of

11 changes: 10 additions & 1 deletion Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@ const recipe = {

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


// 〰️ When the code logs ${recipe}, JavaScript converts the entire object into a string using its default toString() method, which returns [object Object].
// 〰️ This happens because JavaScript doesn't know how to format the object automatically.
// 〰️ Additionally, the code doesn't iterate over the ingredients array, so the list isn't displayed properly.

// 〰️ We can fix it to use recipe.ingredients.join("\n") to join the ingredients with new lines, ensuring each appears on its own line.

// 〰️ recipe.ingredients.join("\n") converts the array into a string with each ingredient on a new line, creating a clean and readable output.
18 changes: 17 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
function contains() {}
function contains(obj, key) {
// 〰️ Checking if the input is a valid object or not null or an array
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
return false;
}
// 〰️ Using hasOwnProperty to check if the key exists directly on the object

Choose a reason for hiding this comment

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

Great job verifying the object parameter types in the contains function, and you've successfully met all the task requirements!

Copy link
Author

Choose a reason for hiding this comment

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

Hi @halilibrahimcelik,

Thank you for the feedback! I’m glad to hear that. Let me know if there’s anything else I can improve. 🤗

return obj.hasOwnProperty(key);
}

console.log(contains({a: 1, b: 2}, 'a'));
console.log(contains({ name: "Ali", age: 25 }, 'age'));
console.log(contains({ x: 10, y: 20 }, 'y'));

console.log(contains({a: 1, b: 2}, 'c'));
console.log(contains({}, 'a'));



module.exports = contains;
27 changes: 26 additions & 1 deletion Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,45 @@ 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("returns true if the object contains the property, false otherwise", () => {
expect(contains({ a: 1, b: 2 }, "a")).toBe(true);
expect(contains({ a: 1, b: 2 }, "c")).toBe(false);
});

// 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 if the property exists in the object", () => {
expect(contains({ x: 10, y: 20 }, "x")).toBe(true); // 'x' var
});

// Given an object with properties
// When passed to contains with a non-existent property name
// Then it should return false
test("contains with non-existent property name 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 with invalid parameters like an array returns false", () => {
expect(contains([1, 2, 3], "a")).toBe(false);
});

// 〰️ Test if contains returns false when null is passed as the object.
test("contains with null returns false", () => {
expect(contains(null, "a")).toBe(false);
});

// 〰️ Test if contains returns false when a non-object value is passed.
test("contains with a non-object value", () => {
expect(contains(42, 'a')).toBe(false);
});
21 changes: 19 additions & 2 deletions Sprint-2/implement/lookup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
function createLookup() {
// implementation here
// 〰️ Function to create a lookup object from an array of country-currency pairs
function createLookup(countryCurrencyPairs) {
// 〰️ Use Object.fromEntries to transform the array into an object
return Object.fromEntries(countryCurrencyPairs);
}

console.log(createLookup([['US', 'USD'], ['CA', 'CAD']]));
console.log(createLookup([['IN', 'INR'], ['JP', 'JPY']]));
console.log(createLookup([]));

module.exports = createLookup;


// 〰️ Explanation of Object.fromEntries():
// 〰️ Object.fromEntries() is a built-in JavaScript method that converts an array of key-value pairs into an object. It's particularly useful when you have data in array format and want to convert it into an object without using loops or more complex methods like reduce().

// 〰️ How It Works:
// 〰️ 1. It expects an array where each element is a two-item array representing [key, value].
// 〰️ 2.It processes each pair and adds the key-value pair to a new object.
// 〰️ 3.It returns the resulting object.

// 〰️ Since createLookup takes an array of pairs, using Object.fromEntries() makes the conversion simple and clean.
31 changes: 30 additions & 1 deletion Sprint-2/implement/lookup.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
const createLookup = require("./lookup.js");

test.todo("creates a country currency code lookup for multiple codes");
test("creates a country-currency lookup object from an array of pairs", () => {
// 〰️ Arrange: Define input and expected output
const input = [['US', 'USD'], ['CA', 'CAD']];
const expectedOutput = {
'US': 'USD',
'CA': 'CAD'
};

// 〰️ Act: Call the function with input
const result = createLookup(input);

// 〰️ Assert: Check if the result matches expected output
expect(result).toEqual(expectedOutput);
});

test("returns an empty object if input is empty", () => {
expect(createLookup([])).toEqual({});
});


test("handles multiple entries correctly", () => {
const input = [['IN', 'INR'], ['JP', 'JPY'], ['GB', 'GBP']];
const expectedOutput = {
'IN': 'INR',
'JP': 'JPY',
'GB': 'GBP'
};
expect(createLookup(input)).toEqual(expectedOutput);
});


/*

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

// 〰️ Check for null, undefined, or non-string input
if (!queryString || typeof queryString !== "string") {
return queryParams;
}

// 〰️ Split the query string into key-value pairs using '&'
const keyValuePairs = queryString.split("&");

for (const pair of keyValuePairs) {
const [key, value] = pair.split("=");
// 〰️ Split each pair by the first '=' and keep remaining as the value
const [rawKey, ...rawValue] = pair.split("=");

// 〰️ Decode keys and values safely
// 〰️ Using decodeURIComponent() to convert percent-encoded strings into readable text.
// 〰️ Example: 'John%20Doe' -> 'John Doe', '5%25' -> '5%'
// 〰️ rawKey || "" ensures that if rawKey is undefined, null, or empty, it returns to an empty string.
const key = decodeURIComponent(rawKey || "");
const value = decodeURIComponent(rawValue.join("=") || "");

Choose a reason for hiding this comment

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

you demonstrates a solid understanding of value-key pair parsing. Maybe in order to enhance test cases, you can also add dublicate test case for keys i.e "key=value1&key=value2" something like this.

Copy link
Author

Choose a reason for hiding this comment

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

Thank you for your feedback @halilibrahimcelik!

I've updated the implementation to handle duplicate keys by storing their values in an array. Additionally, I’ve added a test case to validate this behavior, ensuring that query strings like key=value1&key=value2 are correctly parsed into { key: ["value1", "value2"] }. I hadn’t considered this, but your suggestion helped improve my code. Thanks again!

Let me know if there’s anything else I can improve. 🙌

Choose a reason for hiding this comment

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

I'm glad to hear that! It’s a niche case, and in a real-world scenario, it’s unlikely to have two different values with the same key. However, it’s a good challenge, keep up the good work! Cheers 🎉

Copy link
Author

Choose a reason for hiding this comment

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

Thank you! 🤗


// 〰️ Store the key-value pair in the object
queryParams[key] = value;
}

return queryParams;
}

console.log(parseQueryString("equation=x=y+1"));
console.log(parseQueryString("key1=value1&key2=value2"));
console.log(parseQueryString("equation=x=y+1&other=abc=123"));
console.log(parseQueryString("name=John%20Doe&value=5%25"));
console.log(parseQueryString("first%20name=Alice"));
console.log(parseQueryString("%26key%3D=value%3F"));
console.log(parseQueryString("equation=x%3Dy%2B1"));
console.log(parseQueryString("specialChars=!%40%23%24%25%5E%26*()"));
console.log(parseQueryString("emptyKey="));
console.log(parseQueryString("=emptyValue"));
console.log(parseQueryString("multiple=values&key=value"));
console.log(parseQueryString(null));
console.log(parseQueryString(undefined));
console.log(parseQueryString(""));

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

test("parses multiple key-value pairs", () => {
expect(parseQueryString("key1=value1&key2=value2")).toEqual({
key1: "value1",
key2: "value2",
});
});

test("parses querystring values containing multiple '='", () => {
expect(parseQueryString("equation=x=y+1&other=abc=123")).toEqual({
equation: "x=y+1",
other: "abc=123",
});
});

test('handles empty keys and values', () => {
expect(parseQueryString("emptyKey=")).toEqual({ emptyKey: '' });
expect(parseQueryString("=emptyValue")).toEqual({ '': 'emptyValue' });
});

test('parses simple query strings', () => {
expect(parseQueryString("name=John%20Doe&value=5%25")).toEqual({ name: 'John Doe', value: '5%' });
});

test('handles special characters', () => {
expect(parseQueryString("specialChars=!%40%23%24%25%5E%26*()")).toEqual({ specialChars: '!@#$%^&*()' });
});

test('returns empty object for null, undefined, or empty strings', () => {
expect(parseQueryString(null)).toEqual({});
expect(parseQueryString(undefined)).toEqual({});
expect(parseQueryString("")).toEqual({});
});
27 changes: 26 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
function tally() {}
function tally(arr) {
// 〰️ Check if input is an array
if (!Array.isArray(arr)) {
throw new Error("Input must be an array")
}

// 〰️ Create an empty object to store counts
let counts = {};

// 〰️ Loop through the array and count items
for (let item of arr) {
if (counts[item]) {
counts[item] += 1; // 〰️ Increase count if item exists
} else {
counts[item] = 1; // 〰️ Initialize count if item is new
}
}

return counts; // 〰️ Return the final counts object
}

console.log(tally(['a']));
console.log(tally(['a', 'a', 'a']));
console.log(tally(['a', 'a', 'b', 'c']));
console.log(tally([]));
// console.log(tally('a'));

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 @@ -19,16 +19,33 @@ 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 on an array with one item", ()=> {
expect(tally(["a"])).toEqual({ a: 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 on an array with duplicate items", ()=> {
expect(tally(["a", "a", "a"])).toEqual({ a: 3 });
});

// 〰️ Given an array with different items
test("tally on an array with different items", ()=> {
expect(tally(["a", "a", "b", "c", "d"])).toEqual({ a: 2, b: 1, c: 1, d: 1 });
});

// Given an invalid input like a string
// When passed to tally
// Then it should throw an error
test("throws an error when input is not an array", ()=> {
expect(()=> tally("not an array")).toThrow("Input must be an array");
});

24 changes: 23 additions & 1 deletion Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,43 @@
function invert(obj) {
const invertedObj = {};

// 〰️ We should use Object.entries to get key-value pairs and swap them
for (const [key, value] of Object.entries(obj)) {
invertedObj.key = value;
invertedObj[value] = key; // 〰️ Swap the key and value
}

return invertedObj;
}

console.log(invert({x : 10, y : 20}));

// a) What is the current return value when invert is called with { a : 1 }
// 〰️ The current implementation has a mistake when trying to swap keys and values. Specifically, it uses invertedObj.key = value;, which means it's always assigning the value to the property key of invertedObj.
// 〰️ This is wrong because we want the key to be the new value and the value to be the new key.
// 〰️ When the current function is called with { a : 1 }:
// 〰️ The loop would run once with key = 'a' and value = 1.
// 〰️ The code would execute invertedObj.key = 1, which creates a key key with value 1, and thus the result is { key: 1 }.
// 〰️So, the current return value for { a : 1 } is: { "key": 1 }

// b) What is the current return value when invert is called with { a: 1, b: 2 }
// 〰️ When we call invert({ a: 1, b: 2 }) with the current code:
// 〰️ The first iteration would set invertedObj.key = 1, resulting in { key: 1 }.
// 〰️ The second iteration would also try to assign to invertedObj.key = 2, which overwrites the previous key-value pair, so the final result is { key: 2 }.
// 〰️ The current return value for { a: 1, b: 2 } is: { "key": 2 }

// c) What is the target return value when invert is called with {a : 1, b: 2}
// 〰️ The target return value should swap the keys and values, meaning the key a should become 1 and the key b should become 2. So the expected output for { a: 1, b: 2 } is: { "1": "a", "2": "b" }

// c) What does Object.entries return? Why is it needed in this program?
// 〰️ Object.entries returns an array of key-value pairs from an object. For example:
// 〰️ Object.entries({ a: 1, b: 2 }) would return [["a", 1], ["b", 2]].
// 〰️ It is used in the program to loop over each key-value pair of the object. Without it, there would be no easy way to access both the key and the value at the same time inside the loop.

// d) Explain why the current return value is different from the target output
// 〰️ The current return value is incorrect because the code mistakenly assigns value to the key property of the invertedObj. It does not actually swap the key and value. Instead of using invertedObj.key = value;, we should use invertedObj[value] = key; to properly swap the key-value pairs.

// e) Fix the implementation of invert (and write tests to prove it's fixed!)
// 〰️ Tests to prove it's fixed
console.log(invert({ a: 1 })); // 〰️ Expected output: { "1": "a" }
console.log(invert({ a: 1, b: 2 })); // 〰️ Expected output: { "1": "a", "2": "b" }
console.log(invert({ x: 10, y: 20 })); // 〰️ Expected output: { "10": "x", "20": "y" }