From 4cd41a9bd12d25219e55e0a1e03e0f8f8c1c5d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Mon, 16 Dec 2024 14:44:04 +0100 Subject: [PATCH] feat: add xyObjectMinMaxValues function --- .../__snapshots__/index.test.ts.snap | 1 + src/x/xCheck.ts | 12 ++++--- src/x/xMinMaxValues.ts | 2 +- src/xyObject/__tests__/xyObjectCheck.test.ts | 12 +++++++ .../__tests__/xyObjectMinMaxValues.test.ts | 30 ++++++++++++++++++ src/xyObject/index.ts | 1 + src/xyObject/xyObjectCheck.ts | 21 +++++++++++-- src/xyObject/xyObjectMinMaxValues.ts | 31 +++++++++++++++++++ 8 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 src/xyObject/__tests__/xyObjectMinMaxValues.test.ts create mode 100644 src/xyObject/xyObjectMinMaxValues.ts diff --git a/src/__tests__/__snapshots__/index.test.ts.snap b/src/__tests__/__snapshots__/index.test.ts.snap index 32a03abf..ad722635 100644 --- a/src/__tests__/__snapshots__/index.test.ts.snap +++ b/src/__tests__/__snapshots__/index.test.ts.snap @@ -125,6 +125,7 @@ exports[`test existence of exported functions 1`] = ` "xyObjectJoinX", "xyObjectMaxXPoint", "xyObjectMaxYPoint", + "xyObjectMinMaxValues", "xyObjectMinXPoint", "xyObjectMinYPoint", "xyObjectSlotX", diff --git a/src/x/xCheck.ts b/src/x/xCheck.ts index e2c67e8e..0ce3afd7 100644 --- a/src/x/xCheck.ts +++ b/src/x/xCheck.ts @@ -2,14 +2,18 @@ import type { NumberArray } from 'cheminfo-types'; import { isAnyArray } from 'is-any-array'; export interface XCheckOptions { - /** minimum length */ + /** + * Minimum length + * @default 1 + */ minLength?: number; } /** - * Checks if input is of type array. - * @param input - input - * @param options + * Checks if the input is a non-empty array of numbers. + * Only checks the first element. + * @param input - Array to check. + * @param options - Additional checks. */ export function xCheck( input?: NumberArray, diff --git a/src/x/xMinMaxValues.ts b/src/x/xMinMaxValues.ts index 9c9ea7a9..6a7c7967 100644 --- a/src/x/xMinMaxValues.ts +++ b/src/x/xMinMaxValues.ts @@ -5,7 +5,7 @@ import { xCheck } from './xCheck'; /** * Return min and max values of an array. * @param array - array of number - * @returns - Object with 2 properties, min and max + * @returns - Object with 2 properties, min and max. */ export function xMinMaxValues(array: NumberArray): { min: number; diff --git a/src/xyObject/__tests__/xyObjectCheck.test.ts b/src/xyObject/__tests__/xyObjectCheck.test.ts index 29c24681..34fa9ea3 100644 --- a/src/xyObject/__tests__/xyObjectCheck.test.ts +++ b/src/xyObject/__tests__/xyObjectCheck.test.ts @@ -12,4 +12,16 @@ test('xyObjectCheck', () => { expect(() => xyObjectCheck('a')).toThrow( 'points must be an array of {x,y} objects', ); + expect( + xyObjectCheck( + [ + { x: 0, y: 1 }, + { x: 2, y: 3 }, + ], + { minLength: 2 }, + ), + ).toBeUndefined(); + expect(() => xyObjectCheck([{ x: 0, y: 1 }], { minLength: 2 })).toThrow( + 'points must have a length of at least 2', + ); }); diff --git a/src/xyObject/__tests__/xyObjectMinMaxValues.test.ts b/src/xyObject/__tests__/xyObjectMinMaxValues.test.ts new file mode 100644 index 00000000..b3fa39ac --- /dev/null +++ b/src/xyObject/__tests__/xyObjectMinMaxValues.test.ts @@ -0,0 +1,30 @@ +import { expect, test } from 'vitest'; + +import { xyObjectMinMaxValues } from '../xyObjectMinMaxValues'; + +test('one element', () => { + expect(xyObjectMinMaxValues([{ x: 1, y: 2 }])).toStrictEqual({ + minX: 1, + maxX: 1, + minY: 2, + maxY: 2, + }); +}); + +test('multiple elements', () => { + expect( + xyObjectMinMaxValues([ + { x: 1, y: 2 }, + { x: 5, y: 4 }, + { x: 3, y: 6 }, + { x: -2, y: -5 }, + { x: 0, y: 0 }, + ]), + ).toStrictEqual({ minX: -2, maxX: 5, minY: -5, maxY: 6 }); +}); + +test('throws error if array is empty', () => { + expect(() => xyObjectMinMaxValues([])).toThrow( + 'points must have a length of at least 1', + ); +}); diff --git a/src/xyObject/index.ts b/src/xyObject/index.ts index d50a4b8f..9e475b04 100644 --- a/src/xyObject/index.ts +++ b/src/xyObject/index.ts @@ -3,6 +3,7 @@ export * from './xyObjectCheck'; export * from './xyObjectJoinX'; export * from './xyObjectMaxXPoint'; export * from './xyObjectMaxYPoint'; +export * from './xyObjectMinMaxValues'; export * from './xyObjectMinXPoint'; export * from './xyObjectMinYPoint'; export * from './xyObjectSlotX'; diff --git a/src/xyObject/xyObjectCheck.ts b/src/xyObject/xyObjectCheck.ts index 8119a916..f8e8e104 100644 --- a/src/xyObject/xyObjectCheck.ts +++ b/src/xyObject/xyObjectCheck.ts @@ -1,10 +1,24 @@ import type { Point } from '../types'; +export interface XYObjectCheckOptions { + /** + * Minimum length + * @default 0 + */ + minLength?: number; +} + /** - * Throws an error in not an array of x,y objects. + * Throws an error if it's not an array of x,y objects. + * Only checks the first element. * @param points - List of points. + * @param options - Additional checks. */ -export function xyObjectCheck(points?: Point[]): asserts points is Point[] { +export function xyObjectCheck( + points?: Point[], + options: XYObjectCheckOptions = {}, +): asserts points is Point[] { + const { minLength = 0 } = options; if (!Array.isArray(points)) { throw new Error('points must be an array of {x,y} objects'); } @@ -14,4 +28,7 @@ export function xyObjectCheck(points?: Point[]): asserts points is Point[] { ) { throw new Error('points must be an array of {x,y} objects'); } + if (minLength && points.length < minLength) { + throw new Error(`points must have a length of at least ${minLength}`); + } } diff --git a/src/xyObject/xyObjectMinMaxValues.ts b/src/xyObject/xyObjectMinMaxValues.ts new file mode 100644 index 00000000..43305c65 --- /dev/null +++ b/src/xyObject/xyObjectMinMaxValues.ts @@ -0,0 +1,31 @@ +import type { Point } from '../types'; + +import { xyObjectCheck } from './xyObjectCheck'; + +/** + * Returns all min and max values of an array of points. + * @param points - Array of points {x,y}. + * @returns - Object with the 4 extrema. + */ +export function xyObjectMinMaxValues(points: Point[]): { + minX: number; + maxX: number; + minY: number; + maxY: number; +} { + xyObjectCheck(points, { minLength: 1 }); + + let minX = points[0].x; + let maxX = minX; + let minY = points[0].y; + let maxY = minY; + + for (const point of points) { + if (point.x < minX) minX = point.x; + if (point.x > maxX) maxX = point.x; + if (point.y < minY) minY = point.y; + if (point.y > maxY) maxY = point.y; + } + + return { minX, maxX, minY, maxY }; +}