@pallad/compare
v1.2.0
Published
Custom sorting inspired by Rust
Downloads
1,015
Maintainers
Readme
Sorting library inspired by Rust's Ordering.
Allows to easy definition of comparison abilities for your objects, especially value objects.
Community
Join our discord server
Installation
npm install @pallad/compare
Usage
Comparing two values of same type
import {compare} from '@pallad/compare';
compare(1, 2).isLess // true
compare(1, 2).isGreater // false
compare(1, 2).isEqual // false
Sorting
compare
first tries to leverage compare
defined by Comparable
interface if that is possible.
Otherwise fallbacks to regular operators (<
, ==
, >
) comparison .
import {compare} from '@pallad/compare';
[5, 1, 10].sort(compare); // [1, 5, 10]
[5, 1, 10].sort(compare.reverse); // [10, 5, 1]
Defining custom sorting for any values
You can define your own custom comparator that is used even if values are Comparable
values.
import {compare} from '@pallad/compare';
interface Option {
type: string;
value: string;
}
function comparator(a: Option, b: Option) {
return a.type.localeCompare(b.type);
}
const aValue: Option = {
type: '2',
value: 'Option 2'
};
const bValue: Option = {
type: '1',
value: 'Option 1'
};
compare(aValue, bValue, comparator).isGreater // true
compare.reverse(aValue, bValue, comparator).isGreater // false
You do not need to pass comparator all the time, just create own compare function.
import {createCompareWithComparator} from '@pallad/compare';
const customCompare = createCompareWithComparator(comparator);
customCompare(aValue, bValue).isGreater // true
customCompare.reverse(aValue, bValue).isGreater // false
Defining custom sorting for value objects
import {Comparable, Equal, Less, Greater} from '@pallad/compare';
class Money implements Comparable {
constructor(readonly value: number, readonly currency: string) {
}
compare(another: Money) {
if (another.currency !== this.currency) {
throw new Error('Cannot compare values with different currencies');
}
if (another.value === this.value) {
return Equal;
} else if (this.value < another.value) {
return Less
}
return Greater;
}
}
const amountA = new Money(100, 'BGP');
const amountB = new Money(200, 'BGP');
amountA.compare(amountB) // Less
// uses `compare` since it is defined
compare(amountA, amountB); // Less
Explanation
As in Rust's Ordering
- @pallad/compare
uses 3 immutable objects to describe the comparison result.
The result could be:
Less
Equal
Greater
Each of them implements following shape:
interface Result {
type: 'equal' | 'less' | 'greater',
/**
* Indicates that value is lower
*/
isLess: boolean;
/**
* Indicates that values are equal
*/
isEqual: boolean;
/**
* Indicates that values are not equal (less or greater only)
*/
isNotEqual: boolean;
/**
* Indicates that value is less or equal
*/
isLessOrEqual: boolean;
/**
* Indicates that value is greater or equal
*/
isGreaterOrEqual: boolean;
/**
* Indicates that value is greater
*/
isGreater: boolean;
/**
* Maps result to another value
*/
map(ifLess: T1, ifEqual: T2, ifGreater: T3): T1 | T2 | T3;
map({less: T1, equal: T2, greater: T3}): T1 | T2 | T3;
/**
* Returns opposite value to current one
*
* Less -> Greater
* Equal -> Equal
* Greater -> Less
*/
reverse: Result;
/**
* Just numeric value you can use in `.sort` functions
*/
sortResult: 0 | -1 | 1,
/**
* Just numeric value you can use in `.sort` functions to reverse sorting
*/
sortResultReversed: 0 | -1 | 1,
}
Mapping
You can map comparison result to any other value
import {compare} from '@pallad/compare';
// using arguments for each value
const r1 = compare(1, 5).map('less', 'equal', 'greater') // 'less'
type R1 = typeof r1; // 'less' | 'equal' | 'greater'
// using object
const r2 = compare(1, 5).map({
less: 'less',
equal: 'equal',
greater: 'greater'
}) // 'less'
type R2 = typeof r2; // 'less' | 'equal' | 'greater'
Mapping to boolean
Before your try to map to booleans, think if currently available helpers are not good enough
import {compare} from '@pallad/compare';
compare(1, 10).map(false, true, false); // Too explicit ⚠️
compare(1, 10).isEqual // better 👌
compare(1, 10).map(true, true, false); // Too explicit ⚠️
compare(1, 10).isLessOrEqual // better 👌