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
6 changes: 4 additions & 2 deletions Sprint-2/debug/address.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Predict and explain first...

// I predict the code will print "My house number is defines means undefined.
// This code should log out the houseNumber from the address object
// but it isn't working...
// Explanation: address is an object, and address[0] looks for a property named "0", which does not exist.

// Fix anything that isn't working

const address = {
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"]}`);
4 changes: 3 additions & 1 deletion Sprint-2/debug/author.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Predict and explain first...
// The program will show an error because an object is 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
// We cannot use a for loop directly to an object because objects are not iterable in JavaScript.

const author = {
firstName: "Zadie",
Expand All @@ -11,6 +13,6 @@ const author = {
alive: true,
};

for (const value of author) {
for (const value of Object.values(author)) {
console.log(value);
}
5 changes: 4 additions & 1 deletion Sprint-2/debug/recipe.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Predict and explain first...
// The program will display the title and serving information, but it will not display the
// ingredients correctly because we are trying to print the whole object.

// This program should log out the title, how many it serves and the ingredients.
// Each ingredient should be logged on a new line
// ingredients:${recipe} converts the whole object into a string rather than showing individual ingredients.
// How can you fix it?

const recipe = {
Expand All @@ -12,4 +15,4 @@ const recipe = {

console.log(`${recipe.title} serves ${recipe.serves}
ingredients:
${recipe}`);
${recipe.ingredients.join("\n")}`);
7 changes: 6 additions & 1 deletion Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
function contains() {}
function contains(object, property) {
if (object === null || typeof object !== "object" || Array.isArray(object)) {
throw new Error("contains requires an object");
}
return Object.hasOwn(object, property);
}

module.exports = contains;
26 changes: 25 additions & 1 deletion Sprint-2/implement/contains.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,41 @@ as an array isn't an 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({}, "name")).toBe(false);
});

// Given an object with properties
// When passed to contains with an existing property name
// Then it should return true

test("contains on an object with an existing property return ture", () => {
expect(contains({ name: "Cat", age: 10 }, "name")).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 an object with a non-existent property return false", () => {
expect(contains({ name: "Ramesh", age: 23 }, "location")).toBe(false);
});

// Given a value that isn't an object - an array, a string, a number,
// null, or no argument at all
// When passed to contains
// Then it should throw Error("contains requires an object")
// (careful: typeof [] and typeof null are both "object")
test("contains throws an error for a string", () => {
expect(() => contains("India", "0")).toThrow("contains requires an object");
});
test("contains throws an error for an array", () => {
expect(() => contains([1, 2, 3], "0")).toThrow("contains requires an object");
});
test("contains throws an error for null", () => {
expect(() => contains(null, "name")).toThrow("contains requires an object");
});

test("contains throws an error when no argument is given", () => {
expect(() => contains()).toThrow("contains requires an object");
});
test("contains throws an error for a number", () => {
expect(() => contains(100, "name")).toThrow("contains requires an object");
});
12 changes: 11 additions & 1 deletion Sprint-2/implement/lookup.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
function createLookup() {
function createLookup(pairs) {
// implementation here
const lookup = {};

for (const pair of pairs) {
const countryCode = pair[0];
const currencyCode = pair[1];

lookup[countryCode] = currencyCode;
}

return lookup;
}

module.exports = createLookup;
9 changes: 9 additions & 0 deletions Sprint-2/implement/lookup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,12 @@ It should return:
'CA': 'CAD'
}
*/
test("creates a country currency code lookup for multiple codes", () => {
const pairs = [
["US", "USD"],
["CA", "CAD"],
];
const result = createLookup(pairs);

expect(result).toEqual({ US: "USD", CA: "CAD" });
});
23 changes: 21 additions & 2 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
function parseQueryString(queryString) {
const queryParams = {};

if (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 [key, ...rest] = pair.split("=");
const value = rest.join("=");

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

if (decodedKey in queryParams) {
if (Array.isArray(queryParams[decodedKey])) {
queryParams[decodedKey].push(decodedValue);
} else {
queryParams[decodedKey] = [queryParams[decodedKey], decodedValue];
}
} else {
queryParams[decodedKey] = decodedValue;
}
}

return queryParams;
Expand Down
2 changes: 1 addition & 1 deletion Sprint-2/implement/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Below are some test cases the implementation doesn't handle well.
// Fix the implementation for these tests, and try to think of as many other edge cases as possible - write tests and fix those too.

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

test("should parse values containing '='", () => {
expect(parseQueryString("equation=a=b-2")).toEqual({
Expand Down
25 changes: 24 additions & 1 deletion Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
function tally() {}
function tally(items) {
if (!Array.isArray(items)) {
throw new Error("tally requires an array");
}

const counts = {};

for (const item of items) {
const key = String(item);

if (Object.prototype.hasOwnProperty.call(counts, key)) {
counts[key] += 1;
} else {
Object.defineProperty(counts, key, {
value: 1,
writable: true,
enumerable: true,
configurable: true,
});
}
}

return counts;
}

module.exports = tally;
69 changes: 64 additions & 5 deletions Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,80 @@ const tally = require("./tally.js");
* tally(['a', 'a', 'b', 'c']), target output: { a : 2, b: 1, c: 1 }
*/

// Acceptance criteria:

// Given a function called tally
// When passed an array of items
// Given an array of items
// When passed to tally
// Then it should return an object containing the count for each unique item
test("tally on a single item counts it once", () => {
expect(tally(["a"])).toEqual({ a: 1 });
});

test("tally on repeated items counts each one", () => {
expect(tally(["a", "a", "a"])).toEqual({ a: 3 });
});

test("tally on a mix of items counts each unique item", () => {
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 that are not next to each other", () => {
expect(tally(["a", "b", "a", "c", "b", "a"])).toEqual({ a: 3, b: 2, c: 1 });
});

// Given an invalid input like a string, a number, or no argument at all
// When passed to tally
// Then it should throw Error("tally requires an array")
test("tally throws on a string", () => {
expect(() => tally("abc")).toThrow(new Error("tally requires an array"));
});

test("tally throws on a number", () => {
expect(() => tally(42)).toThrow(new Error("tally requires an array"));
});

test("tally throws when called with no argument", () => {
expect(() => tally()).toThrow(new Error("tally requires an array"));
});

test("tally throws on null, undefined and plain objects", () => {
expect(() => tally(null)).toThrow(new Error("tally requires an array"));
expect(() => tally(undefined)).toThrow(new Error("tally requires an array"));
expect(() => tally({ a: 1 })).toThrow(new Error("tally requires an array"));
});

// Extra edge cases
test("tally handles item names that match built-in object properties", () => {
const result = tally(["constructor", "toString", "constructor"]);
expect(result.constructor).toBe(2);
expect(result.toString).toBe(1);
});

test("tally does not allow prototype pollution via __proto__", () => {
const result = tally(["__proto__", "__proto__"]);
expect({}.polluted).toBeUndefined();
expect(Object.keys(result)).toEqual(["__proto__"]);
expect(Object.getOwnPropertyDescriptor(result, "__proto__").value).toBe(2);
});

test("tally counts numbers and booleans by their string form", () => {
expect(tally([1, 1, 2, true, true])).toEqual({ 1: 2, 2: 1, true: 2 });
});

test("tally treats different cases as different items", () => {
expect(tally(["a", "A", "a"])).toEqual({ a: 2, A: 1 });
});

test("tally does not change the array it is given", () => {
const input = ["a", "b", "a"];
tally(input);
expect(input).toEqual(["a", "b", "a"]);
});
18 changes: 12 additions & 6 deletions Sprint-2/interpret/invert.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,26 @@ function invert(obj) {
const invertedObj = {};

for (const [key, value] of Object.entries(obj)) {
invertedObj.key = value;
// it's the error
// invertedObj.key = value;
// fixed the code
invertedObj[value] = key;
}

return invertedObj;
}
module.exports = invert;

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

// ==>The return value when invert is called is "a":"1"
// b) What is the current return value when invert is called with { a: 1, b: 2 }

// ==>The return value when invert is called are "a":"1", "b":"2"
// c) What is the target return value when invert is called with {a : 1, b: 2}

// ==>The target return value when invert is called with{a:1, b: 2} are "1": "a", "2": "b"
// c) What does Object.entries return? Why is it needed in this program?

// ==>It turns an object into an array of [key, value] pairs. Object.entries({ a: 1, b: 2 }) gives [["a", 1], ["b", 2]]. It’s needed because a for...of loop can’t go through a plain object directly, and the function needs both the key and the value of each property. Object.entries provides both, and const [key, value] unpacks each pair.
// d) Explain why the current return value is different from the target output

// ==>first it used the dot notation instead of square bracket which is accessing the property using a literal name
// ==>second it is not inverted it still uses key value instead of value key so that it returns the inverted one.
// e) Fix the implementation of invert (and write tests to prove it's fixed!)
// ==>i have fixed the code and for testing i made the another file and make the edges cases.
43 changes: 43 additions & 0 deletions Sprint-2/interpret/invert.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const invert = require("./invert.js");

test("swaps keys and values for the example in the exercise", () => {
expect(invert({ x: 10, y: 20 })).toEqual({ 10: "x", 20: "y" });
});

test("works with a single key", () => {
expect(invert({ a: 1 })).toEqual({ 1: "a" });
});

test("works with two keys", () => {
expect(invert({ a: 1, b: 2 })).toEqual({ 1: "a", 2: "b" });
});

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

test("string values become keys", () => {
expect(invert({ first: "one", second: "two" })).toEqual({
one: "first",
two: "second",
});
});

test("original numeric keys come back as strings", () => {
expect(invert({ 1: "a", 2: "b" })).toEqual({ a: "1", b: "2" });
});

test("when two keys share a value, the last key wins", () => {
expect(invert({ a: 1, b: 1 })).toEqual({ 1: "b" });
});

test("does not change the original object", () => {
const original = { a: 1, b: 2 };
invert(original);
expect(original).toEqual({ a: 1, b: 2 });
});

test("inverting twice gives back the original when values are unique strings", () => {
const original = { a: "x", b: "y" };
expect(invert(invert(original))).toEqual(original);
});
Loading