1
0
mirror of https://github.com/robonen/tools.git synced 2026-03-20 10:54:44 +00:00

refactor: change separate tools by category

This commit is contained in:
2025-05-19 17:43:42 +07:00
parent d55737df2f
commit 78fb4da82a
158 changed files with 32 additions and 24 deletions

View File

@@ -0,0 +1 @@
export * from './stack';

View File

@@ -0,0 +1,119 @@
import { describe, it, expect } from 'vitest';
import { Stack } from '.';
describe('stack', () => {
describe('constructor', () => {
it('create an empty stack if no initial values are provided', () => {
const stack = new Stack<number>();
expect(stack.length).toBe(0);
expect(stack.isEmpty).toBe(true);
});
it('create a stack with the provided initial values', () => {
const initialValues = [1, 2, 3];
const stack = new Stack(initialValues);
expect(stack.length).toBe(initialValues.length);
expect(stack.peek()).toBe(initialValues.at(-1));
});
it('create a stack with the provided initial value', () => {
const initialValue = 1;
const stack = new Stack(initialValue);
expect(stack.length).toBe(1);
expect(stack.peek()).toBe(initialValue);
});
it('create a stack with the provided options', () => {
const options = { maxSize: 5 };
const stack = new Stack<number>(undefined, options);
expect(stack.length).toBe(0);
expect(stack.isFull).toBe(false);
});
});
describe('push', () => {
it('push an element onto the stack', () => {
const stack = new Stack<number>();
stack.push(1);
expect(stack.length).toBe(1);
expect(stack.peek()).toBe(1);
});
it('throw an error if the stack is full', () => {
const options = { maxSize: 1 };
const stack = new Stack<number>(undefined, options);
stack.push(1);
expect(() => stack.push(2)).toThrow(new RangeError('Stack is full'));
});
});
describe('pop', () => {
it('pop an element from the stack', () => {
const stack = new Stack<number>([1, 2, 3]);
const poppedElement = stack.pop();
expect(poppedElement).toBe(3);
expect(stack.length).toBe(2);
});
it('return undefined if the stack is empty', () => {
const stack = new Stack<number>();
const poppedElement = stack.pop();
expect(poppedElement).toBeUndefined();
});
});
describe('peek', () => {
it('return the top element of the stack', () => {
const stack = new Stack<number>([1, 2, 3]);
const topElement = stack.peek();
expect(topElement).toBe(3);
expect(stack.length).toBe(3);
});
it('return undefined if the stack is empty', () => {
const stack = new Stack<number>();
const topElement = stack.peek();
expect(topElement).toBeUndefined();
});
});
describe('clear', () => {
it('clear the stack', () => {
const stack = new Stack<number>([1, 2, 3]);
stack.clear();
expect(stack.length).toBe(0);
expect(stack.isEmpty).toBe(true);
});
});
describe('iteration', () => {
it('iterate over the stack in LIFO order', () => {
const stack = new Stack<number>([1, 2, 3]);
const elements = [...stack];
expect(elements).toEqual([3, 2, 1]);
});
it('iterate over the stack asynchronously in LIFO order', async () => {
const stack = new Stack<number>([1, 2, 3]);
const elements: number[] = [];
for await (const element of stack) {
elements.push(element);
}
expect(elements).toEqual([3, 2, 1]);
});
});
});

View File

@@ -0,0 +1,152 @@
import { last } from '../../arrays';
import { isArray } from '../../types';
export type StackOptions = {
maxSize?: number;
};
/**
* @name Stack
* @category Data Structures
* @description Represents a stack data structure
*
* @since 0.0.2
*
* @template T The type of elements stored in the stack
*/
export class Stack<T> implements Iterable<T>, AsyncIterable<T> {
/**
* The maximum number of elements that the stack can hold
*
* @private
* @type {number}
*/
private readonly maxSize: number;
/**
* The stack data structure
*
* @private
* @type {T[]}
*/
private readonly stack: T[];
/**
* Creates an instance of Stack
*
* @param {(T[] | T)} [initialValues] The initial values to add to the stack
* @param {StackOptions} [options] The options for the stack
* @memberof Stack
*/
constructor(initialValues?: T[] | T, options?: StackOptions) {
this.maxSize = options?.maxSize ?? Infinity;
this.stack = isArray(initialValues) ? initialValues : initialValues ? [initialValues] : [];
}
/**
* Gets the number of elements in the stack
* @returns {number} The number of elements in the stack
*/
public get length() {
return this.stack.length;
}
/**
* Checks if the stack is empty
* @returns {boolean} `true` if the stack is empty, `false` otherwise
*/
public get isEmpty() {
return this.stack.length === 0;
}
/**
* Checks if the stack is full
* @returns {boolean} `true` if the stack is full, `false` otherwise
*/
public get isFull() {
return this.stack.length === this.maxSize;
}
/**
* Pushes an element onto the stack
* @param {T} element The element to push onto the stack
* @returns {this}
* @throws {RangeError} If the stack is full
*/
public push(element: T) {
if (this.isFull)
throw new RangeError('Stack is full');
this.stack.push(element);
return this;
}
/**
* Pops an element from the stack
* @returns {T | undefined} The element popped from the stack
*/
public pop() {
return this.stack.pop();
}
/**
* Peeks at the top element of the stack
* @returns {T | undefined} The top element of the stack
*/
public peek() {
if (this.isEmpty)
return undefined;
return last(this.stack);
}
/**
* Clears the stack
*
* @returns {this}
*/
public clear() {
this.stack.length = 0;
return this;
}
/**
* Converts the stack to an array
*
* @returns {T[]}
*/
public toArray() {
return this.stack.toReversed();
}
/**
* Returns a string representation of the stack
*
* @returns {string}
*/
public toString() {
return this.toArray().toString();
}
/**
* Returns an iterator for the stack
*
* @returns {IterableIterator<T>}
*/
public [Symbol.iterator]() {
return this.toArray()[Symbol.iterator]();
}
/**
* Returns an async iterator for the stack
*
* @returns {AsyncIterableIterator<T>}
*/
public async *[Symbol.asyncIterator]() {
for (const element of this.toArray()) {
yield element;
}
}
}