From dd734e285cf7e00060c564658a77770eca3b8b6a Mon Sep 17 00:00:00 2001 From: Zadri Abdule Date: Tue, 7 Jul 2026 00:33:16 +0100 Subject: [PATCH 1/6] updated fix/median.js to make tests pass --- Sprint-1/fix/median.js | 31 +++++++++++++++++++++++++++++-- Sprint-1/fix/median.test.js | 22 +++++++++++++++++----- Sprint-1/implement/dedupe.test.js | 2 +- Sprint-1/implement/max.js | 3 +-- Sprint-1/implement/sum.js | 3 +-- Sprint-1/implement/sum.test.js | 2 +- Sprint-1/jest.config.js | 9 +++++++++ 7 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 Sprint-1/jest.config.js diff --git a/Sprint-1/fix/median.js b/Sprint-1/fix/median.js index b22590bc6..674fc4587 100644 --- a/Sprint-1/fix/median.js +++ b/Sprint-1/fix/median.js @@ -6,8 +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 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" && !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; } diff --git a/Sprint-1/fix/median.test.js b/Sprint-1/fix/median.test.js index 21da654d7..c262c3776 100644 --- a/Sprint-1/fix/median.test.js +++ b/Sprint-1/fix/median.test.js @@ -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)) ); [ @@ -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]", () => { @@ -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)) ); [ @@ -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)) ); }); diff --git a/Sprint-1/implement/dedupe.test.js b/Sprint-1/implement/dedupe.test.js index d7c8e3d8e..3a7fc548f 100644 --- a/Sprint-1/implement/dedupe.test.js +++ b/Sprint-1/implement/dedupe.test.js @@ -24,5 +24,5 @@ test.todo("given an empty array, it returns an empty array"); // 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. diff --git a/Sprint-1/implement/max.js b/Sprint-1/implement/max.js index 6dd76378e..69ca11e9c 100644 --- a/Sprint-1/implement/max.js +++ b/Sprint-1/implement/max.js @@ -1,4 +1,3 @@ -function findMax(elements) { -} +function findMax(elements) {} module.exports = findMax; diff --git a/Sprint-1/implement/sum.js b/Sprint-1/implement/sum.js index 9062aafe3..873b75249 100644 --- a/Sprint-1/implement/sum.js +++ b/Sprint-1/implement/sum.js @@ -1,4 +1,3 @@ -function sum(elements) { -} +function sum(elements) {} module.exports = sum; diff --git a/Sprint-1/implement/sum.test.js b/Sprint-1/implement/sum.test.js index dd0a090ca..38f6f252e 100644 --- a/Sprint-1/implement/sum.test.js +++ b/Sprint-1/implement/sum.test.js @@ -13,7 +13,7 @@ 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") +test.todo("given an empty array, returns 0"); // Given an array with just one number // When passed to the sum function diff --git a/Sprint-1/jest.config.js b/Sprint-1/jest.config.js new file mode 100644 index 000000000..079d3cdb1 --- /dev/null +++ b/Sprint-1/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + // Treat Sprint-1 as the root for Jest when running from this package + rootDir: '.', + // Only match tests inside the `fix` folder (you can expand this if needed) + testMatch: ['/fix/**/*.test.js'], + // Ignore node_modules inside Sprint-1 + testPathIgnorePatterns: ['/node_modules/'], + modulePathIgnorePatterns: ['/node_modules/'], +}; From 135bf30b2449ca59fe14c860ee212e988f75d102 Mon Sep 17 00:00:00 2001 From: Zadri Abdule Date: Wed, 8 Jul 2026 15:47:34 +0100 Subject: [PATCH 2/6] added dedupe implementation and tests --- Sprint-1/fix/median.js | 3 +- Sprint-1/implement/dedupe.js | 58 ++++++++++++++++++++++++++++++- Sprint-1/implement/dedupe.test.js | 54 ++++++++++++++++++++++++++-- Sprint-1/jest.config.js | 5 +-- 4 files changed, 112 insertions(+), 8 deletions(-) diff --git a/Sprint-1/fix/median.js b/Sprint-1/fix/median.js index 674fc4587..f8d868fca 100644 --- a/Sprint-1/fix/median.js +++ b/Sprint-1/fix/median.js @@ -4,7 +4,6 @@ // 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) { // Return null when input is not an array (tests expect null for invalid input) if (!Array.isArray(list)) { @@ -14,7 +13,7 @@ function calculateMedian(list) { // 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" && !isFinite(item)); + 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). diff --git a/Sprint-1/implement/dedupe.js b/Sprint-1/implement/dedupe.js index 781e8718a..08a244934 100644 --- a/Sprint-1/implement/dedupe.js +++ b/Sprint-1/implement/dedupe.js @@ -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; + + + diff --git a/Sprint-1/implement/dedupe.test.js b/Sprint-1/implement/dedupe.test.js index 3a7fc548f..5f89586a5 100644 --- a/Sprint-1/implement/dedupe.test.js +++ b/Sprint-1/implement/dedupe.test.js @@ -1,4 +1,4 @@ -const dedupe = require("./dedupe.js"); + /* Dedupe Array @@ -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 // 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]); +}); diff --git a/Sprint-1/jest.config.js b/Sprint-1/jest.config.js index 079d3cdb1..c4036c4eb 100644 --- a/Sprint-1/jest.config.js +++ b/Sprint-1/jest.config.js @@ -1,8 +1,9 @@ module.exports = { // Treat Sprint-1 as the root for Jest when running from this package rootDir: '.', - // Only match tests inside the `fix` folder (you can expand this if needed) - testMatch: ['/fix/**/*.test.js'], + // Match tests inside the `fix` and `implement` folders + // so running `npm test -- fix` will also pick up implement tests like dedupe.test.js + testMatch: ['/fix/**/*.test.js', '/implement/**/*.test.js'], // Ignore node_modules inside Sprint-1 testPathIgnorePatterns: ['/node_modules/'], modulePathIgnorePatterns: ['/node_modules/'], From 91bb6480972f9f2f7733e66c4fe89335ce27bf59 Mon Sep 17 00:00:00 2001 From: Zadri Abdule Date: Wed, 8 Jul 2026 20:03:41 +0100 Subject: [PATCH 3/6] implemented findMax and added tests --- Sprint-1/implement/max.js | 13 ++++++- Sprint-1/implement/max.test.js | 64 ++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/Sprint-1/implement/max.js b/Sprint-1/implement/max.js index 69ca11e9c..79f06667c 100644 --- a/Sprint-1/implement/max.js +++ b/Sprint-1/implement/max.js @@ -1,3 +1,14 @@ -function findMax(elements) {} +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; diff --git a/Sprint-1/implement/max.test.js b/Sprint-1/implement/max.test.js index 82f18fd88..a2a041fce 100644 --- a/Sprint-1/implement/max.test.js +++ b/Sprint-1/implement/max.test.js @@ -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); + }); +}); From 050521682528872a0f82093af20a229db02781d7 Mon Sep 17 00:00:00 2001 From: Zadri Abdule Date: Wed, 8 Jul 2026 20:41:08 +0100 Subject: [PATCH 4/6] added sum implementation and tests --- Sprint-1/implement/sum.js | 10 +++++++++- Sprint-1/implement/sum.test.js | 26 ++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Sprint-1/implement/sum.js b/Sprint-1/implement/sum.js index 873b75249..8638400f5 100644 --- a/Sprint-1/implement/sum.js +++ b/Sprint-1/implement/sum.js @@ -1,3 +1,11 @@ -function sum(elements) {} +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; diff --git a/Sprint-1/implement/sum.test.js b/Sprint-1/implement/sum.test.js index 38f6f252e..7f4da067a 100644 --- a/Sprint-1/implement/sum.test.js +++ b/Sprint-1/implement/sum.test.js @@ -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); +}); From f1631e72b400a2e31e1733d5fed674b5bbb8e935 Mon Sep 17 00:00:00 2001 From: Zadri Abdule Date: Wed, 8 Jul 2026 20:59:20 +0100 Subject: [PATCH 5/6] refactored includes --- Sprint-1/refactor/includes.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sprint-1/refactor/includes.js b/Sprint-1/refactor/includes.js index 29dad81f0..8c9ae2e66 100644 --- a/Sprint-1/refactor/includes.js +++ b/Sprint-1/refactor/includes.js @@ -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; } From 9c4d4dd6d705ebdd9b6eb759491b75ca932b062c Mon Sep 17 00:00:00 2001 From: Zadri Abdule Date: Wed, 8 Jul 2026 21:38:14 +0100 Subject: [PATCH 6/6] completed aoc 2018 day 1 solutions --- Sprint-1/stretch/aoc-2018-day1/solution.js | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Sprint-1/stretch/aoc-2018-day1/solution.js b/Sprint-1/stretch/aoc-2018-day1/solution.js index e69de29bb..9263787c8 100644 --- a/Sprint-1/stretch/aoc-2018-day1/solution.js +++ b/Sprint-1/stretch/aoc-2018-day1/solution.js @@ -0,0 +1,32 @@ +const fs = require("fs"); +const path = require("path"); + +// Read input relative to this file so script can be run from anywhere +const data = fs.readFileSync(path.join(__dirname, "input.txt"), "utf8"); +const changes = data + .split(/\r?\n/) + .map((s) => s.trim()) + .filter(Boolean) + .map(Number); + +// Part 1: final frequency after one pass +const finalFrequency = changes.reduce((acc, change) => acc + change, 0); + +// Part 2: first repeated cumulative frequency (iterate the list repeatedly) +function findFirstDuplicate(arr) { + const seen = new Set([0]); + let freq = 0; + + while (true) { + for (const change of arr) { + freq += change; + if (seen.has(freq)) return freq; + seen.add(freq); + } + } +} + +const firstDuplicate = findFirstDuplicate(changes); + +console.log("Final frequency (part 1):", finalFrequency); +console.log("First repeated frequency (part 2):", firstDuplicate);