diff --git a/src/index.ts b/src/index.ts index bae8cad..a8de2f6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,8 +53,6 @@ export * from './redactCredentialsInURL.js'; export * from './resultify.js'; export * from './secToString.js'; export * from './set.js'; -export * from './sort-factory.js'; -export * from './sort.js'; export * from './stripWhitespace.js'; export * from './suffix.js'; export * from './toPascalCase.js'; diff --git a/src/predicate/factory/index.ts b/src/predicate/factory/index.ts index 0f96c5e..06eb3f3 100644 --- a/src/predicate/factory/index.ts +++ b/src/predicate/factory/index.ts @@ -9,6 +9,7 @@ export * from './matching.js'; export * from './maybeArray.js'; export * from './nonNullable.js'; export * from './nullable.js'; +export * from './nullish.js'; export * from './optional.js'; export * from './range.js'; export * from './shape.js'; diff --git a/src/predicate/factory/nullish.ts b/src/predicate/factory/nullish.ts new file mode 100644 index 0000000..7b3c597 --- /dev/null +++ b/src/predicate/factory/nullish.ts @@ -0,0 +1,7 @@ +import type { Nullish } from '../../types/common.js'; +import type { TypePredicateFn } from '../../types/functions.js'; + +export const nullish = + (predicate: TypePredicateFn): TypePredicateFn> => + (input: unknown): input is Nullish => + input == null || predicate(input); diff --git a/src/sort.ts b/src/sort.ts deleted file mode 100644 index 19b76f5..0000000 --- a/src/sort.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { getMilliseconds } from './getMilliseconds.js'; -import { parseNumber } from './parseNumber.js'; - -export const byLocaleCompare = (left: string, right: string): number => left.localeCompare(right); - -export const byTimestamp = (left: T, right: T): number => { - const timeDiff = getMilliseconds(left) - getMilliseconds(right); - - return Number.isNaN(timeDiff) ? -1 : timeDiff; -}; - -export const bySimpleComparison = (left: T, right: T): number => { - return left === right ? 0 : left > right ? 1 : -1; -}; - -export const bySubtraction = (left: T, right: T): number => - parseNumber(left) - parseNumber(right); diff --git a/src/sort/byLocaleCompare.ts b/src/sort/byLocaleCompare.ts new file mode 100644 index 0000000..d0a5cc3 --- /dev/null +++ b/src/sort/byLocaleCompare.ts @@ -0,0 +1 @@ +export const byLocaleCompare = (left: string, right: string): number => left.localeCompare(right); diff --git a/src/sort/bySimpleComparison.ts b/src/sort/bySimpleComparison.ts new file mode 100644 index 0000000..acdff77 --- /dev/null +++ b/src/sort/bySimpleComparison.ts @@ -0,0 +1,3 @@ +export const bySimpleComparison = (left: T, right: T): number => { + return left === right ? 0 : left > right ? 1 : -1; +}; diff --git a/src/sort/bySubtraction.ts b/src/sort/bySubtraction.ts new file mode 100644 index 0000000..3244c14 --- /dev/null +++ b/src/sort/bySubtraction.ts @@ -0,0 +1,4 @@ +import { parseNumber } from '../parseNumber.js'; + +export const bySubtraction = (left: T, right: T): number => + parseNumber(left) - parseNumber(right); diff --git a/src/sort/byTimestamp.ts b/src/sort/byTimestamp.ts new file mode 100644 index 0000000..84a3930 --- /dev/null +++ b/src/sort/byTimestamp.ts @@ -0,0 +1,7 @@ +import { getMilliseconds } from '../getMilliseconds.js'; + +export const byTimestamp = (left: T, right: T): number => { + const timeDiff = getMilliseconds(left) - getMilliseconds(right); + + return Number.isNaN(timeDiff) ? -1 : timeDiff; +}; diff --git a/src/sort-factory.ts b/src/sort/factory/byProperty.ts similarity index 52% rename from src/sort-factory.ts rename to src/sort/factory/byProperty.ts index 46dc628..f26d900 100644 --- a/src/sort-factory.ts +++ b/src/sort/factory/byProperty.ts @@ -1,11 +1,6 @@ -import type { CompareFn } from './types/functions.js'; +import type { CompareFn } from '../../types/functions.js'; export const byProperty = (compareFn: CompareFn, property: K): CompareFn => (left, right) => compareFn(left[property], right[property]); - -export const byReverseOf = - (compareFn: CompareFn): CompareFn => - (left, right) => - 0 - compareFn(left, right); diff --git a/src/sort/factory/byReverseOf.ts b/src/sort/factory/byReverseOf.ts new file mode 100644 index 0000000..d65149d --- /dev/null +++ b/src/sort/factory/byReverseOf.ts @@ -0,0 +1,6 @@ +import type { CompareFn } from '../../types/functions.js'; + +export const byReverseOf = + (compareFn: CompareFn): CompareFn => + (left, right) => + 0 - compareFn(left, right); diff --git a/src/types/common.ts b/src/types/common.ts index 433cbe8..bba6a48 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -43,3 +43,5 @@ export type AnyAsyncFunction = (...args: any[]) => Promise; export type AnyNewable = { new (...args: any[]): any; }; + +export type Nullish = T | null | undefined; diff --git a/test/predicate/factory/nullish.test.ts b/test/predicate/factory/nullish.test.ts new file mode 100644 index 0000000..e965aa8 --- /dev/null +++ b/test/predicate/factory/nullish.test.ts @@ -0,0 +1,22 @@ +import { describe, it, expect, vi } from 'vitest'; + +import { nullish } from '../../../src/predicate/factory/nullish.js'; +import { isBoolean } from '../../../src/predicate/isBoolean.js'; + +describe('nullish()', () => { + it('Returns a type predicate function', () => { + expect(nullish(isBoolean)).toBeInstanceOf(Function); + }); + + it('Calls a type predicate function', () => { + const predicate = vi.fn(isBoolean) as unknown as typeof isBoolean; + + const fn = nullish(predicate); + + expect(fn(true)).toBeTruthy(); + expect(fn(false)).toBeTruthy(); + expect(fn(null)).toBeTruthy(); + expect(fn(undefined)).toBeTruthy(); + expect(predicate).toHaveBeenCalledTimes(2); + }); +}); diff --git a/test/sort.test.ts b/test/sort.test.ts deleted file mode 100644 index 1a4a2f1..0000000 --- a/test/sort.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { byTimestamp, byLocaleCompare, bySimpleComparison, bySubtraction } from '../src/sort.js'; - -describe('byLocaleCompare()', () => { - it('compares by string localeCompare', () => { - expect(['c', 'b', 'a'].sort(byLocaleCompare)).toEqual(['a', 'b', 'c']); - }); -}); - -describe('byTimestamp()', () => { - it('sorts by timestamp', () => { - const now = new Date(); - const past = new Date(); - - past.setDate(past.getDate() - 10); - - const data = [now, 0, past, now.toISOString(), past.toISOString(), 1]; - - expect(data.sort(byTimestamp)).toEqual([0, 1, past, past.toISOString(), now, now.toISOString()]); - }); - - it('Handles bad dates', () => { - const now = new Date(); - const bad = Date.parse('not a date'); - - const data = [now, bad]; - - expect(data.sort(byTimestamp)).toEqual([bad, now]); - }); -}); - -describe('bySimpleComparison()', () => { - it('compares using operators', () => { - expect([1, 0, 3, 2].sort(bySimpleComparison)).toEqual([0, 1, 2, 3]); - - expect([true, false, true].sort(bySimpleComparison)).toEqual([false, true, true]); - - expect(['c', 'b', 1, 'a', 'x', 'z', 'y', 1].sort(bySimpleComparison)).toEqual(['a', 1, 1, 'b', 'c', 'x', 'y', 'z']); - }); -}); - -describe('bySubtraction()', () => { - it('compares using subtraction', () => { - expect([1, 0, 3, 2].sort(bySubtraction)).toEqual([0, 1, 2, 3]); - - expect(['1', 0n, 3, 2].sort(bySubtraction)).toEqual([0n, '1', 2, 3]); - }); -}); diff --git a/test/sort/byLocaleCompare.test.ts b/test/sort/byLocaleCompare.test.ts new file mode 100644 index 0000000..b9c6c1c --- /dev/null +++ b/test/sort/byLocaleCompare.test.ts @@ -0,0 +1,9 @@ +import { describe, expect, it } from 'vitest'; + +import { byLocaleCompare } from '../../src/sort/byLocaleCompare.js'; + +describe('byLocaleCompare()', () => { + it('compares by string localeCompare', () => { + expect(['c', 'b', 'a'].sort(byLocaleCompare)).toEqual(['a', 'b', 'c']); + }); +}); diff --git a/test/sort/bySimpleComparison.test.ts b/test/sort/bySimpleComparison.test.ts new file mode 100644 index 0000000..be9d7b2 --- /dev/null +++ b/test/sort/bySimpleComparison.test.ts @@ -0,0 +1,13 @@ +import { describe, expect, it } from 'vitest'; + +import { bySimpleComparison } from '../../src/sort/bySimpleComparison.js'; + +describe('bySimpleComparison()', () => { + it('compares using operators', () => { + expect([1, 0, 3, 2].sort(bySimpleComparison)).toEqual([0, 1, 2, 3]); + + expect([true, false, true].sort(bySimpleComparison)).toEqual([false, true, true]); + + expect(['c', 'b', 1, 'a', 'x', 'z', 'y', 1].sort(bySimpleComparison)).toEqual(['a', 1, 1, 'b', 'c', 'x', 'y', 'z']); + }); +}); diff --git a/test/sort/bySubtraction.test.ts b/test/sort/bySubtraction.test.ts new file mode 100644 index 0000000..16e7456 --- /dev/null +++ b/test/sort/bySubtraction.test.ts @@ -0,0 +1,11 @@ +import { describe, expect, it } from 'vitest'; + +import { bySubtraction } from '../../src/sort/bySubtraction.js'; + +describe('bySubtraction()', () => { + it('compares using subtraction', () => { + expect([1, 0, 3, 2].sort(bySubtraction)).toEqual([0, 1, 2, 3]); + + expect(['1', 0n, 3, 2].sort(bySubtraction)).toEqual([0n, '1', 2, 3]); + }); +}); diff --git a/test/sort/byTimestamp.test.ts b/test/sort/byTimestamp.test.ts new file mode 100644 index 0000000..2d9a55c --- /dev/null +++ b/test/sort/byTimestamp.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from 'vitest'; + +import { byTimestamp } from '../../src/sort/byTimestamp.js'; + +describe('byTimestamp()', () => { + it('sorts by timestamp', () => { + const now = new Date(); + const past = new Date(); + + past.setDate(past.getDate() - 10); + + const data = [now, 0, past, now.toISOString(), past.toISOString(), 1]; + + expect(data.sort(byTimestamp)).toEqual([0, 1, past, past.toISOString(), now, now.toISOString()]); + }); + + it('Handles bad dates', () => { + const now = new Date(); + const bad = Date.parse('not a date'); + + const data = [now, bad]; + + expect(data.sort(byTimestamp)).toEqual([bad, now]); + }); +}); diff --git a/test/sort-factory.test.ts b/test/sort/factory/byProperty.test.ts similarity index 55% rename from test/sort-factory.test.ts rename to test/sort/factory/byProperty.test.ts index 886d5d0..1f5ec7e 100644 --- a/test/sort-factory.test.ts +++ b/test/sort/factory/byProperty.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import { byProperty, byReverseOf } from '../src/sort-factory.js'; -import { byLocaleCompare } from '../src/sort.js'; +import { byLocaleCompare } from '../../../src/sort/byLocaleCompare.js'; +import { byProperty } from '../../../src/sort/factory/byProperty.js'; describe('byProperty()', () => { it('returns a CompareFn', () => { @@ -28,13 +28,3 @@ describe('byProperty()', () => { ]); }); }); - -describe('byReverseOf()', () => { - it('returns a CompareFn', () => { - expect(byReverseOf(byLocaleCompare)).instanceOf(Function); - }); - - it('Sorts using the returned CompareFn', () => { - expect(['a', 'b', 'c'].sort(byReverseOf(byLocaleCompare))).toEqual(['c', 'b', 'a']); - }); -}); diff --git a/test/sort/factory/byReverseOf.test.ts b/test/sort/factory/byReverseOf.test.ts new file mode 100644 index 0000000..4f90118 --- /dev/null +++ b/test/sort/factory/byReverseOf.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, it } from 'vitest'; + +import { byLocaleCompare } from '../../../src/sort/byLocaleCompare.js'; +import { byReverseOf } from '../../../src/sort/factory/byReverseOf.js'; + +describe('byReverseOf()', () => { + it('returns a CompareFn', () => { + expect(byReverseOf(byLocaleCompare)).instanceOf(Function); + }); + + it('Sorts using the returned CompareFn', () => { + expect(['a', 'b', 'c'].sort(byReverseOf(byLocaleCompare))).toEqual(['c', 'b', 'a']); + }); +});