util/comparator.js

'use strict';

/**
 * @author schukai GmbH
 */

import {Monster, Base} from '../types/base.js';
import {isFunction} from '../types/is.js';

/**
 * the comparator allows a comparison function to be abstracted.
 *
 * ```
 * <script type="module">
 * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.6.0/dist/modules/types/id.js';
 * console.log(new Monster.Util.Comparator())
 * console.log(new Monster.Util.Comparator())
 * </script>
 * ```
 *
 * Alternatively, you can also integrate this function individually.
 *
 * ```
 * <script type="module">
 * import {Comparator} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.6.0/dist/modules/types/id.js';
 * console.log(new Util())
 * console.log(new Util())
 * </script>
 * ```
 *
 * the following are some examples of the application of the class.
 *
 * ```
 * new Comparator().lessThanOrEqual(2, 5) // ↦ true
 * new Comparator().greaterThan(4, 2) // ↦ true
 * new Comparator().equal(4, 4) // ↦ true
 * new Comparator().equal(4, 5) // ↦ false
 * ```
 *
 * you can also pass your own comparison function, and thus define the comparison function.
 *
 * ```
 * new Comparator(function (a, b) {
 *      if (a.v === b.v) return 0;
 *         return a.v < b.v ? -1 : 1;
 *      }).equal({v: 2}, {v: 2});  // ↦ true
 * ```
 *
 * @since 1.3.0
 * @memberOf Monster/Util
 */
class Comparator extends Base {

    /**
     * create new comparator
     *
     * @param {function} callback
     * @throw {TypeError} "unsupported type"
     * @throw {TypeError} "impractical comparison"
     */
    constructor(callback) {
        super();

        if (isFunction(callback)) {
            this.compare = callback
        } else if (callback !== undefined) {
            throw new TypeError("unsupported type")
        } else {
            // default compare function
            this.compare = function (a, b) {

                if (typeof a !== typeof b) {
                    throw new TypeError("impractical comparison")
                }

                if (a === b) {
                    return 0;
                }
                return a < b ? -1 : 1;
            };
        }

    }

    /**
     * changes the order of the operators
     *
     * @return {Comparator}
     */
    reverse() {
        const original = this.compare;
        this.compare = (a, b) => original(b, a);
        return this;
    }

    /**
     * Checks if two variables are equal.
     *
     * @param {*} a
     * @param {*} b
     *
     * @return {boolean}
     */
    equal(a, b) {
        return this.compare(a, b) === 0;
    }


    /**
     * Checks if variable `a` is greater than `b`
     *
     * @param {*} a
     * @param {*} b
     *
     * @return {boolean}
     */
    greaterThan(a, b) {
        return this.compare(a, b) > 0;
    }

    /**
     * Checks if variable `a` is greater than or equal to `b`
     *
     * @param {*} a
     * @param {*} b
     *
     * @return {boolean}
     */
    greaterThanOrEqual(a, b) {
        return this.greaterThan(a, b) || this.equal(a, b);
    }

    /**
     * Checks if variable `a` is less than or equal to `b`
     *
     * @param {*} a
     * @param {*} b
     *
     * @return {boolean}
     */
    lessThanOrEqual(a, b) {
        return this.lessThan(a, b) || this.equal(a, b);
    }

    /**
     * Checks if variable a is less than b
     *
     * @param {*} a
     * @param {*} b
     *
     * @return {boolean}
     */
    lessThan(a, b) {
        return this.compare(a, b) < 0;
    }


}

Monster.assignToNamespace('Monster.Util', Comparator);
export {Monster, Comparator}