data-structures/Stack.mjs

/**
 * @module data-structures
 */

/**
 * High-performance, LIFO (Last-In-First-Out) Stack data structure.
 * Encapsulates an internal private array (#items) to prevent index tampering
 * and optimize V8 memory locality and fast push/pop operations.
 */
export class Stack {
  #items = [];

  /**
   * Creates a new Stack instance.
   * @param {Iterable} [iter] - An optional iterable object to initialize the stack with.
   */
  constructor(iter) {
    for (const el of iter ?? []) {
      this.push(el);
    }
  }

  /**
   * Pushes one or more items onto the top of the stack.
   * @param {...*} items - Elements to push onto the stack.
   * @returns {Stack} The Stack instance for fluent method chaining.
   */
  push(...items) {
    this.#items.push(...items);
    return this;
  }

  /**
   * Removes and returns the item from the top of the stack.
   * @returns {*|undefined} The top item, or undefined if the stack is empty.
   */
  pop() {
    return this.#items.pop();
  }

  /**
   * Returns the item from the top of the stack without removing it.
   * @returns {*|undefined} The top item, or undefined if the stack is empty.
   */
  peek() {
    return this.#items[this.#items.length - 1];
  }

  /**
   * Checks whether the stack is empty.
   * @returns {boolean} True if the stack contains no items, false otherwise.
   */
  isEmpty() {
    return this.#items.length === 0;
  }

  /**
   * Gets the number of items currently in the stack.
   * @returns {number} The number of items in the stack.
   */
  get size() {
    return this.#items.length;
  }

  /**
   * Gets the number of items currently in the stack (Array length compatibility alias).
   * @returns {number} The number of items in the stack.
   */
  get length() {
    return this.#items.length;
  }

  /**
   * Removes all items from the stack.
   * @returns {Stack} The Stack instance for fluent method chaining.
   */
  clear() {
    this.#items.length = 0;
    return this;
  }

  /**
   * Returns a shallow copy of the stack items as an Array from bottom to top.
   * @returns {Array} Array of items from bottom to top.
   */
  toArray() {
    return [...this.#items];
  }

  /**
   * Iterates through stack items from top to bottom (LIFO order).
   * @returns {Iterator}
   */
  *[Symbol.iterator]() {
    for (let i = this.#items.length - 1; i >= 0; i--) {
      yield this.#items[i];
    }
  }
}