Skip to content
Open
26 changes: 24 additions & 2 deletions Sprint-1/fix/median.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,30 @@
// or contains values that aren't numbers (the function is expected to throw - see the tests).

function calculateMedian(list) {
const middleIndex = Math.floor(list.length / 2);
const median = list.splice(middleIndex, 1)[0];
if (!Array.isArray(list)) {
throw new Error("calculateMedian requires an array of numbers");
}
if (list.length === 0) {
throw new Error("calculateMedian requires a non-empty array");
}

if (
!list.every(
(currentValue) =>
typeof currentValue === "number" && !Number.isNaN(currentValue)
)
Comment on lines +17 to +20

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • Could also consider excluding +/- Infinity.

  • Could probably give a simpler name than currentValue to the parameter. The prefix "current" does not add any new info.

) {
throw new Error("calculateMedian requires an array of numbers");
}

const sortedList = list.toSorted((a, b) => a - b);
const middleIndex = Math.floor(sortedList.length / 2);
let median;
if (sortedList.length % 2 === 0) {
median = (sortedList[middleIndex - 1] + sortedList[middleIndex]) / 2;
} else {
median = sortedList[middleIndex];
}
return median;
}

Expand Down
13 changes: 12 additions & 1 deletion Sprint-1/implement/dedupe.js
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
function dedupe() {}
function dedupe(elements) {
const dedupedArray = [];

for (value of elements) {
if (!dedupedArray.includes(value)) {
dedupedArray.push(value);
}
}
Comment on lines +4 to +8

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should the loop variable value be declared using const or let in this loop?

return dedupedArray;
}

module.exports = dedupe;
26 changes: 15 additions & 11 deletions Sprint-1/implement/dedupe.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@ E.g. dedupe([1, 2, 1]) returns [1, 2]

// Acceptance Criteria:

// Given an empty array
// When passed to the dedupe function
// Then it should return an empty array
test.todo("given an empty array, it returns an empty array");
test("given an empty array, it returns an empty array", () => {
expect(dedupe([])).toEqual([]);
});

// Given an array with no duplicates
// When passed to the dedupe function
// Then it should return a copy of the original array
test("given an array with no duplicates, should return a copy of the same array", () => {
expect(dedupe([1, 5, 9, 23])).toEqual([1, 5, 9, 23]);
});
Comment on lines -21 to +22

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Your function implementation is correct. However, this test could be improved to better ensure
that any future changes continue to align with the expected behavior specified in the original comment:

Then it should return a copy of the original array

This test should fail if the function returns the original array (instead of a copy of the original array).

The current test checks only if both the original array and the returned array contain identical elements.
In order to validate the returned array is a different array, we need an additional check.

Can you find out what this additional check is?


// Given an array of strings or numbers
// When passed to the dedupe function
// Then it should return a new array with duplicates removed while preserving the
// first occurrence of each element from the original array.
test("given an array with strings or numbers, should return an array with duplicates removed and first occurrence preserved", () => {
expect(dedupe([1, 6, 1, "a", 5, 6, "a", "b", "b"])).toEqual([
1,
6,
"a",
5,
"b",
]);
});
14 changes: 13 additions & 1 deletion Sprint-1/implement/describe-median.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ function calculateMedian(list) {
}

// Implement this function. See describe-median.test.js for the acceptance criteria.
function describeMedian(list) {}
function describeMedian(list) {
try {
return "The median is " + calculateMedian(list);
} catch {
if (list.length === 0) {
return "Could not calculate a median: calculateMedian requires a non-empty array";
}

if (!Array.isArray(list)) {
return "Could not calculate a median: calculateMedian requires an array of numbers";
}
}
Comment on lines +27 to +34

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you look up how to use a try...catch block to catch a thrown error object, then simplify the code on lines 27-33 into a single line by constructing the return value from the error object's message property?

}

module.exports = describeMedian;
28 changes: 15 additions & 13 deletions Sprint-1/implement/describe-median.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@ const describeMedian = require("./describe-median.js");

// Acceptance criteria:

// Given an array of numbers
// When passed to describeMedian
// Then it should return "The median is " followed by the median
// Delete this test.todo and replace it with a test.
test.todo('given [1, 2, 3], returns "The median is 2"');

// Given an empty array
// When passed to describeMedian
// Then it should return "Could not calculate a median: calculateMedian requires a non-empty array"

// Given something that isn't an array of numbers, e.g. "banana"
// When passed to describeMedian
// Then it should return "Could not calculate a median: calculateMedian requires an array of numbers"
test('given [1, 2, 3], returns "The median is 2"', () => {
expect(describeMedian([1, 2, 3])).toEqual("The median is 2");
});

test('given empty array, returns "Could not calculate a median: calculateMedian requires a non-empty array"', () => {
expect(describeMedian([])).toEqual(
"Could not calculate a median: calculateMedian requires a non-empty array"
);
});

test('given something that isnt an array of numbers, returns "Could not calculate a median: calculateMedian requires an array of numbers"', () => {
expect(describeMedian("banana")).toEqual(
"Could not calculate a median: calculateMedian requires an array of numbers"
);
});
20 changes: 20 additions & 0 deletions Sprint-1/implement/max.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
function findMax(elements) {
if (!Array.isArray(elements)) {
throw new Error("findMax requires an array of numbers");
}

if (elements.length === 0) {
return -Infinity;
}

if (
!elements.every(
(currentValue) =>
typeof currentValue === "number" && !Number.isNaN(currentValue)
)
) {
throw new Error("findMax requires an array of numbers");
}

sortedElements = elements.toSorted((a, b) => a - b);

return sortedElements[sortedElements.length - 1];
Comment on lines +19 to +21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This work.

Suggestion: Explore a more efficient approach to find the largest numbers in an array.

}

module.exports = findMax;
60 changes: 31 additions & 29 deletions Sprint-1/implement/max.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,34 @@ We have set things up already so that this file can see your function from the o

const findMax = require("./max.js");

// Given an empty array
// When passed to the max function
// Then it should return -Infinity
// Delete this test.todo and replace it with a test.
test.todo("given an empty array, returns -Infinity");

// Given an array with one number
// When passed to the max function
// Then it should return that number

// Given an array with both positive and negative numbers
// When passed to the max function
// Then it should return the largest number overall

// Given an array with just negative numbers
// When passed to the max function
// Then it should return the closest one to zero

// Given an array with decimal numbers
// When passed to the max function
// Then it should return the largest decimal number

// Given an array containing a value that isn't a number
// When passed to the max function
// Then it should throw Error("findMax requires an array of numbers")

// Given something that isn't an array at all, such as "hey", 42 or no argument
// When passed to the max function
// Then it should throw Error("findMax requires an array of numbers")
test("given an empty array, returns -Infinity", () => {
expect(findMax([])).toEqual(-Infinity);
});

test("given an array with one number, returns that number", () => {
expect(findMax([10])).toEqual(10);
});

test("given an array with positive and negative numbers, returns largest number overall", () => {
expect(findMax([10, 58, -74, 984, -87, -995, 47])).toEqual(984);
});

test("given an array with only negative numbers, returns number closest to zero", () => {
expect(findMax([-58, -65, -7, -2, -96, -753])).toEqual(-2);
});

test("given an array with decimal numbers, returns largest decimal number", () => {
expect(findMax([10, 9.5, 65.6, 41, 36.8])).toEqual(65.6);
});

test("given an array with with value that isn't a number, throws error", () => {
expect(() => findMax([10, 23, 18, "hello", 89, 2])).toThrow(
new Error("findMax requires an array of numbers")
);
});

test("given something that isn't an array, throws error", () => {
expect(() => findMax("Hello")).toThrow(
new Error("findMax requires an array of numbers")
);
});
28 changes: 27 additions & 1 deletion Sprint-1/implement/mean.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
function calculateMean(list) {}
function calculateMean(list) {
if (!Array.isArray(list)) {
throw new Error("calculateMean requires an array of numbers");
}

if (list.length === 0) {
throw new Error("calculateMean requires a non-empty array");
}

if (
!list.every(
(currentValue) =>
typeof currentValue === "number" && !Number.isNaN(currentValue)
)
Comment on lines +11 to +14

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  • Infinity is also a value of type "number".

  • Could also consider importing sum() from sum.js and use it in this script.

) {
throw new Error("calculateMean requires an array of numbers");
}

let total = 0;

for (number of list) {
total += number;
}
const mean = total / list.length;

return mean;
}

module.exports = calculateMean;
64 changes: 35 additions & 29 deletions Sprint-1/implement/mean.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,38 @@ const calculateMean = require("./mean.js");

// Acceptance criteria:

// Given an array of numbers
// When passed to calculateMean
// Then it should return their mean
// Delete this test.todo and replace it with a test.
test.todo("given [1, 2, 6], returns 3");

// Given an array with a single number
// When passed to calculateMean
// Then it should return that number

// Given an array containing negative or decimal numbers
// When passed to calculateMean
// Then it should return the correct mean

// Given an empty array
// When passed to calculateMean
// Then it should throw Error("calculateMean requires a non-empty array")

// Given a value that isn't an array, e.g. "banana", 42, null or {}
// When passed to calculateMean
// Then it should throw Error("calculateMean requires an array of numbers")

// Given no argument at all
// When passed to calculateMean
// Then it should throw Error("calculateMean requires an array of numbers")

// Given an array containing a non-number value, e.g. [1, "2", 3]
// When passed to calculateMean
// Then it should throw Error("calculateMean requires an array of numbers")
test("given [1, 2, 6], returns 3", () => {
expect(calculateMean([1, 2, 6])).toEqual(3);
});

test("given array with single number, return that number", () => {
expect(calculateMean([5])).toEqual(5);
});

test("given an array with positive and negative numbers, return the correct mean", () => {
expect(calculateMean([5, 8, -2, 7, -9])).toBeCloseTo(1.8);
});

test("given an empty array, throw an error", () => {
expect(() => calculateMean([])).toThrow(
"calculateMean requires a non-empty array"
);
});

test("given something that isn't an array, throw an error", () => {
expect(() => calculateMean("banana")).toThrow(
"calculateMean requires an array of numbers"
);
});

test("given no argument, throw an error", () => {
expect(() => calculateMean()).toThrow(
"calculateMean requires an array of numbers"
);
});

test("given an array with non-number values, throw an error", () => {
expect(() => calculateMean([1, 5, 9, "hello", 4])).toThrow(
"calculateMean requires an array of numbers"
);
});
18 changes: 18 additions & 0 deletions Sprint-1/implement/sum.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
function sum(elements) {
if (!Array.isArray(elements)) {
throw new Error("sum requires an array of numbers");
}

if (
!elements.every(
(currentValue) =>
typeof currentValue === "number" && !Number.isNaN(currentValue)
)
) {
throw new Error("sum requires an array of numbers");
}

let total = 0;
for (number of elements) {
total += number;
}
return total;
}

module.exports = sum;
51 changes: 27 additions & 24 deletions Sprint-1/implement/sum.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,30 @@ const sum = require("./sum.js");

// Acceptance Criteria:

// Given an empty array
// When passed to the sum function
// Then it should return 0
test.todo("given an empty array, returns 0")

// Given an array with just one number
// When passed to the sum function
// Then it should return that number

// Given an array containing negative numbers
// When passed to the sum function
// Then it should still return the correct total sum

// Given an array with decimal/float numbers
// When passed to the sum function
// Then it should return the correct total sum

// Given an array containing a value that isn't a number
// When passed to the sum function
// Then it should throw Error("sum requires an array of numbers")

// Given something that isn't an array at all, such as "hey", 42 or no argument
// When passed to the sum function
// Then it should throw Error("sum requires an array of numbers")
test("given an empty array, returns 0", () => {
expect(sum([])).toEqual(0);
});

test("given an array with one number, return that number", () => {
expect(sum([5])).toEqual(5);
});

test("given an array with negative numbers, correctly return sum of numbers", () => {
expect(sum([-10, -45, -6, -74])).toEqual(-135);
});

test("given an array with decimal numbers, correctly return total sum", () => {
expect(sum([12.7, 3.6, 32.48, 8, 32.53])).toEqual(89.31);
});
Comment on lines +25 to +27

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Decimal numbers in most programming languages (including JS) are internally represented in "floating point number" format. Floating point arithmetic is not exact.

console.log(12.7 + 3.6 + 32.48 + 8 + 32.53   == 89.31);  // true

// When numbers are added in different order
console.log(32.53 + 8 + 32.48 + 3.6 + 12.7   == 89.31);  // false

Can you find a more appropriate way to test a value (that involves decimal number calculations) for equality?

Suggestion: Look up

  • Checking equality in floating point arithmetic in JavaScript
  • Checking equality in floating point arithmetic with Jest


test("given an array with some values that aren't numbers, throw an Error", () => {
expect(() => {
sum([10, 8, 32, "hello", 2, 53, 32]);
}).toThrow(new Error("sum requires an array of numbers"));
});

test("given something that isn't an array, throw an Error", () => {
expect(() => {
sum("hello");
}).toThrow(new Error("sum requires an array of numbers"));
});
Loading
Loading