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
7 changes: 5 additions & 2 deletions Sprint-2/implement/contains.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
function contains() {}
function contains(obj, prop) {
if (typeof obj !== "object" || obj === null) return false;
return Object.prototype.hasOwnProperty.call(obj, prop);
}

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

/*
Implement a function called contains that checks an object contains a
Expand All @@ -16,20 +16,44 @@ 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("contains returns true for existing property", () => {
const currentOutput = contains({a:1,b:2}, 'a');
const targetOutput = true;
expect(currentOutput).toEqual(targetOutput);
});

// 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", () => {
const currentOutput = contains({}, 'a');
const targetOutput = false;
expect(currentOutput).toEqual(targetOutput);
});

// Given an object with properties
// When passed to contains with an existing property name
// Then it should return true
test("contains returns true for existing property", () => {
const currentOutput = contains({a:1,b:2}, 'b');
const targetOutput = true;
expect(currentOutput).toEqual(targetOutput);
});

// Given an object with properties
// When passed to contains with a non-existent property name
// Then it should return false
test("contains returns false for non-existent property", () => {
const currentOutput = contains({a:1,b:2}, 'c');
const targetOutput = false;
expect(currentOutput).toEqual(targetOutput);
});

// Given invalid parameters like an array
// When passed to contains
// Then it should return false or throw an error
test("contains returns false for invalid parameters", () => {
const currentOutput = contains([1,2,3], 'a');
const targetOutput = false;
expect(currentOutput).toEqual(targetOutput);
});
10 changes: 7 additions & 3 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 lookup = {};
for (const [countryCode, currencyCode] of pairs) {
lookup[countryCode] = currencyCode;
}
return lookup;
}

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

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

test("creates a country currency code lookup for multiple codes", () => {
const countryCurrencyPairs = [["US", "USD"], ["CA", "CAD"]];
const result = createLookup(countryCurrencyPairs);
expect(result).toEqual({ US: "USD", CA: "CAD" });
});
Comment on lines +3 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I would consider having a smaller starting test for the valid input, to build the test suite up more gradually, prove the function can take different inputs and isn't hardcoded

/*

Create a lookup object of key value pairs from an array of code pairs
Expand Down
24 changes: 16 additions & 8 deletions Sprint-2/implement/querystring.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
function decodeComponent(str) {
return decodeURIComponent(str.replaceAll("+", " "));
}

function parseQueryString(queryString) {
const queryParams = {};
if (queryString.length === 0) {
return queryParams;
return {};
}
const keyValuePairs = queryString.split("&");

for (const pair of keyValuePairs) {
const [key, value] = pair.split("=");
queryParams[key] = value;
const params = {};
const pairs = queryString.split("&").filter((pair) => pair !== "");

for (const pair of pairs) {
const [rawKey, ...rawValueParts] = pair.split("=");
const key = decodeComponent(rawKey);
const value = decodeComponent(rawValueParts.join("="));

params[key] = key in params ? [].concat(params[key], value) : value;
}

return queryParams;
return params;
}

module.exports = parseQueryString;
module.exports = parseQueryString;
12 changes: 9 additions & 3 deletions 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");

test("should parse values containing '='", () => {
expect(parseQueryString("equation=a=b-2")).toEqual({
Expand Down Expand Up @@ -37,12 +37,18 @@ test("should replace '+' by ' '", () => {
});
});

test("should return {} for an empty string", () => {
expect(parseQueryString("")).toEqual({});
});

// Stretch exercise: Handling query strings that contain identical keys

// Delete this test if you are not working on this optional case
test("should store values of a key in an array when the key has 2 or more values", () => {
expect(parseQueryString("key=value1&key=value2&key=value3&foo=bar")).toEqual({
expect(
parseQueryString("key=value1&key=value2&key=value3&foo=bar")
).toEqual({
key: ["value1", "value2", "value3"],
foo: "bar",
});
});
});
14 changes: 12 additions & 2 deletions Sprint-2/implement/tally.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
function tally() {}
function tally(arr) {
if (!Array.isArray(arr)) {
throw new Error("tally expects an array");
}

module.exports = tally;
const counts = {};
for (const item of arr) {
counts[item] = (counts[item] || 0) + 1;
}
return counts;
}

module.exports = tally;
25 changes: 13 additions & 12 deletions Sprint-2/implement/tally.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,22 @@ 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
// Then it should return an object containing the count for each unique item
describe("tally on an array of items returns counts for each unique item", () => {
test("tally on an empty array returns an empty object", () => {
expect(tally([])).toEqual({});
});

// 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 array with duplicate items returns counts for each unique item", () => {
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 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
test("tally throws an error for invalid input", () => {
expect(() => tally("not an array")).toThrow();
});
});
60 changes: 44 additions & 16 deletions Sprint-2/interpret/invert.js

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You're missing all the explanations and answers to questions a, b, c, c, d, e - please add them in

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

hi @Poonam-raj , i answered all questions

Original file line number Diff line number Diff line change
@@ -1,29 +1,57 @@
// Let's define how invert should work

// inverse typically means to reverse the key and value in an object.
// Given an object
// When invert is passed this object
// Then it should swap the keys and values in the object

// E.g. invert({x : 10, y : 20}), target output: {"10": "x", "20": "y"}

// The original buggy implementation looked like this:
//
// 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 }?
// -> { key: 1, "1": "a" }
// The line `invertedObj.key = value` sets a property literally named
// "key" (not the loop variable's value) to 1, alongside the correct
// inverted pair "1": "a" from the line below it.

// b) What is the current return value when invert is called with { a: 1, b: 2 }?
// -> { key: 2, "1": "a", "2": "b" }
// Each loop iteration overwrites the same literal "key" property, so
// only the last value assigned to it survives - here, 2 from { b: 2 }.

// c) What does Object.entries return, and why is it needed?
// -> Object.entries(obj) returns an array of [key, value] pairs, e.g.
// [["a", 1], ["b", 2]]. It's needed because a for...of loop can't
// iterate directly over an object's properties - Object.entries
// converts the object into something iterable, and array
// destructuring ([key, value]) lets us pull out both parts at once.

// d) Why is the current return value different from the target output?
// -> The bug is `invertedObj.key = value`. Because "key" is written as a
// literal property name (dot notation), it always sets a property
// called "key" rather than using the value of the loop variable
// `key`. Only bracket notation - invertedObj[key] - would use the
// variable's actual value as the property name. This line also isn't
// needed at all for a correct inversion; it should be removed.

// e) Fix: remove the incorrect `invertedObj.key = value` line entirely,
// leaving only `invertedObj[value] = key`, which correctly maps each
// value to its original key. See invert.test.js for tests proving
// the fix works, including empty objects and string values.
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 }

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

// 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!)
module.exports = invert;
17 changes: 17 additions & 0 deletions Sprint-2/interpret/invert.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const invert = require("./invert");

test("invert swaps keys and values in an object", () => {
const input = { a: 1, b: 2 };
const expectedOutput = { "1": "a", "2": "b" };
expect(invert(input)).toEqual(expectedOutput);
});

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

test("invert works with string values", () => {
const input = { x: "10", y: "20" };
const expectedOutput = { "10": "x", "20": "y" };
expect(invert(input)).toEqual(expectedOutput);
});
42 changes: 14 additions & 28 deletions Sprint-2/stretch/count-words.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,14 @@
/*
Count the number of times a word appears in a given string.

Write a function called countWords that
- takes a string as an argument
- returns an object where
- the keys are the words from the string and
- the values are the number of times the word appears in the string

Example
If we call countWords like this:

countWords("you and me and you") then the target output is { you: 2, and: 2, me: 1 }

To complete this exercise you should understand
- Strings and string manipulation
- Loops
- Comparison inside if statements
- Setting values on an object

## Advanced challenges

1. Remove all of the punctuation (e.g. ".", ",", "!", "?") to tidy up the results

2. Ignore the case of the words to find more unique words. e.g. (A === a, Hello === hello)

3. Order the results to find out which word is the most common in the input
*/
function countWords(str) {
const wordCount = {};
const words = str
.toLowerCase()
.replace(/[^\w\s]/g, "")
.split(/\s+/)
.filter(Boolean);
for (const word of words) {
wordCount[word] = (wordCount[word] || 0) + 1;
}
return wordCount;
}

module.exports = countWords;
17 changes: 17 additions & 0 deletions Sprint-2/stretch/count-words.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const countWords = require("./count-words");

test("counts word occurrences in a string", () => {
expect(countWords("you and me and you")).toEqual({ you: 2, and: 2, me: 1 });
});

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

test("ignores punctuation", () => {
expect(countWords("Hello, world! Hello?")).toEqual({ hello: 2, world: 1 });
});

test("ignores case", () => {
expect(countWords("A a Hello hello")).toEqual({ a: 2, hello: 2 });
});
Loading