From e8b91bae4fd6a5f5542fcf23f6f3cfcf101b5730 Mon Sep 17 00:00:00 2001 From: Pascal Syma Date: Mon, 2 Dec 2024 14:10:59 +0100 Subject: [PATCH] Add jsdoc and unit tests for util functions --- utils/array.test.ts | 130 +++++++++++++++++++++++++++++++++++++++++++ utils/array.ts | 51 +++++++++++++---- utils/random.test.ts | 17 ++++++ utils/random.ts | 5 ++ 4 files changed, 192 insertions(+), 11 deletions(-) create mode 100644 utils/array.test.ts create mode 100644 utils/random.test.ts diff --git a/utils/array.test.ts b/utils/array.test.ts new file mode 100644 index 0000000..46ceee8 --- /dev/null +++ b/utils/array.test.ts @@ -0,0 +1,130 @@ +import { describe, expect, it } from 'bun:test'; +import { multArray, multBy, sumArray, sumBy } from './array.ts'; + +describe('array utils', () => { + it('sumArray number array', () => { + const array = [1, 2, 3]; + const sum = sumArray(array); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(1 + 2 + 3); + + expect(sumArray([])).toBeNumber(); + expect(sumArray([])).toBeFinite(); + expect(sumArray([])).toBe(0); + }); + it('sumArray read-only number array', () => { + const array = [1, 2, 3] as const; + const sum = sumArray(array); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(1 + 2 + 3); + + expect(sumArray([])).toBeNumber(); + expect(sumArray([])).toBeFinite(); + expect(sumArray([])).toBe(0); + }); + it('sumBy number array', () => { + const array = [1, 2, 3]; + const sum = sumBy(array, (e) => e); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(1 + 2 + 3); + }); + it('sumBy read-only number array', () => { + const array = [1, 2, 3] as const; + const sum = sumBy(array, (e) => e); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(1 + 2 + 3); + }); + it('sumBy mixed array', () => { + const array = [1, '2', 3.1, '']; + + const sum = sumBy(array, (e) => e); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(1 + 2 + 3.1); + }); + it('sumBy object array', () => { + const array: ( + | { value: number | string } + | { differentValue: number | string } + )[] = [{ value: 3 }, { value: '2' }, { differentValue: '-2.44' }]; + + const sum = sumBy(array, (e) => + 'differentValue' in e ? e.differentValue : e.value + ); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(3 + 2 - 2.44); + }); + it('sumBy empty array', () => { + expect(sumBy([], (e) => e)).toBeNumber(); + expect(sumBy([], (e) => e)).toBeFinite(); + expect(sumBy([], (e) => e)).toBe(0); + }); + + it('multArray number array', () => { + const array = [1, 2, 3]; + const sum = multArray(array); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(2 * 3); + + expect(multArray([])).toBeNumber(); + expect(multArray([])).toBeFinite(); + expect(multArray([])).toBe(0); + }); + it('multArray read-only number array', () => { + const array = [1, 2, 3] as const; + const sum = multArray(array); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(2 * 3); + + expect(multArray([])).toBeNumber(); + expect(multArray([])).toBeFinite(); + expect(multArray([])).toBe(0); + }); + it('multBy number array', () => { + const array = [1, 2, 3]; + const sum = sumBy(array, (e) => e); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(1 + 2 + 3); + }); + it('multBy read-only number array', () => { + const array = [1, 2, 3] as const; + const sum = multBy(array, (e) => e); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(1 + 2 + 3); + }); + it('multBy mixed array', () => { + const array = [1, '2', 3.1, '']; + + const sum = multBy(array, (e) => e); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(0); + }); + it('multBy object array', () => { + const array: ( + | { value: number | string } + | { differentValue: number | string } + )[] = [{ value: 3 }, { value: '2' }, { differentValue: '-2.44' }]; + + const sum = multBy(array, (e) => + 'differentValue' in e ? e.differentValue : e.value + ); + expect(sum).toBeNumber(); + expect(sum).toBeFinite(); + expect(sum).toBe(3 * 2 * -2.44); + }); + it('multBy empty array', () => { + expect(multBy([], (e) => e)).toBeNumber(); + expect(multBy([], (e) => e)).toBeFinite(); + expect(multBy([], (e) => e)).toBe(0); + }); +}); diff --git a/utils/array.ts b/utils/array.ts index 6f8928f..09f6b7d 100644 --- a/utils/array.ts +++ b/utils/array.ts @@ -1,21 +1,41 @@ -export function sumArray(arr: Array): number { +/** + * Returns the sum of all the elements of a numeric array. + * @param arr Numeric array. + */ +export function sumArray(arr: ReadonlyArray): number { return sumBy(arr, (a) => a); } -export function sumBy( - arr: Array, - by: (array: T) => number | string +/** + * Returns the sum of all the elements of any array, + * given a lamda function to receive the numeric representation of each element. + * @param arr Any array. + * @param by Map element to number or string, which will be interpreted as a number. + */ +export function sumBy( + arr: ReadonlyArray, + by: (element: E) => number | string ): number { return arr.reduce((sum, element) => sum + Number(by(element)), 0); } -export function multArray(arr: Array): number { +/** + * Returns the product of a numeric array. + * @param arr Numeric array. + */ +export function multArray(arr: ReadonlyArray): number { return multBy(arr, (a) => a); } -export function multBy( - arr: Array, - by: (array: T) => number | string +/** + * Returns the product of any array, + * given a lamda function to receive the numeric representation of each element. + * @param arr Any array. + * @param by Map element to number or string, which will be interpreted as a number. + */ +export function multBy( + arr: ReadonlyArray, + by: (element: E) => number | string ): number { if (arr.length <= 0) { return 0; @@ -23,19 +43,28 @@ export function multBy( return arr.reduce((sum, element) => sum * Number(by(element)), 1); } +/** + * Group an array by a given key. + * @param arr Any array. + * @param by Map element to key. + * @param equals Check if two keys are identical. Defaults to strict equality. + */ export function groupBy( - arr: Array, - by: (element: E) => K + arr: ReadonlyArray, + by: (element: E) => K, + equals: (a: K, b: K) => boolean = (a, b) => a === b ): { key: K; elements: Array }[] { return arr.reduce( (groups, element) => { const key = by(element); - const index = groups.findIndex((e) => e.key === key); + const index = groups.findIndex((e) => equals(e.key, key)); if (index < 0) { + // New key found, add to group array. groups.push({ key, elements: [element] }); return groups; } + const existing = groups[index]; existing.elements.push(element); diff --git a/utils/random.test.ts b/utils/random.test.ts new file mode 100644 index 0000000..7aa70f4 --- /dev/null +++ b/utils/random.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'bun:test'; +import { randomInt } from './random.ts'; + +describe('random utils', () => { + it('randomInt', () => { + const from = -2; + const to = 6; + for (let i = 0; i < 20; i++) { + const number = randomInt(from, to); + expect(number).toBeNumber(); + expect(number).toBeFinite(); + expect(number).toBeInteger(); + expect(number).toBeGreaterThanOrEqual(from); + expect(number).toBeLessThan(to); + } + }); +}); diff --git a/utils/random.ts b/utils/random.ts index 57b0478..3be1879 100644 --- a/utils/random.ts +++ b/utils/random.ts @@ -1,3 +1,8 @@ +/** + * Returns a random integer between from and to, including from, excluding to. + * @param fromInclusive Lower bound, inclusive. + * @param toExclusive Upper Bound, exclusive. + */ export function randomInt(fromInclusive: number, toExclusive: number): number { return ( Math.floor(Math.random() * (toExclusive - fromInclusive)) + fromInclusive