-
-
Notifications
You must be signed in to change notification settings - Fork 312
London | 26-May-ITP | Yonatan Teklemariam | Sprint 2 | exercises #1237
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
base: main
Are you sure you want to change the base?
Changes from all commits
28a696f
cff19af
7f760a0
7ed6911
6ba6fdc
0c16c1d
5ebaa3d
0960929
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,10 @@ | ||
| function contains() {} | ||
| function contains(obj, key) { | ||
| for (const currentKey of Object.keys(obj)) { | ||
| if (currentKey === key) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| module.exports = contains; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,18 @@ | ||
| function createLookup() { | ||
| // implementation here | ||
| function createLookup(countrycurrency) { | ||
| const lookup = {}; // lookup object is created empty to store the new keys and values mapped from the function | ||
| countrycurrency.map(function ([country, currency]) { | ||
| // maps every keys and values passed as argument in countrycurrency and stores to the variable names of country and currency | ||
| console.log(`${country}: ${currency}`); | ||
| lookup[country] = currency; // this lookup object is then used to store the already mapped keys and values of the countrycurrency | ||
| }); | ||
| return lookup; | ||
| } | ||
| console.log( | ||
| createLookup([ | ||
| ["US", "USD"], | ||
| ["UAE", "AED"], | ||
| ["ERI", "ERN"], | ||
| ]) | ||
| ); | ||
|
|
||
| module.exports = createLookup; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,16 @@ | ||
| function tally() {} | ||
| function tally(arr) { | ||
| if (!Array.isArray(arr)) throw new TypeError("Input must be an array"); | ||
| const counts = Object.create(null); | ||
| if (arr.length === 0) { | ||
| return counts; | ||
| } | ||
| for (const item of arr) { | ||
| if (typeof item !== "string" || !/^[A-Za-z0-9]+$/.test(item)) { | ||
| throw new Error("Please Enter Valid Input!"); | ||
| } | ||
| counts[item] = (counts[item] ?? 0) + 1; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain how ?? works here? |
||
| } | ||
| return counts; | ||
| } | ||
|
|
||
| module.exports = tally; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
| describe("tally", () => { | ||
| test("Given an array of items, returns counts for each unique item", () => { | ||
| const input = ["apple", "banana", "apple", "orange", "banana", "apple"]; | ||
| const expected = { apple: 3, banana: 2, orange: 1 }; | ||
| expect(tally(input)).toEqual(expected); | ||
| }); | ||
| // Given an empty array | ||
| // When passed to tally | ||
| // Then it should return an empty object | ||
| test("tally on an empty array returns an empty object", () => { | ||
| expect([]).toEqual({}); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I get a failure when I run this test, can you find out why? |
||
| }); | ||
|
|
||
| // 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 | ||
| // Then it should return counts for each unique item | ||
|
|
||
| // Given an invalid input like a string | ||
| // When passed to tally | ||
| // Then it should throw an error | ||
| // 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 items, returns counts for each unique item", () => { | ||
| const input = ["x", "x", "y", "x", "y"]; | ||
| const expected = { x: 3, y: 2 }; | ||
| expect(tally(input)).toEqual(expected); | ||
| }); | ||
| // Given an invalid input like a string | ||
| // When passed to tally | ||
| // Then it should throw an error | ||
| test("Given an array containing an invalid item, throws an Error", () => { | ||
| expect(() => tally(["valid", "hello world"])).toThrow( | ||
| "Please Enter Valid Input!" | ||
| ); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,23 +7,50 @@ | |
| // 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; | ||
| // Validate input type | ||
| if (!obj || typeof obj !== "object" || Array.isArray(obj)) { | ||
| throw new Error("Input must be an object"); | ||
| } | ||
|
|
||
| return invertedObj; | ||
| } | ||
| const entries = Object.entries(obj); | ||
| if (entries.length === 0) return {}; | ||
|
|
||
| // a) What is the current return value when invert is called with { a : 1 } | ||
| // take only the values | ||
| const values = entries.map(([_, v]) => v); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of getting the entries, then mapping for the values, is there a faster route to getting the values? |
||
|
|
||
| // b) What is the current return value when invert is called with { a: 1, b: 2 } | ||
| // Detect duplicate values | ||
| const hasDuplicate = new Set(values).size !== values.length; | ||
| if (hasDuplicate) { | ||
| throw new Error("Values cannot be duplicated"); | ||
| } | ||
|
|
||
| // c) What is the target return value when invert is called with {a : 1, b: 2} | ||
| const inverted = {}; | ||
|
|
||
| // c) What does Object.entries return? Why is it needed in this program? | ||
| for (const [key, value] of entries) { | ||
| // Validate key and value types | ||
| if ( | ||
| (typeof key !== "string" && typeof key !== "number") || | ||
| (typeof value !== "string" && typeof value !== "number") | ||
| ) { | ||
| throw new Error("Keys and values must be a string or a number"); | ||
| } | ||
|
|
||
| // d) Explain why the current return value is different from the target output | ||
| inverted[value] = key; | ||
| } | ||
|
|
||
| return inverted; | ||
| } | ||
| module.exports = invert; | ||
|
|
||
| // e) Fix the implementation of invert (and write tests to prove it's fixed!) | ||
| // a) What is the current return value when invert is called with { a : 1 } | ||
| // the current value is 1 | ||
| // b) What is the current return value when invert is called with { a: 1, b: 2 } | ||
| // the current return value at this call is 2 | ||
| // c) What is the target return value when invert is called with {a : 1, b: 2} | ||
| // the target return value at this call is to supposed to be 'b' | ||
| // d) What does Object.entries return? Why is it needed in this program? | ||
| // Object.entries is used because both key and value are needed to be returned from the loop | ||
| // e) Explain why the current return value is different from the target output | ||
| // it's because the code in the line 13 is only returning single literal property named "key" on every loop iteration, so each assignment overwrites the previous one and the final object ends up with { key: 2 } but the intended inverted output requires using the loop variables as property names (dynamic keys) so each iteration creates a distinct property (for example invertedObj[value] = key), producing separate entries like { 1: 'a', 2: 'b' } instead of repeatedly writing to the same "key" property which the current function is doing | ||
| // f) Fix the implementation of invert (and write tests to prove it's fixed!) | ||
| // Above is the fixed function | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| const invert = require("./invert"); | ||
|
|
||
| test("inverts a simple object", () => { | ||
| const input = { a: "x", b: "y" }; | ||
| const expected = { x: "a", y: "b" }; | ||
| expect(invert(input)).toEqual(expected); | ||
| }); | ||
|
|
||
| test("inverts an object with number values", () => { | ||
| const input = { a: 1, b: 2 }; | ||
| const expected = { 1: "a", 2: "b" }; | ||
| expect(invert(input)).toEqual(expected); | ||
| }); | ||
|
|
||
| test("accepts empty string as a key", () => { | ||
| const input = { "": "x" }; | ||
| const expected = { x: "" }; | ||
| expect(invert(input)).toEqual(expected); | ||
| }); | ||
|
|
||
| test("returns an empty object when input is empty", () => { | ||
| expect(invert({})).toEqual({}); | ||
| }); | ||
|
|
||
| test("throws when input is null", () => { | ||
| expect(() => invert(null)).toThrow("Input must be an object"); | ||
| }); | ||
|
|
||
| test("throws when input is not an object", () => { | ||
| expect(() => invert("hello")).toThrow("Input must be an object"); | ||
| expect(() => invert(123)).toThrow("Input must be an object"); | ||
| expect(() => invert(true)).toThrow("Input must be an object"); | ||
| }); | ||
|
|
||
| test("throws when input is an array", () => { | ||
| expect(() => invert(["a", "b"])).toThrow("Input must be an object"); | ||
| }); | ||
|
|
||
| test("throws when a value is not a string or number", () => { | ||
| const obj = { a: {} }; | ||
| expect(() => invert(obj)).toThrow( | ||
| "Keys and values must be a string or a number" | ||
| ); | ||
| }); | ||
|
|
||
| test("throws when values are duplicated (string)", () => { | ||
| const obj = { a: "x", b: "y", c: "x" }; | ||
| expect(() => invert(obj)).toThrow("Values cannot be duplicated"); | ||
| }); | ||
|
|
||
| test("throws when numeric values are duplicated", () => { | ||
| const obj = { a: 1, b: 2, c: 1 }; | ||
| expect(() => invert(obj)).toThrow("Values cannot be duplicated"); | ||
| }); | ||
|
|
||
| test("duplicate keys are overwritten by JavaScript before reaching invert()", () => { | ||
| const obj = { a: "x", a: "y" }; // JS overwrites first 'a' | ||
| expect(invert(obj)).toEqual({ y: "a" }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically the point of using tests is to test the output, if the output is correct, then we can trust that anything printed should be correct. This means we don't want a function under test to print anything. With that in mind, what is the purpose of this test?