Skip to content
34 changes: 30 additions & 4 deletions Sprint-1/fix/median.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,35 @@
// or 'list' has mixed values (the function is expected to sort only numbers).

function calculateMedian(list) {
const middleIndex = Math.floor(list.length / 2);
const median = list.splice(middleIndex, 1)[0];
return median;
// Check that the input passed to the function is an array
if (!Array.isArray(list)) {
return null;
}

// Filter valid numbers and store them in `validNumbers`
const validNumbers = list.filter(
(item) => typeof item === "number" && !isNaN(item)
);

// Return `null` if there are no valid numbers
if (validNumbers.length === 0) {
return null;
}

// Sort `validNumbers` in ascending order
// Sort validNumbers (safe to mutate because it's a new array created by filter)
validNumbers.sort((a, b) => a - b);

// Calculate the middle index of the sorted array
const middleIndex = Math.floor(validNumbers.length / 2);

// If the array has an odd number of elements, return the middle element
if (validNumbers.length % 2 !== 0) {
return validNumbers[middleIndex];
} else {
// If the array has an even number of elements, return the average of the two middle elements
return (validNumbers[middleIndex - 1] + validNumbers[middleIndex]) / 2;
}
}

module.exports = calculateMedian;
module.exports = calculateMedian;
24 changes: 18 additions & 6 deletions Sprint-1/fix/median.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ describe("calculateMedian", () => {
{ input: [1, 2, 3, 4], expected: 2.5 },
{ input: [1, 2, 3, 4, 5, 6], expected: 3.5 },
].forEach(({ input, expected }) =>
it(`returns the median for [${input}]`, () => expect(calculateMedian(input)).toEqual(expected))
it(`returns the median for [${input}]`, () =>
expect(calculateMedian(input)).toEqual(expected))
);

[
Expand All @@ -24,7 +25,8 @@ describe("calculateMedian", () => {
{ input: [110, 20, 0], expected: 20 },
{ input: [6, -2, 2, 12, 14], expected: 6 },
].forEach(({ input, expected }) =>
it(`returns the correct median for unsorted array [${input}]`, () => expect(calculateMedian(input)).toEqual(expected))
it(`returns the correct median for unsorted array [${input}]`, () =>
expect(calculateMedian(input)).toEqual(expected))
);

it("doesn't modify the input array [3, 1, 2]", () => {
Expand All @@ -33,8 +35,17 @@ describe("calculateMedian", () => {
expect(list).toEqual([3, 1, 2]);
});

[ 'not an array', 123, null, undefined, {}, [], ["apple", null, undefined] ].forEach(val =>
it(`returns null for non-numeric array (${val})`, () => expect(calculateMedian(val)).toBe(null))
[
"not an array",
123,
null,
undefined,
{},
[],
["apple", null, undefined],
].forEach((val) =>
it(`returns null for non-numeric array (${val})`, () =>
expect(calculateMedian(val)).toBe(null))
);

[
Expand All @@ -45,6 +56,7 @@ describe("calculateMedian", () => {
{ input: [3, "apple", 1, null, 2, undefined, 4], expected: 2.5 },
{ input: ["banana", 5, 3, "apple", 1, 4, 2], expected: 3 },
].forEach(({ input, expected }) =>
it(`filters out non-numeric values and calculates the median for [${input}]`, () => expect(calculateMedian(input)).toEqual(expected))
it(`filters out non-numeric values and calculates the median for [${input}]`, () =>
expect(calculateMedian(input)).toEqual(expected))
);
});
});
44 changes: 43 additions & 1 deletion Sprint-1/implement/dedupe.js
Original file line number Diff line number Diff line change
@@ -1 +1,43 @@
function dedupe() {}
// This defines a function named dedupe with a parameter arrayInput. arrayInput should be an array.
function dedupe(arrayInput) {
// This checks whether the input is not an array.
if (!Array.isArray(arrayInput)) {
// If the input is not an array, the function stops, throws a TypeError and shows an error message saying:“Input must be an array.”
throw new TypeError("Input must be an array");
}
// This checks if the array is empty (has no elements).
if (arrayInput.length === 0) {
// If the array is empty, it returns an empty array.
return [];
}

// This declares a new variable `newArray` which will hold a reference to an empty array in memory
const newArray = [];
// This decares a new varibale `seen` which will hold a reference to the actual set object in memory
const seen = new Set(); // variable seen keeps track of all items that have already been added to the result array
// seen is a Set object that remembers items,

// newArray is an array that holds the actual result.

// This starts a loop that goes through each element in the input array one by one.
for (const item of arrayInput) {
// This big condition checks two things before adding an item:

// !seen.has(item) — The item has not been added before.

// The item is either a number (but not NaN) or a string.
// So only real numbers and strings that haven’t appeared yet will be kept.
if (
!seen.has(item) &&
((typeof item === "number" && !Number.isNaN(item)) ||
typeof item === "string")
) {
newArray.push(item); // If the item passes the condition, it gets added to the new array.
seen.add(item); // The item is then recorded in the Set, so it won’t be added again later.
}
}
// After the loop finishes, return the final deduplicated array
return newArray; // After checking all items, the function returns the new array with duplicates removed and invalid items filtered out.
}

module.exports = dedupe; // This line allows the dedupe function to be used in other files (like the test file).
90 changes: 78 additions & 12 deletions Sprint-1/implement/dedupe.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
const dedupe = require("./dedupe.js");

/*
Dedupe Array

📖 Dedupe means **deduplicate**
📖 Dedupe means **dedupe**

In this kata, you will need to deduplicate the elements of an array
In this kata, you will need to dedupe the elements of an array

E.g. dedupe(['a','a','a','b','b','c']) target output: ['a','b','c']
E.g. dedupe([5, 1, 1, 2, 3, 2, 5, 8]) target output: [5, 1, 2, 3, 8]
Expand All @@ -13,15 +14,80 @@ E.g. dedupe([1, 2, 1]) target output: [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");
describe("dedupe - valid inputs", () => {
// Given an empty array
// When passed to the dedupe function
// Then it should return an empty array
[{ input: [], expected: [] }].forEach(({ input, expected }) =>
test(`returns an empty array for input [${input}]`, () => {
const result = dedupe(input);
expect(result).toEqual(expected);
expect(result).not.toBe(input); // ensures it's a new array
})
);

// Given an array with no duplicates
// When passed to the dedupe function
// Then it should return a copy of the original array
[
{ input: [1, 2, 3], expected: [1, 2, 3] },
{ input: ["a", "b", "car"], expected: ["a", "b", "car"] },
{ input: [0, 1, 5, 6], expected: [0, 1, 5, 6] },
].forEach(({ input, expected }) =>
test(`returns a copy of the array when no duplicates exist for input [${input}]`, () => {
const result = dedupe(input);
expect(result).toEqual(expected);
expect(result).not.toBe(input); // ensures it's a new array
})
);

// Given arrays with duplicates
[
{ input: [5, 1, 1, 2, 3, 2, 5, 8], expected: [5, 1, 2, 3, 8] },
{ input: [1, 1, 1, 1], expected: [1] },
{ input: ["a", "a", "a", "b", "b", "c"], expected: ["a", "b", "c"] },
].forEach(({ input, expected }) =>
test(`removes duplicate elements and keeps the first occurrence for [${input}]`, () => {
const result = dedupe(input);
expect(result).toEqual(expected);
expect(result).not.toBe(input); // ensures it's a new array
})
);

// Given arrays with numbers and strings mixed
[
{ input: ["1", 1, "1", 1], expected: ["1", 1] },
{ input: [2, "3", "hello", 5, 5], expected: [2, "3", "hello", 5] },
].forEach(({ input, expected }) =>
test(`removes duplicates from an array of numbers and strings, keeping the first occurrence for input [${input}]`, () => {
const result = dedupe(input);
expect(result).toEqual(expected);
expect(result).not.toBe(input); // ensures it's a new array
})
);

// Given arrays with invalid elements
[
{ input: [1, 2, true, 3, null, "a", {}], expected: [1, 2, 3, "a"] },
{ input: ["x", undefined, "x", "y", []], expected: ["x", "y"] },
{ input: [false, 5, 5, "hello", () => {}, 5], expected: [5, "hello"] },
{ input: [1, NaN, 2, NaN, "hello"], expected: [1, 2, "hello"] },
].forEach(({ input, expected }) =>
test(`filters invalid elements, removes duplicates, and returns a copy for input [${input}]`, () => {
const result = dedupe(input);
expect(result).toEqual(expected);
expect(result).not.toBe(input); // ensures it's a new array
})
);
});

// Given an array with no duplicates
// When passed to the dedupe function
// Then it should return a copy of the original array
describe("dedupe - invalid inputs", () => {
const invalidInputs = [null, undefined, 123, "string", {}, () => {}];

// Given an array with strings or numbers
// When passed to the dedupe function
// Then it should remove the duplicate values, preserving the first occurence of each element
invalidInputs.forEach((input) =>
test(`throws typeError when input is ${String(input)}`, () => {
expect(() => dedupe(input)).toThrow(TypeError);
expect(() => dedupe(input)).toThrow("Input must be an array");
})
);
});
10 changes: 9 additions & 1 deletion Sprint-1/implement/max.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
function findMax(elements) {
if (!Array.isArray(elements)) {
throw new TypeError("Input must be an array");
} else {
const filteredArray = elements.filter(
(ele) => typeof ele === "number" && !isNaN(ele)
)
return Math.max(...filteredArray);
}
}

module.exports = findMax;
module.exports = findMax;
50 changes: 49 additions & 1 deletion Sprint-1/implement/max.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,76 @@ const findMax = require("./max.js");
// 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");
test("given an empty array, returns -Infinity", () => {
expect(findMax([])).toBe(-Infinity);
});

// Given an array with one number
// When passed to the max function
// Then it should return that number
test("given an array with one number, returns that number", () => {
expect(findMax([8])).toBe(8);
expect(findMax([-42])).toBe(-42);
expect(findMax([0])).toBe(0);
});

// Given an array with both positive and negative numbers
// When passed to the max function
// Then it should return the largest number overall
test("given an array with both positive and negative numbers, returns the largest number overall", () => {
expect(findMax([10, -5, 20, 0])).toBe(20);
expect(findMax([-1, -100, -50, 30])).toBe(30);
expect(findMax([0, -10, 5])).toBe(5);
});

// Given an array with just negative numbers
// When passed to the max function
// Then it should return the closest one to zero
test("given an array with just negative numbers, returns the closest one to zero", () => {
expect(findMax([-10, -5, -20])).toBe(-5);
expect(findMax([-100, -50, -60])).toBe(-50);
expect(findMax([-1000, -60, -1])).toBe(-1);
});

// Given an array with decimal numbers
// When passed to the max function
// Then it should return the largest decimal number
test("given an array with decimal numbers, returns the largest decimal number", () => {
expect(findMax([1.1, 2.5, 0.3])).toBe(2.5);
expect(findMax([-1.2, -0.5, -0.1])).toBe(-0.1);
expect(findMax([-1000.23, -80.54, -63.110])).toBe(-63.110);
});

// Given an array with non-number values
// When passed to the max function
// Then it should return the max and ignore non-numeric values
test("given an array with non-number values, returns the max and ignore the non-numeric values", () => {
expect(findMax([10, "hi", 20, null])).toBe(20);
expect(findMax([0, false, 5, "a"])).toBe(5);
expect(findMax([undefined, 5, null, NaN, -9])).toBe(5);
});

// Given an array with only non-number values
// When passed to the max function
// Then it should return the least surprising value given how it behaves for all other inputs
test("given an array with only non-number values", () => {
expect(findMax(["a", null, undefined])).toBe(-Infinity);
expect(findMax([{}, [], "hi"])).toBe(-Infinity);
expect(findMax([true, false, true])).toBe(-Infinity);
expect(findMax([Symbol("x"), () => {}, function () {}])).toBe(-Infinity);
});

// Edge cases
// Given an array with non-array values
// when passed to the max function
// Then it should throw and error
test("given a non-array input, throws a TypeError with message 'Input must be an array'", () => {
expect(() => findMax(42)).toThrow(new TypeError("Input must be an array"));
expect(() => findMax("hello")).toThrow(
new TypeError("Input must be an array")
);
expect(() => findMax(null)).toThrow(new TypeError("Input must be an array"));
expect(() => findMax(undefined)).toThrow(
new TypeError("Input must be an array")
);
});
14 changes: 13 additions & 1 deletion Sprint-1/implement/sum.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
function sum(elements) {
if (!Array.isArray(elements)) {
throw new TypeError("Input must be an array");
}
const filteredElements = elements.filter(
(ele) => typeof ele === "number" && !isNaN(ele)
);
let sumElements = 0;

for (const item of filteredElements) {
sumElements += item;
}
return sumElements;
}

module.exports = sum;
module.exports = sum;
Loading