Skip to content
Vitali Haradkou edited this page Apr 10, 2024 · 1 revision

RSLike. Comparison package.

This package only provide types and definitions.

Symbols

This package introduce custom Symbol implementation in global object.

Symbol.compare

This symbol exists to compare 2 values - Self and another.

We recommend to throw UndefinedBehaviorError for uncomparable types.

This method should returns a number.

  • If method returns 1 (or more) - Self > another
  • If method returns 0 - Self == another
  • If method returns -1 - Self < another
  • If method returns NaN - arguments have same types but not comparable between each others. E.g. NaN == NaN -> false

Symbol.compare implemented for Number, String, Boolean and Date objects. You can implement this trait for any object and use it with primitives.

Examples

Built-in

(5)[Symbol.compare](3); // 5 > 3 returns 1
"qwe"[Symbol.compare]("asd"); // 'a' > 'q'. returns -1

Custom implementation

class A {
  constructor(private readonly value: number) {}
  [Symbol.compare](this, another: unknown) {
    if (typeof another === "number") {
      return this.value - another;
    }
    throw new Error("unsupported type");
  }
}

const a = new A(5);

(5)[Symbol.compare](a); // A(5) - 5 = 0

Symbol.partialEquals

This method tests for this and other values to be equal, and is used by == (with type lossenes).

NOTE: other always will be unknown. Generic T only helps when use with typescript. Perform checks on your side.

NOTE: partialEquals function uses this to binds self result.

Symbol.partialEquals implemented for Number, String, Boolean and Date objects. You can implement this trait for any object and use it with primitives.

Best practice

accept other as unknown type

use function declaration for this binding if you need to use original object to compare

If you delcare a class - implement PartialEquals type

for objects - use partialEquals since it can interpretated as "shallow" equality

Example

Built-in:

(5)[Symbol.partialEquals]("5"); // 5 == '5' -> true

Custom imeplementation

class A {
  constructor(private readonly value: number) {}
  [Symbol.partialEquals](this, another: unknown) {
    if (typeof another === "number") {
      return true;
    }
    throw new Error("unsupported type");
  }
}

const a = new A(5);

(6)[Symbol.partialEquals](a); // A(6) always is true for number

Symbol.equals

Type for equality comparisons which are equivalence relations.

other always will be unknown. Generic T only helps when use with typescript. Perform checks on your side.

built-in objects(Number, String, Boolean, Date) will throw UndefinedBehaviorError error for object with [Symbol.equals] trait implementation but returns not a boolean type

Best practice

Return "boolean" type without throwing an error

Example

Built-in

(6)[Symbol.equals]("6"); // 6 === '6' -> false

Custom implementation

import { type Eq, equals } from "@rslike/cmp";

enum BookFormat {
  Paperback,
  Hardback,
  Ebook,
}
class Book implements Eq {
  public isbn: boolean;
  public format: BookFormat;
  [Symbol.equals](other: Book) {
    return other instanceof Book && this.isbn == other.isbn;
  }
}

const book1 = new Book();
book1.isbn = true;
const book2 = new Book();
book2.isbn = true;

equals(book1, book2); // true
equals(book1, 5); // false
equals(book1, true); // false

Utility functions

compare

Called Symbol.compare to compare 2 arguments between each others and returns number.

In Most cases it returns 4 possible numeric values and can be interpretate:

  • result is >=1 - first argument is more than incoming
  • result is 0 - arguments are the same
  • result is <=-1 - second argument is more than incoming
  • result is NaN - argument have same types but uncomparable between each other (e.g. comparing NaN with NaN gives NaN, since we cannot compare 2 NaNs)

If neigther of arguments not implemnting Symbol.compare trait - compareFn(3rd argument) will be called.

Throws

  • UndefinedBehaviorError if compareFn is not defined and neigther of arguments implements Symbol.compare trait

  • UndefinedBehaviorError if compareFn is not a function

  • UndefinedBehaviorError if compareFn returns not a number type (e.g. string or boolean)

Example

import { compare } from "@rslike/cmp";

compare(2, 3); // -1, 3 > 2
compare(2, {
  [Symbol.compare]() {
    return 500;
  },
}); // 500, object returns 500

compare(2, {
  [Symbol.compare]() {
    return 'hello world'';
  },
}); // throws undefiend behavior. Symbol.compare should return a number

partialEquals

Partial equals. Same as ==(type loose comparison). You can interpretate it as "shallow" equal

Throws

  • UndefinedBehaviorError for a and b objects without implementation Symbol.partialEquals trait or if Symbol.partialEquals trait returns not boolean type.

Example

import { partialEquals } from "@rslike/cmp";

partialEquals(5, "5"); // true

equals

Equals. Same as ===(type looseness comparison).

Called [Symbol.equals] implementation for first or another argument. If neither of the arguments implements [Symbol.equals] trait then equalityFn argument will be called. Else - UndefinedBehaviorError will be throws

Throws

UndefinedBehaviorError for a and b objects without implementation "Symbol.equals" trait

UndefinedBehaviorError if [Symbol.equals] trait returns not boolean type.(e.g. string or number)

Types

Eq

type for check equality. requires to implement [Symbol.equals]

Type for equality comparisons which are equivalence relations.

This means, that in addition to a == b and a != b being strict inverses, the equality must be (for all a, b and c):

reflexive: a == a; symmetric: a == b implies b == a; and transitive: a == b and b == c implies a == c. This property cannot be checked by the compiler, and therefore Eq implies PartialEq, and has equality extra method.

Example

specify that your type implements Eq.

enum BookFormat {
  Paperback,
  Hardback,
  Ebook,
}
class Book implements Eq {
  public isbn: number;
  public format: BookFormat;
  [Symbol.equals](another) {
    return other instanceof Book && this.isbn == other.isbn;
  }
}

Ord

Requires to implement Symbol.compare trait

Example

import { type Ord } from "@rslike/cmp";

class MyArr<number> implements Ord {
  readonly currentIndex: number;
  [Symbol.compare](other: number) {
    return this.currentIndex - other;
  }
}