diff --git a/packages/collection/__tests__/collection.test.ts b/packages/collection/__tests__/collection.test.ts index ea8f918bd..81ccd802f 100644 --- a/packages/collection/__tests__/collection.test.ts +++ b/packages/collection/__tests__/collection.test.ts @@ -829,6 +829,33 @@ describe('sort() tests', () => { const coll = createCollectionFrom(['a', 5], ['b', 3], ['c', 1]); expect(coll.sort()).toStrictEqual(createCollectionFrom(['c', 1], ['b', 3], ['a', 5])); }); + + describe('returns correct sort order for test values', () => { + const defaultSort = Collection['defaultSort']; // eslint-disable-line @typescript-eslint/dot-notation + const testDefaultSortOrder = (firstValue: any, secondValue: any, result: number) => { + expect(defaultSort(firstValue, secondValue)).toStrictEqual(result); + expect(defaultSort(secondValue, firstValue)).toStrictEqual(result ? result * -1 : 0); + }; + + test('correctly evaluates sort order of undefined', () => { + testDefaultSortOrder(undefined, undefined, 0); + testDefaultSortOrder(0, undefined, -1); + }); + + test('correctly evaluates numeric values stringwise', () => { + testDefaultSortOrder(-1, -2, -1); // "-1" before "-2" + testDefaultSortOrder(1, '1', 0); // "1" equal to "1" + testDefaultSortOrder(1, '1.0', -1); // "1" before "1.0" + testDefaultSortOrder(1.1, '1.1', 0); // "1.1" equal to "1.1" + testDefaultSortOrder('01', 1, -1); // "01" before "1" + testDefaultSortOrder(1, 1n, 0); // "1" equal to "1" + testDefaultSortOrder(Number.NaN, 'NaN', 0); // "NaN" equal to "NaN" + }); + + test('evaluates object literals as equal', () => { + testDefaultSortOrder({ a: 1 }, { b: 2 }, 0); + }); + }); }); }); diff --git a/packages/collection/src/collection.ts b/packages/collection/src/collection.ts index 2b4868a5f..288b2291c 100644 --- a/packages/collection/src/collection.ts +++ b/packages/collection/src/collection.ts @@ -831,10 +831,11 @@ export class Collection extends Map { /** * The sort method sorts the items of a collection in place and returns it. - * The default sort order is according to string Unicode code points. + * If a comparison function is not provided, the function sorts by element values, using the same stringwise comparison algorithm as + * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort | Array.sort()}. * - * @param compareFunction - Specifies a function that defines the sort order. - * If omitted, the collection is sorted according to each character's Unicode code point value, according to the string conversion of each element. + * @param compareFunction - Specifies a function that defines the sort order. The return value of this function should be negative if + * `a` comes before `b`, positive if `b` comes before `a`, or zero if `a` and `b` are considered equal. * @example * ```ts * collection.sort((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); @@ -1024,15 +1025,15 @@ export class Collection extends Map { } /** - * The sorted method sorts the items of a collection and returns it. - * The default sort order is according to string Unicode code points. + * The toSorted method returns a shallow copy of the collection with the items sorted. + * If a comparison function is not provided, the function sorts by element values, using the same stringwise comparison algorithm as + * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort | Array.sort()}. * - * @param compareFunction - Specifies a function that defines the sort order. - * If omitted, the collection is sorted according to each character's Unicode code point value, - * according to the string conversion of each element. + * @param compareFunction - Specifies a function that defines the sort order. The return value of this function should be negative if + * `a` comes before `b`, positive if `b` comes before `a`, or zero if `a` and `b` are considered equal. * @example * ```ts - * collection.sorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); + * const sortedCollection = collection.toSorted((userA, userB) => userA.createdTimestamp - userB.createdTimestamp); * ``` */ public toSorted(compareFunction: Comparator = Collection.defaultSort): Collection { @@ -1044,8 +1045,20 @@ export class Collection extends Map { return [...this.entries()]; } + /** + * Emulates the default sort comparison algorithm used in ECMAScript. Equivalent to calling the + * {@link https://tc39.es/ecma262/multipage/indexed-collections.html#sec-comparearrayelements | CompareArrayElements} + * operation with arguments `firstValue`, `secondValue` and `undefined`. + */ private static defaultSort(firstValue: Value, secondValue: Value): number { - return Number(firstValue > secondValue) || Number(firstValue === secondValue) - 1; + if (firstValue === undefined) return secondValue === undefined ? 0 : 1; + if (secondValue === undefined) return -1; + + const x = String(firstValue); + const y = String(secondValue); + if (x < y) return -1; + if (y < x) return 1; + return 0; } /**