UNPKG

@rushstack/node-core-library

Version:

Core libraries that every NodeJS toolchain project should use

266 lines 8.2 kB
"use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. Object.defineProperty(exports, "__esModule", { value: true }); exports.Sort = void 0; /** * Operations for sorting collections. * * @public */ class Sort { /** * Compares `x` and `y` using the JavaScript `>` and `<` operators. This function is suitable for usage as * the callback for `array.sort()`. * * @remarks * * The JavaScript ordering is generalized so that `undefined` \< `null` \< all other values. * * @returns -1 if `x` is smaller than `y`, 1 if `x` is greater than `y`, or 0 if the values are equal. * * @example * * ```ts * let array: number[] = [3, 6, 2]; * array.sort(Sort.compareByValue); // [2, 3, 6] * ``` */ // eslint-disable-next-line @typescript-eslint/no-explicit-any static compareByValue(x, y) { if (x === y) { return 0; } // Undefined is smaller than anything else if (x === undefined) { return -1; } if (y === undefined) { return 1; } // Null is smaller than anything except undefined if (x === null) { return -1; } if (y === null) { return 1; } // These comparisons always return false if either of the arguments is "undefined". // These comparisons return nonsense for "null" (true for "null > -1", but false for "null < 0" and "null > 0") if (x < y) { return -1; } if (x > y) { return 1; } return 0; } /** * Sorts the array according to a key which is obtained from the array elements. * The result is guaranteed to be a stable sort. * * @example * * ```ts * let array: string[] = [ 'aaa', 'bb', 'c' ]; * Sort.sortBy(array, x => x.length); // [ 'c', 'bb', 'aaa' ] * ``` */ static sortBy(array, // eslint-disable-next-line @typescript-eslint/no-explicit-any keySelector, // eslint-disable-next-line @typescript-eslint/no-explicit-any comparer = Sort.compareByValue) { array.sort((x, y) => comparer(keySelector(x), keySelector(y))); } /** * Returns true if the collection is already sorted. */ static isSorted(collection, // eslint-disable-next-line @typescript-eslint/no-explicit-any comparer = Sort.compareByValue) { let isFirst = true; let previous = undefined; for (const element of collection) { if (isFirst) { // Don't start by comparing against undefined. isFirst = false; } else if (comparer(previous, element) > 0) { return false; } previous = element; } return true; } /** * Returns true if the collection is already sorted by the specified key. * * @example * * ```ts * let array: string[] = [ 'a', 'bb', 'ccc' ]; * Sort.isSortedBy(array, x => x.length); // true * ``` */ static isSortedBy(collection, // eslint-disable-next-line @typescript-eslint/no-explicit-any keySelector, // eslint-disable-next-line @typescript-eslint/no-explicit-any comparer = Sort.compareByValue) { let isFirst = true; let previousKey = undefined; for (const element of collection) { const key = keySelector(element); if (isFirst) { // Don't start by comparing against undefined. isFirst = false; } else if (comparer(previousKey, key) > 0) { return false; } previousKey = key; } return true; } /** * Sorts the entries in a Map object according to the map keys. * The result is guaranteed to be a stable sort. * * @example * * ```ts * let map: Map<string, number> = new Map<string, number>(); * map.set('zebra', 1); * map.set('goose', 2); * map.set('aardvark', 3); * Sort.sortMapKeys(map); * console.log(JSON.stringify(Array.from(map.keys()))); // ["aardvark","goose","zebra"] * ``` */ // eslint-disable-next-line @typescript-eslint/no-explicit-any static sortMapKeys(map, keyComparer = Sort.compareByValue) { // Sorting a map is expensive, so first check whether it's already sorted. if (Sort.isSorted(map.keys(), keyComparer)) { return; } const pairs = Array.from(map.entries()); Sort.sortBy(pairs, (x) => x[0], keyComparer); map.clear(); for (const pair of pairs) { map.set(pair[0], pair[1]); } } /** * Sorts the entries in a Set object according to the specified keys. * The result is guaranteed to be a stable sort. * * @example * * ```ts * let set: Set<string> = new Set<string>(); * set.add('aaa'); * set.add('bb'); * set.add('c'); * Sort.sortSetBy(set, x => x.length); * console.log(Array.from(set)); // ['c', 'bb', 'aaa'] * ``` */ static sortSetBy(set, // eslint-disable-next-line @typescript-eslint/no-explicit-any keySelector, keyComparer = Sort.compareByValue) { // Sorting a set is expensive, so first check whether it's already sorted. if (Sort.isSortedBy(set, keySelector, keyComparer)) { return; } const array = Array.from(set); array.sort((x, y) => keyComparer(keySelector(x), keySelector(y))); set.clear(); for (const item of array) { set.add(item); } } /** * Sorts the entries in a Set object. The result is guaranteed to be a stable sort. * * @example * * ```ts * let set: Set<string> = new Set<string>(); * set.add('zebra'); * set.add('goose'); * set.add('aardvark'); * Sort.sortSet(set); * console.log(Array.from(set)); // ['aardvark', 'goose', 'zebra'] * ``` */ // eslint-disable-next-line @typescript-eslint/no-explicit-any static sortSet(set, comparer = Sort.compareByValue) { // Sorting a set is expensive, so first check whether it's already sorted. if (Sort.isSorted(set, comparer)) { return; } const array = Array.from(set); array.sort((x, y) => comparer(x, y)); set.clear(); for (const item of array) { set.add(item); } } /** * Sort the keys deeply given an object or an array. * * Doesn't handle cyclic reference. * * @param object - The object to be sorted * * @example * * ```ts * console.log(Sort.sortKeys({ c: 3, b: 2, a: 1 })); // { a: 1, b: 2, c: 3} * ``` */ static sortKeys(object) { if (!isPlainObject(object) && !Array.isArray(object)) { throw new TypeError(`Expected object or array`); } return Array.isArray(object) ? innerSortArray(object) : innerSortKeys(object); } } exports.Sort = Sort; function isPlainObject(obj) { return obj !== null && typeof obj === 'object'; } function innerSortArray(arr) { const result = []; for (const entry of arr) { if (Array.isArray(entry)) { result.push(innerSortArray(entry)); } else if (isPlainObject(entry)) { result.push(innerSortKeys(entry)); } else { result.push(entry); } } return result; } function innerSortKeys(obj) { const result = {}; const keys = Object.keys(obj).sort(); for (const key of keys) { const value = obj[key]; if (Array.isArray(value)) { result[key] = innerSortArray(value); } else if (isPlainObject(value)) { result[key] = innerSortKeys(value); } else { result[key] = value; } } return result; } //# sourceMappingURL=Sort.js.map