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
34 changes: 26 additions & 8 deletions src/reduce.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,37 @@
* @returns {*}
*/
function reduce(callback, startValue) {
let prev = startValue;
let startIndex = 0;
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}

const arr = this;
const length = arr.length;
let i = 0;
let accumulator;

if (arguments.length >= 2) {
accumulator = startValue;
} else {
// Find first defined element
while (i < length && !(i in arr)) {
i++;
}

if (arguments.length < 2) {
startIndex = 1;
prev = this[0];
if (i >= length) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = arr[i];
i++;
}

for (let i = startIndex; i < this.length; i++) {
prev = callback(prev, this[i], i, this);
for (; i < length; i++) {
if (i in arr) {
accumulator = callback(accumulator, arr[i], i, arr);
}
}

return prev;
return accumulator;
}

Choose a reason for hiding this comment

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

You're correctly testing that a TypeError should be thrown, but it looks like you're calling a function named reduce2 which isn't defined. You should use your callReduce helper here to ensure you're testing your custom reduce function.

module.exports = { reduce };
77 changes: 70 additions & 7 deletions src/reduce.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,81 @@

const { reduce } = require('./reduce');

describe('reduce', () => {
beforeAll(() => {
Array.prototype.reduce2 = reduce; // eslint-disable-line
describe('custom reduce', () => {
const callReduce = (arr, ...args) =>
reduce.call(arr, ...args);

test('reduces with an initial value', () => {
const arr = [1, 2, 3];
const result = callReduce(arr, (acc, v) => acc + v, 0);

expect(result).toBe(6);
});

test('reduces without an initial value', () => {
const arr = [1, 2, 3];
const result = callReduce(arr, (acc, v) => acc + v);

expect(result).toBe(6);
});

test('uses first element as initial accumulator when none provided', () => {
const arr = [5, 10, 15];
const result = callReduce(arr, (acc, v) => acc + v);

expect(result).toBe(30);
});

test('throws TypeError when callback is not a function', () => {
const arr = [1, 2, 3];

expect(() => callReduce(arr, null, 0)).toThrow(TypeError);
});

afterAll(() => {
delete Array.prototype.reduce2;
test('throws TypeError on empty array without initial value', () => {
expect(() => callReduce([], (a, b) => a + b)).toThrow(TypeError);
});

it('should ', () => {
test('passes correct arguments to callback', () => {
const arr = [10, 20, 30];
const mock = jest.fn((acc, v) => acc + v);

callReduce(arr, mock, 0);

expect(mock).toHaveBeenCalledTimes(3);
expect(mock.mock.calls[0]).toEqual([0, 10, 0, arr]);
expect(mock.mock.calls[1]).toEqual([10, 20, 1, arr]);
expect(mock.mock.calls[2]).toEqual([30, 30, 2, arr]);
});

// Add tests here
test('works with objects', () => {
const arr = [{ x: 1 }, { x: 2 }, { x: 3 }];
const result = callReduce(arr, (acc, obj) => acc + obj.x, 0);

expect(result).toBe(6);
});

test('skips empty slots in a sparse array', () => {
// eslint-disable-next-line no-sparse-arrays
const arr = [1, , 3]; // A sparse array
const callback = jest.fn((sum, el) => sum + el);
const result = callReduce(arr, callback, 0);

expect(callback).toHaveBeenCalledTimes(2); // Only indices 0 and 2 exist
expect(result).toBe(4); // 0 + 1 + 3
});

test('single-element arr without initial value returns that element', () => {
const arr = [42];
const result = callReduce(arr, (acc, v) => acc + v);

expect(result).toBe(42);
});

test('single-element array with initial value applies callback once', () => {
const arr = [42];
const result = callReduce(arr, (acc, v) => acc + v, 10);

expect(result).toBe(52);
});
});