Skip to content
Closed
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
32 changes: 29 additions & 3 deletions Sprint-1/fix/median.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,36 @@

// Hint: Please consider scenarios when 'list' doesn't have numbers (the function is expected to return null)
// 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 null when input is not an array (tests expect null for invalid input)
if (!Array.isArray(list)) {
return null;
}

// Keep only actual numbers. This filters out strings, null, undefined, objects, etc.
// We deliberately do not coerce numeric strings (e.g. "3") to numbers — tests expect
// non-number values to be ignored rather than parsed.
const numericList = list.filter((item) => typeof item === "number" && Number.isFinite(item));

// If there are no numeric values, return null (the function should not attempt
// to compute a median from non-numeric or empty input).
if (numericList.length === 0) {
return null;
}

// Sort numerically. Using slice() first ensures we don't mutate the filtered
// array in case the original caller relies on references; the tests also assert
// the original `list` should not be modified.
const sorted = numericList.slice().sort((a, b) => a - b);

// Compute middle index. For odd lengths the middle element is the median.
// For even lengths the median is the average of the two middle elements.
const middleIndex = Math.floor(sorted.length / 2);
const median = sorted.length % 2 === 1
? sorted[middleIndex]
: (sorted[middleIndex - 1] + sorted[middleIndex]) / 2;

// Return the computed median (number).
return median;
}

Expand Down
22 changes: 17 additions & 5 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))
);
});
58 changes: 57 additions & 1 deletion Sprint-1/implement/dedupe.js
Original file line number Diff line number Diff line change
@@ -1 +1,57 @@
function dedupe() {}
/*
notes:

- One-line summary: return a new array with duplicates removed, preserving
the order of first occurrences.
- Input handling: null or undefined -> return [], any other non-array -> throw.
- Key idea: use a Set to track values we've seen; push unseen items to result.

Quick checks:
1) If nothing changes, test with: node -e "console.log(require('./implement/dedupe')([1,1,2]))"
2) If order looks wrong, remember we preserve first occurrences.
3) If unexpected equality (objects), remember objects are compared by reference.

Tiny TODOs (one small step each):
- [ ] Consider accepting array-like objects? (optional)
- [ ] Decide whether to treat non-array values consistently (return [] vs throw)

Short examples:
dedupe([1,2,1]) -> [1,2]
dedupe(null) -> []
dedupe([{a:1},{a:1}]) -> [{a:1},{a:1}] // different refs stay, not deduped by shape

Implementation (no behavior changes below):
*/

function dedupe(arr) {
// Quick guard: treat null/undefined as empty lists — doesn't throw, just returns []
if (arr === null || arr === undefined) {
return [];
}

// If it's not an array, fail fast — makes bugs obvious (helps debugging)
if (!Array.isArray(arr)) {
throw new TypeError("Input must be an array");
}

// seen: keep track of values we've already added (O(1) lookup)
const seen = new Set();
// result: build new array, preserve first-seen order
const result = [];

// iterate in order; if unseen -> keep it
for (const item of arr) {
if (!seen.has(item)) {
seen.add(item);
result.push(item);
}
}

// return the deduped array
return result;
}

module.exports = dedupe;



56 changes: 52 additions & 4 deletions Sprint-1/implement/dedupe.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const dedupe = require("./dedupe.js");

/*
Dedupe Array

Expand All @@ -16,13 +16,61 @@ E.g. dedupe([1, 2, 1]) returns [1, 2]
// 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");

const dedupe = require("./dedupe.js");

describe("dedupe", () => {
test("given an empty array, it returns an empty array", () => {
expect(dedupe([])).toEqual([]);
});
});

test("throws for non-array input", () => {
expect(() => dedupe(42)).toThrow(TypeError);
expect(() => dedupe("string")).toThrow(TypeError);
expect(() => dedupe({})).toThrow(TypeError);
});

test("does not mutate the input array", () => {
const input = [1, 2, 3];
dedupe(input);
expect(input).toEqual([1, 2, 3]);
});

test("objects are compared by reference", () => {
const a = { a: 1 };
const b = { a: 1 };
expect(dedupe([a, b, a])).toEqual([a, b]);
});

test('null or undefined returns empty array', () => {
expect(dedupe(null)).toEqual([]);
expect(dedupe(undefined)).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, it returns a copy of the original array", () => {
expect(dedupe(['a', 'b', 'c'])).toEqual(['a', 'b', 'c']);
expect(dedupe([5, 1, 2, 3, 8])).toEqual([5, 1, 2, 3, 8]);
expect(dedupe([1, 2])).toEqual([1, 2]);
expect(dedupe(['apple', 'banana', 'orange'])).toEqual(['apple', 'banana', 'orange']);
expect(dedupe(['Monday', 'Tuesday', 'Wednesday'])).toEqual(['Monday', 'Tuesday', 'Wednesday']);
});
// 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
// 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 of strings or numbers, it returns a new array with duplicates removed while preserving the first occurrence of each element", () => {
expect(dedupe(['a', 'b', 'a', 'c', 'b'])).toEqual(['a', 'b', 'c']);
expect(dedupe([1, 2, 1, 3, 2])).toEqual([1, 2, 3]);
expect(dedupe([1, 2, 1])).toEqual([1, 2]);
expect(dedupe(['apple', 'banana', 'apple', 'orange', 'banana'])).toEqual(['apple', 'banana', 'orange']);
expect(dedupe([true, false, true])).toEqual([true, false]);
});

test("given an array with mixed types, it returns a new array with duplicates removed while preserving the first occurrence", () => {
expect(dedupe([1, '1', 2, '2', 1])).toEqual([1, '1', 2, '2']);
expect(dedupe(['apple', 1, 'apple', 2])).toEqual(['apple', 1, 2]);
});
10 changes: 10 additions & 0 deletions Sprint-1/implement/max.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
function findMax(elements) {
let max = -Infinity;
for (const value of elements) {
if (typeof value === "number") {
max = Math.max(max, value);
}
}
return max;
}




module.exports = findMax;
64 changes: 45 additions & 19 deletions Sprint-1/implement/max.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,54 @@ 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");
describe("findMax", () => {
test("should find the maximum positive number", () => {
expect(findMax([1, 2, 3, 4, 5])).toBe(5);
});

// Given an array with one number
// When passed to the max function
// Then it should return that number
test("should return -Infinity if array contains no numbers", () => {
expect(findMax(['a', 'b', 'c'])).toBe(-Infinity);
});

// 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 one number
// When passed to the max function
// Then it should return that number
test("should handle an array with a single number", () => {
expect(findMax([42])).toBe(42);
});

// 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 both positive and negative numbers
// When passed to the max function
// Then it should return the largest number overall
test("should find the maximum number in a mixed array", () => {
expect(findMax([-10, 0, 10, 20])).toBe(20);
});

// Given an array with decimal numbers
// When passed to the max function
// Then it should return the largest decimal number
// Given an array with just negative numbers
// When passed to the max function
// Then it should return the closest one to zero
test("should work correctly with negative numbers", () => {
expect(findMax([-10, -20, -30])).toBe(-10);
});

// Given an array with non-number values
// When passed to the max function
// Then it should return the max and ignore non-numeric values
// Given an array with decimal numbers
// When passed to the max function
// Then it should return the largest decimal number
test("should work correctly with decimal numbers", () => {
expect(findMax([1.5, 2.5, 3.5])).toBe(3.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
// 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("should ignore non-number values" , () => {
expect(findMax(["a", "b", "c", 40])).toBe(40);
});

// 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, returns -Infinity", () => {
expect(findMax(["a", "b", "c"])).toBe(-Infinity);
});
});
7 changes: 7 additions & 0 deletions Sprint-1/implement/sum.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
function sum(elements) {
let total = 0;
for (let i = 0; i < elements.length; i++) {
if (typeof elements[i] === "number") {
total += elements[i];
}
}
return total;
}

module.exports = sum;
26 changes: 24 additions & 2 deletions Sprint-1/implement/sum.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,46 @@ const sum = require("./sum.js");
// Given an empty array
// When passed to the sum function
// Then it should return 0
test.todo("given an empty array, returns 0")
describe("sum", () => {
test("given an empty array, returns 0", () => {
expect(sum([])).toBe(0);
});

test("given an array with just one number, returns that number", () => {
expect(sum([42])).toBe(42);
});
});

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

test("should sum an array of positive numbers", () => {
expect(sum([1, 2, 3, 4, 5])).toBe(15);
});
// Given an array containing negative numbers
// When passed to the sum function
// Then it should still return the correct total sum
test("should sum an array containing negative numbers", () => {
expect(sum([-1, -2, -3, -4, -5])).toBe(-15);
});

// Given an array with decimal/float numbers
// When passed to the sum function
// Then it should return the correct total sum
test("should sum an array of decimal numbers", () => {
expect(sum([1.5, 2.5, 3.5])).toBe(7.5);
});

// Given an array containing non-number values
// When passed to the sum function
// Then it should ignore the non-numerical values and return the sum of the numerical elements
test("should ignore non-number values", () => {
expect(sum(["a", "b", "c", 40])).toBe(40);
});

// Given an array with only non-number values
// When passed to the sum 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, returns 0", () => {
expect(sum(["a", "b", "c"])).toBe(0);
});
10 changes: 10 additions & 0 deletions Sprint-1/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
// Treat Sprint-1 as the root for Jest when running from this package
rootDir: '.',
// Match tests inside the `fix` and `implement` folders
// so running `npm test -- fix` will also pick up implement tests like dedupe.test.js
testMatch: ['<rootDir>/fix/**/*.test.js', '<rootDir>/implement/**/*.test.js'],
// Ignore node_modules inside Sprint-1
testPathIgnorePatterns: ['/node_modules/'],
modulePathIgnorePatterns: ['/node_modules/'],
};
3 changes: 1 addition & 2 deletions Sprint-1/refactor/includes.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Refactor the implementation of includes to use a for...of loop

function includes(list, target) {
for (let index = 0; index < list.length; index++) {
const element = list[index];
for (const element of list) {
if (element === target) {
return true;
}
Expand Down
Loading
Loading