@ts-java/comparator
Version:
A pure Typescript implementation of the Java `Comparator` functional interface.
221 lines (216 loc) • 6.63 kB
JavaScript
// ../common/src/exception/null-pointer.ts
var NullPointerException = class extends Error {
constructor(message) {
super(message);
this.name = "NullPointerException";
}
};
// ../common/src/typeguards/index.ts
function isBoolean(input) {
return typeof input === "boolean";
}
function isDate(input) {
return input instanceof Date;
}
function isFunction(input) {
return typeof input === "function";
}
function isNone(input) {
return input === void 0 || input === null;
}
function isPresent(input) {
return input !== null && input !== void 0;
}
function isNull(input) {
return input === null;
}
function isNumber(input) {
return typeof input === "number";
}
function isString(input) {
return typeof input === "string";
}
function isUndefined(input) {
return typeof input === "undefined";
}
// src/typeguards.ts
function isComparable(value) {
return typeof value === "object" && value !== null && "compareTo" in value && isFunction(value.compareTo);
}
function isClass(value) {
return typeof value === "object" && value !== null && typeof value.constructor === "function";
}
function isSameClass(value, other) {
return isClass(value) && isClass(other) && value.constructor === other.constructor;
}
function isSameType(value, other) {
return isSameClass(value, other) || typeof value === typeof other;
}
// src/comparator.ts
var Comparator = class _Comparator {
static comparing(keyExtractor, keyComparator) {
return new _Comparator.#Impl((a, b) => {
const valueOfA = keyExtractor(a);
const valueOfB = keyExtractor(b);
if (isUndefined(keyComparator)) {
return _Comparator.naturalOrder().compare(valueOfA, valueOfB);
}
return keyComparator.compare(valueOfA, valueOfB);
});
}
/**
* Returns a Comparator that compares {@link ComparableValue Comparable} objects by their natural order.
* @returns a new Comparator that compares objects by their natural order.
* @throws a {@link NullPointerException} if either of the objects is null.
* @throws an Error if the objects are not of the same type or are not comparable by natural order.
*
* @example
* ```typescript
* const numbers = [1,5,3,2,4];
* numbers.toSorted(Comparator.naturalOrder().compare); // [1,2,3,4,5]
* ```
*/
static naturalOrder() {
return new _Comparator.#Impl((a, b) => {
if (isNull(a) || isNull(b)) {
throw new NullPointerException();
}
if (!isSameType(a, b) || !isSameType(b, a)) {
throw new TypeError("Cannot compare objects of different types");
}
if (isString(a) && isString(b)) {
return a.localeCompare(b);
}
if (isNumber(a) && isNumber(b)) {
return a - b;
}
if (isBoolean(a) && isBoolean(b)) {
return Number(b) - Number(a);
}
if (isDate(a) && isDate(b)) {
return a.getTime() - b.getTime();
}
if (isComparable(a) && isComparable(b)) {
return a.compareTo(b);
}
throw new TypeError("Objects must be comparable by natural order");
});
}
/**
* Returns a Comparator that compares {@link ComparableValue Comparable} objects by their reverse order.
* This is equivalent to calling `Comparator.naturalOrder().reversed()`.
* @returns a new Comparator that compares objects by their reverse order.
* @see {@link Comparator.naturalOrder}
* @example
* ```typescript
* const numbers = [1,5,3,2,4];
* numbers.toSorted(Comparator.reverseOrder().compare); // [5,4,3,2,1]
* ```
*/
static reverseOrder() {
return _Comparator.naturalOrder().reversed();
}
/**
* A null-friendly comparator that compares objects using the given Comparator, putting null values first.
* @param comparator the comparator that will be used to compare the objects.
* @returns a new Comparator that compares objects using the given Comparator, with nulls first.
* @example
* ```typescript
* const numbers = [1,5,3,2,4,null];
* numbers.toSorted(
* Comparator.nullFirst(
* Comparator.naturalOrder()
* ).compare
* ); // [null,1,2,3,4,5]
* ```
*/
static nullFirst(comparator) {
return new _Comparator.#Impl((a, b) => {
if (isPresent(a) && isPresent(b)) {
return comparator.compare(a, b);
}
if (isNull(a) && isNull(b)) {
return 0;
}
if (isNull(a)) {
return -1;
}
return 1;
});
}
/**
* A null-friendly comparator that compares objects using the given Comparator, putting null values last.
* @param comparator the comparator that will be used to compare the objects.
* @returns a new Comparator that compares objects using the given Comparator, with nulls last.
* @example
* ```typescript
* const numbers = [1,5,null,3,2,4];
* numbers.toSorted(
* Comparator.nullLast(
* Comparator.naturalOrder()
* ).compare
* ); // [1,2,3,4,5,null]
*/
static nullLast(comparator) {
return new _Comparator.#Impl((a, b) => {
if (isPresent(a) && isPresent(b)) {
return comparator.compare(a, b);
}
if (isNull(a) && isNull(b)) {
return 0;
}
if (isNull(a)) {
return 1;
}
return -1;
});
}
/**
* An internal class that implements the abstract Comparator.
* Used for the static methods of this class.
*/
static #Impl = class ComparatorImpl extends _Comparator {
constructor(compare) {
super();
this.compare = compare;
}
};
/**
* Returns a new Comparator that imposes the reverse ordering of this Comparator.
* @returns a Comparator that imposes the reverse ordering of this Comparator.
* @example
* ```typescript
* const comparator = Comparator.naturalOrder();
* const numbers = [1,5,3,2,4];
* numbers.toSorted(
* comparator.reversed().compare
* ); // [5,4,3,2,1]
*/
reversed() {
return new _Comparator.#Impl((b, a) => this.compare(a, b));
}
thenComparing(keyExtractorOrComparator, keyComparator) {
return new _Comparator.#Impl((a, b) => {
const result = this.compare(a, b);
if (result !== 0) {
return result;
}
if (keyExtractorOrComparator instanceof _Comparator) {
return keyExtractorOrComparator.compare(a, b);
}
if (isUndefined(keyComparator)) {
return _Comparator.comparing(keyExtractorOrComparator).compare(a, b);
}
return _Comparator.comparing(
keyExtractorOrComparator,
keyComparator
).compare(a, b);
});
}
};
export {
NullPointerException,
isNone,
isString,
Comparator
};