Source: PriorityQueue.mjs

/**
 * Priority Queue
 * @constructor
 * @param {string=} type - (min|max|custom) - default min. If custom {comparator} should be provided
 * @param {function=} comparator - Custom comparator function. E.g. (a,b) => a.priority - b.priority
 */
class PriorityQueue {
  constructor(type = "min", comparator = null) {
    if (!["min", "max", "custom"].includes(type))
      throw new Error("Invalid type");
    if (comparator && typeof comparator !== "function")
      throw new Error("Invalid comparator function");

    const min = (a, b) => a - b; // Min heap
    const max = (a, b) => b - a; // Max heap

    this.comparator = type === "min" ? min : type === "max" ? max : comparator;
    this.PQ = [];
  }
  /**
   * Add a new value to the queue
   * @param {any} val
   */
  add(val) {
    this.PQ.push(val);
    let idx = this.PQ.length - 1;

    while (
      idx > 0 &&
      this.comparator(this.PQ[idx], this.PQ[this.#parent(idx)]) < 0
    ) {
      this.#swap(idx, this.#parent(idx));
      idx = this.#parent(idx);
    }
  }

  /** Swap two elements **/
  #swap = (idx1, idx2) =>
    ([this.PQ[idx1], this.PQ[idx2]] = [this.PQ[idx2], this.PQ[idx1]]);

  /** Get parent of element **/
  #parent = (idx) => Math.floor(idx - 1 / 2);

  /** Get children of element
   * @param {string=} val - left || right **/
  #child = (idx, val) => 2 * idx + (val === "left" ? 1 : 2);

  /** Heapify - Convert tree into heap **/
  #heapify = (idx = 0) => {
    let needle = idx;
    const leftChildIdx = this.#child(idx, "left");
    const rightChildIdx = this.#child(idx, "right");

    if (
      leftChildIdx <= this.PQ.length - 1 &&
      this.comparator(this.PQ[needle], this.PQ[leftChildIdx]) > 0
    )
      needle = leftChildIdx;
    if (
      rightChildIdx <= this.PQ.length - 1 &&
      this.comparator(this.PQ[needle], this.PQ[rightChildIdx]) > 0
    )
      needle = rightChildIdx;

    if (needle !== idx) {
      this.#swap(needle, idx);
      this.#heapify(needle);
    }
  };

  /**
   * Retrieves and remove element with the highest priority
   * @returns {any} element
   */
  poll() {
    const idx = this.PQ.length - 1;
    this.#swap(0, idx); // Swap 1st -> Last
    const tmp = this.PQ.pop();
    this.#heapify();
    return tmp;
  }

  /**
   * Returns true/false if PQ contains certain value among its entries,
   * @param {any} val - The value to search for.
   * @returns {boolean}
   */
  contains(val) {
    if (typeof val !== "object") return this.PQ.includes(val);
    return this.PQ.some(
      (element) => JSON.stringify(element) === JSON.stringify(val)
    );
  }
  /**
   * Clears the PQ
   */
  clear() {
    this.PQ.length = 0;
  }
  /**
   * Returns number of elements
   * @returns {number} size
   */
  get size() {
    return this.PQ.length;
  }
  /**
   * Returns true/false if PQ is empty
   * @returns {boolean}
   */
  get isEmpty() {
    return this.PQ.length === 0;
  }
  /**
   * Converts and return an array from stack
   * @returns {Array}
   */
  toArray() {
    return this.PQ;
  }
  /**
   * Peek heap element
   * @returns {any} element
   */
  get peek() {
    return this.PQ[0] ?? null;
  }
}

export { PriorityQueue };