feat: All data structures except Graph have implemented *[Symbol.iterator], forEach, filter, map, reduce methods.

This commit is contained in:
Revone 2023-11-23 13:19:13 +08:00
parent 37e32fedcf
commit 5cf88d098e
18 changed files with 950 additions and 410 deletions

View file

@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file.
- [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
- [`auto-changelog`](https://github.com/CookPete/auto-changelog)
## [v1.47.3](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
## [v1.47.4](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
### Changes

View file

@ -313,12 +313,14 @@ export class HashMap<K = any, V = any> {
* @returns a new HashMap object that contains the key-value pairs from the original HashMap that
* satisfy the given predicate function.
*/
filter(predicate: (element: [K, V], map: HashMap<K, V>) => boolean): HashMap<K, V> {
filter(predicate: (element: [K, V], index: number, map: HashMap<K, V>) => boolean): HashMap<K, V> {
const filteredMap = new HashMap<K, V>();
let index = 0;
for (const [key, value] of this) {
if (predicate([key, value], this)) {
if (predicate([key, value], index, this)) {
filteredMap.set(key, value);
}
index++;
}
return filteredMap;
}
@ -330,11 +332,13 @@ export class HashMap<K = any, V = any> {
* `map`.
* @returns a new HashMap object with the values mapped according to the provided callback function.
*/
map<NV>(callback: (element: [K, V], map: HashMap<K, V>) => NV): HashMap<K, NV> {
map<NV>(callback: (element: [K, V], index: number, map: HashMap<K, V>) => NV): HashMap<K, NV> {
const mappedMap = new HashMap<K, NV>();
let index = 0;
for (const [key, value] of this) {
const newValue = callback([key, value], this);
const newValue = callback([key, value], index, this);
mappedMap.set(key, newValue);
index++;
}
return mappedMap;
}
@ -351,10 +355,12 @@ export class HashMap<K = any, V = any> {
* @returns The `reduce` function is returning the final value of the accumulator after iterating
* over all the elements in the HashMap and applying the callback function to each element.
*/
reduce<A>(callback: (accumulator: A, element: [K, V], map: HashMap<K, V>) => A, initialValue: A): A {
reduce<A>(callback: (accumulator: A, element: [K, V], index: number, map: HashMap<K, V>) => A, initialValue: A): A {
let accumulator = initialValue;
for (const element of this) {
accumulator = callback(accumulator, element, this);
let index = 0;
for (const entry of this) {
accumulator = callback(accumulator, entry, index, this);
index++;
}
return accumulator;
}

View file

@ -9,18 +9,18 @@
export class HashTableNode<K, V> {
key: K;
value: V;
next: HashTableNode<K, V> | null;
next: HashTableNode<K, V> | undefined;
constructor(key: K, value: V) {
this.key = key;
this.value = value;
this.next = null;
this.next = undefined;
}
}
import { HashFunction } from '../../types';
export class HashTable<K, V> {
export class HashTable<K = any, V = any> {
protected static readonly DEFAULT_CAPACITY = 16;
protected static readonly LOAD_FACTOR = 0.75;
@ -28,7 +28,7 @@ export class HashTable<K, V> {
this._hashFn = hashFn || this._defaultHashFn;
this._capacity = Math.max(capacity, HashTable.DEFAULT_CAPACITY);
this._size = 0;
this._buckets = new Array<HashTableNode<K, V> | null>(this._capacity).fill(null);
this._buckets = new Array<HashTableNode<K, V> | undefined>(this._capacity).fill(undefined);
}
protected _capacity: number;
@ -43,9 +43,9 @@ export class HashTable<K, V> {
return this._size;
}
protected _buckets: Array<HashTableNode<K, V> | null>;
protected _buckets: Array<HashTableNode<K, V> | undefined>;
get buckets(): Array<HashTableNode<K, V> | null> {
get buckets(): Array<HashTableNode<K, V> | undefined> {
return this._buckets;
}
@ -125,7 +125,7 @@ export class HashTable<K, V> {
delete(key: K): void {
const index = this._hash(key);
let currentNode = this._buckets[index];
let prevNode: HashTableNode<K, V> | null = null;
let prevNode: HashTableNode<K, V> | undefined = undefined;
while (currentNode) {
if (currentNode.key === key) {
@ -135,7 +135,7 @@ export class HashTable<K, V> {
this._buckets[index] = currentNode.next;
}
this._size--;
currentNode.next = null; // Release memory
currentNode.next = undefined; // Release memory
return;
}
prevNode = currentNode;
@ -143,6 +143,56 @@ export class HashTable<K, V> {
}
}
* [Symbol.iterator](): Generator<[K, V], void, undefined> {
for (const bucket of this._buckets) {
let currentNode = bucket;
while (currentNode) {
yield [currentNode.key, currentNode.value];
currentNode = currentNode.next;
}
}
}
forEach(callback: (entry: [K, V], index: number, table: HashTable<K, V>) => void): void {
let index = 0;
for (const entry of this) {
callback(entry, index, this);
index++;
}
}
filter(predicate: (entry: [K, V], index: number, table: HashTable<K, V>) => boolean): HashTable<K, V> {
const newTable = new HashTable<K, V>();
let index = 0;
for (const [key, value] of this) {
if (predicate([key, value], index, this)) {
newTable.set(key, value);
}
index++;
}
return newTable;
}
map<T>(callback: (entry: [K, V], index: number, table: HashTable<K, V>) => T): HashTable<K, T> {
const newTable = new HashTable<K, T>();
let index = 0;
for (const [key, value] of this) {
newTable.set(key, callback([key, value], index, this));
index++;
}
return newTable;
}
reduce<T>(callback: (accumulator: T, entry: [K, V], index: number, table: HashTable<K, V>) => T, initialValue: T): T {
let accumulator = initialValue;
let index = 0;
for (const entry of this) {
accumulator = callback(accumulator, entry, index, this);
index++;
}
return accumulator;
}
/**
* The function `_defaultHashFn` calculates the hash value of a given key and returns the remainder when divided by the
* capacity of the data structure.
@ -241,7 +291,7 @@ export class HashTable<K, V> {
*/
protected _expand(): void {
const newCapacity = this._capacity * 2;
const newBuckets = new Array<HashTableNode<K, V> | null>(newCapacity).fill(null);
const newBuckets = new Array<HashTableNode<K, V> | undefined>(newCapacity).fill(undefined);
for (const bucket of this._buckets) {
let currentNode = bucket;

View file

@ -226,29 +226,30 @@ export class Heap<E = any> {
* @param order - Traverse order parameter: 'in' (in-order), 'pre' (pre-order) or 'post' (post-order).
* @returns An array containing elements traversed in the specified order.
*/
dfs(order: DFSOrderPattern): E[] {
dfs(order: DFSOrderPattern = 'pre'): E[] {
const result: E[] = [];
// Auxiliary recursive function, traverses the binary heap according to the traversal order
const dfsHelper = (index: number) => {
const _dfs = (index: number) => {
const left = 2 * index + 1, right = left + 1;
if (index < this.size) {
if (order === 'in') {
dfsHelper(2 * index + 1);
_dfs(left);
result.push(this.elements[index]);
dfsHelper(2 * index + 2);
_dfs(right);
} else if (order === 'pre') {
result.push(this.elements[index]);
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
_dfs(left);
_dfs(right);
} else if (order === 'post') {
dfsHelper(2 * index + 1);
dfsHelper(2 * index + 2);
_dfs(left);
_dfs(right);
result.push(this.elements[index]);
}
}
};
dfsHelper(0); // Traverse starting from the root node
_dfs(0); // Traverse starting from the root node
return result;
}
@ -324,6 +325,56 @@ export class Heap<E = any> {
for (let i = Math.floor(this.size / 2); i >= 0; i--) this._sinkDown(i, this.elements.length >> 1);
}
* [Symbol.iterator]() {
for (const element of this.elements) {
yield element;
}
}
forEach(callback: (element: E, index: number, heap: this) => void): void {
let index = 0;
for (const el of this) {
callback(el, index, this);
index++;
}
}
filter(predicate: (element: E, index: number, heap: Heap<E>) => boolean): Heap<E> {
const filteredHeap: Heap<E> = new Heap<E>({ comparator: this.comparator });
let index = 0;
for (const el of this) {
if (predicate(el, index, this)) {
filteredHeap.push(el);
}
index++;
}
return filteredHeap;
}
map<T>(callback: (element: E, index: number, heap: Heap<E>) => T, comparator: Comparator<T>): Heap<T> {
const mappedHeap: Heap<T> = new Heap<T>({ comparator: comparator });
let index = 0;
for (const el of this) {
mappedHeap.add(callback(el, index, this));
index++;
}
return mappedHeap;
}
reduce<T>(
callback: (accumulator: T, currentValue: E, currentIndex: number, heap: Heap<E>) => T,
initialValue: T
): T {
let accumulator: T = initialValue;
let index = 0;
for (const el of this) {
accumulator = callback(accumulator, el, index, this);
index++;
}
return accumulator;
}
/**
* Time Complexity: O(log n)
* Space Complexity: O(1)

View file

@ -441,6 +441,50 @@ export class DoublyLinkedList<E = any> {
return false;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
*
* The `insertAfter` function inserts a new node with a given value after an existing node in a doubly linked list.
* @param {E | DoublyLinkedListNode<E>} existingValueOrNode - The existing value or node in the doubly linked list
* after which the new value will be inserted. It can be either the value of the existing node or the existing node
* itself.
* @param {E} newValue - The value that you want to insert into the doubly linked list.
* @returns The method returns a boolean value. It returns true if the insertion is successful, and false if the
* existing value or node is not found in the doubly linked list.
*/
insertAfter(existingValueOrNode: E | DoublyLinkedListNode<E>, newValue: E): boolean {
let existingNode;
if (existingValueOrNode instanceof DoublyLinkedListNode) {
existingNode = existingValueOrNode;
} else {
existingNode = this.getNode(existingValueOrNode);
}
if (existingNode) {
const newNode = new DoublyLinkedListNode(newValue);
newNode.next = existingNode.next;
if (existingNode.next) {
existingNode.next.prev = newNode;
}
newNode.prev = existingNode;
existingNode.next = newNode;
if (existingNode === this.tail) {
this._tail = newNode;
}
this._length++;
return true;
}
return false;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
@ -511,28 +555,6 @@ export class DoublyLinkedList<E = any> {
return false;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*
* The `toArray` function converts a linked list into an array.
* @returns The `toArray()` method is returning an array of type `E[]`.
*/
toArray(): E[] {
const array: E[] = [];
let current = this.head;
while (current) {
array.push(current.value);
current = current.next;
}
return array;
}
/**
* The function checks if a variable has a length greater than zero and returns a boolean value.
* @returns A boolean value is being returned.
@ -631,28 +653,6 @@ export class DoublyLinkedList<E = any> {
return null;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*
* The `toArrayBackward` function converts a doubly linked list into an array in reverse order.
* @returns The `toArrayBackward()` function returns an array of type `E[]`.
*/
toArrayBackward(): E[] {
const array: E[] = [];
let current = this.tail;
while (current) {
array.push(current.value);
current = current.prev;
}
return array;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
@ -674,6 +674,62 @@ export class DoublyLinkedList<E = any> {
}
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*
* The `toArray` function converts a linked list into an array.
* @returns The `toArray()` method is returning an array of type `E[]`.
*/
toArray(): E[] {
const array: E[] = [];
let current = this.head;
while (current) {
array.push(current.value);
current = current.next;
}
return array;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*
* The `toReversedArray` function converts a doubly linked list into an array in reverse order.
* @returns The `toReversedArray()` function returns an array of type `E[]`.
*/
toReversedArray(): E[] {
const array: E[] = [];
let current = this.tail;
while (current) {
array.push(current.value);
current = current.prev;
}
return array;
}
/**
* The function returns an iterator that iterates over the values of a linked list.
*/
* [Symbol.iterator]() {
let current = this.head;
while (current) {
yield current.value;
current = current.next;
}
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
@ -688,42 +744,14 @@ export class DoublyLinkedList<E = any> {
* represents the value of the current node in the linked list, and the index argument represents the index of the
* current node in the linked list.
*/
forEach(callback: (value: E, index: number) => void): void {
let current = this.head;
forEach(callback: (value: E, index: number, list: DoublyLinkedList<E>) => void): void {
let index = 0;
while (current) {
callback(current.value, index);
current = current.next;
for (const el of this) {
callback(el, index, this);
index++;
}
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*
* The `map` function takes a callback function and applies it to each element in the DoublyLinkedList, returning a new
* DoublyLinkedList with the transformed values.
* @param callback - The callback parameter is a function that takes a value of type E (the type of values stored in
* the original DoublyLinkedList) and returns a value of type U (the type of values that will be stored in the mapped
* DoublyLinkedList).
* @returns The `map` function is returning a new instance of `DoublyLinkedList<U>` that contains the mapped values.
*/
map<U>(callback: (value: E) => U): DoublyLinkedList<U> {
const mappedList = new DoublyLinkedList<U>();
let current = this.head;
while (current) {
mappedList.push(callback(current.value));
current = current.next;
}
return mappedList;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
@ -739,18 +767,45 @@ export class DoublyLinkedList<E = any> {
* It is used to determine whether a value should be included in the filtered list or not.
* @returns The filtered list, which is an instance of the DoublyLinkedList class.
*/
filter(callback: (value: E) => boolean): DoublyLinkedList<E> {
filter(callback: (value: E, index: number, list: DoublyLinkedList<E>) => boolean): DoublyLinkedList<E> {
const filteredList = new DoublyLinkedList<E>();
let current = this.head;
while (current) {
if (callback(current.value)) {
filteredList.push(current.value);
let index = 0;
for (const current of this) {
if (callback(current, index, this)) {
filteredList.push(current);
}
current = current.next;
index++;
}
return filteredList;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*
* The `map` function takes a callback function and applies it to each element in the DoublyLinkedList, returning a new
* DoublyLinkedList with the transformed values.
* @param callback - The callback parameter is a function that takes a value of type E (the type of values stored in
* the original DoublyLinkedList) and returns a value of type T (the type of values that will be stored in the mapped
* DoublyLinkedList).
* @returns The `map` function is returning a new instance of `DoublyLinkedList<T>` that contains the mapped values.
*/
map<T>(callback: (value: E, index: number, list: DoublyLinkedList<E>) => T): DoublyLinkedList<T> {
const mappedList = new DoublyLinkedList<T>();
let index = 0;
for (const current of this) {
mappedList.push(callback(current, index, this));
index++;
}
return mappedList;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
@ -764,74 +819,19 @@ export class DoublyLinkedList<E = any> {
* single value.
* @param callback - The `callback` parameter is a function that takes two arguments: `accumulator` and `value`. It is
* used to perform a specific operation on each element of the linked list.
* @param {U} initialValue - The `initialValue` parameter is the initial value of the accumulator. It is the starting
* @param {T} initialValue - The `initialValue` parameter is the initial value of the accumulator. It is the starting
* point for the reduction operation.
* @returns The `reduce` method is returning the final value of the accumulator after iterating through all the
* elements in the linked list.
*/
reduce<U>(callback: (accumulator: U, value: E) => U, initialValue: U): U {
reduce<T>(callback: (accumulator: T, value: E, index: number, list: DoublyLinkedList<E>) => T, initialValue: T): T {
let accumulator = initialValue;
let current = this.head;
while (current) {
accumulator = callback(accumulator, current.value);
current = current.next;
let index = 0;
for (const current of this) {
accumulator = callback(accumulator, current, index, this);
index++;
}
return accumulator;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
*
* The `insertAfter` function inserts a new node with a given value after an existing node in a doubly linked list.
* @param {E | DoublyLinkedListNode<E>} existingValueOrNode - The existing value or node in the doubly linked list
* after which the new value will be inserted. It can be either the value of the existing node or the existing node
* itself.
* @param {E} newValue - The value that you want to insert into the doubly linked list.
* @returns The method returns a boolean value. It returns true if the insertion is successful, and false if the
* existing value or node is not found in the doubly linked list.
*/
insertAfter(existingValueOrNode: E | DoublyLinkedListNode<E>, newValue: E): boolean {
let existingNode;
if (existingValueOrNode instanceof DoublyLinkedListNode) {
existingNode = existingValueOrNode;
} else {
existingNode = this.getNode(existingValueOrNode);
}
if (existingNode) {
const newNode = new DoublyLinkedListNode(newValue);
newNode.next = existingNode.next;
if (existingNode.next) {
existingNode.next.prev = newNode;
}
newNode.prev = existingNode;
existingNode.next = newNode;
if (existingNode === this.tail) {
this._tail = newNode;
}
this._length++;
return true;
}
return false;
}
/**
* The function returns an iterator that iterates over the values of a linked list.
*/
* [Symbol.iterator]() {
let current = this.head;
while (current) {
yield current.value;
current = current.next;
}
}
}

View file

@ -665,111 +665,6 @@ export class SinglyLinkedList<E = any> {
return count;
}
/**
* Time Complexity: O(n) - Linear time, where n is the length of the list, as it needs to reverse the pointers of each node.
* Space Complexity: O(1) - Constant space.
*/
/**
* Time Complexity: O(n) - Linear time, where n is the length of the list, as it needs to reverse the pointers of each node.
* Space Complexity: O(1) - Constant space.
*
* The `forEach` function iterates over each element in a linked list and applies a callback function to each element.
* @param callback - The callback parameter is a function that takes two arguments: value and index. The value argument
* represents the value of the current node in the linked list, and the index argument represents the index of the
* current node in the linked list.
*/
forEach(callback: (value: E, index: number) => void): void {
let current = this.head;
let index = 0;
while (current) {
callback(current.value, index);
current = current.next;
index++;
}
}
/**
* Time Complexity: O(n) - Linear time, where n is the length of the list, as they need to traverse the entire list to apply the callback or reduce the list.
* Space Complexity: O(n) - Linear space, as they create new nodes or arrays.
*/
/**
* Time Complexity: O(n) - Linear time, where n is the length of the list, as they need to traverse the entire list to apply the callback or reduce the list.
* Space Complexity: O(n) - Linear space, as they create new nodes or arrays.
*
* The `map` function takes a callback function and applies it to each element in the SinglyLinkedList, returning a new
* SinglyLinkedList with the transformed values.
* @param callback - The callback parameter is a function that takes a value of type E (the type of values stored in
* the original SinglyLinkedList) and returns a value of type U (the type of values that will be stored in the mapped
* SinglyLinkedList).
* @returns The `map` function is returning a new instance of `SinglyLinkedList<U>` that contains the mapped values.
*/
map<U>(callback: (value: E) => U): SinglyLinkedList<U> {
const mappedList = new SinglyLinkedList<U>();
let current = this.head;
while (current) {
mappedList.push(callback(current.value));
current = current.next;
}
return mappedList;
}
/**
* Time Complexity: O(n) - Linear time, where n is the length of the list, as they need to traverse the entire list to apply the callback or reduce the list.
* Space Complexity: O(n) - Linear space, as they create new nodes or arrays.
*/
/**
* Time Complexity: O(n) - Linear time, where n is the length of the list, as they need to traverse the entire list to apply the callback or reduce the list.
* Space Complexity: O(n) - Linear space, as they create new nodes or arrays.
*
* The `filter` function iterates through a SinglyLinkedList and returns a new SinglyLinkedList containing only the
* elements that satisfy the given callback function.
* @param callback - The `callback` parameter is a function that takes a value of type `E` and returns a boolean value.
* It is used to determine whether a value should be included in the filtered list or not.
* @returns The filtered list, which is an instance of the SinglyLinkedList class.
*/
filter(callback: (value: E) => boolean): SinglyLinkedList<E> {
const filteredList = new SinglyLinkedList<E>();
let current = this.head;
while (current) {
if (callback(current.value)) {
filteredList.push(current.value);
}
current = current.next;
}
return filteredList;
}
/**
* Time Complexity: O(n) - Linear time, where n is the length of the list, as they need to traverse the entire list to apply the callback or reduce the list.
* Space Complexity: O(n) - Linear space, as they create new nodes or arrays.
*/
/**
* Time Complexity: O(n) - Linear time, where n is the length of the list, as they need to traverse the entire list to apply the callback or reduce the list.
* Space Complexity: O(n) - Linear space, as they create new nodes or arrays.
*
* The `reduce` function iterates over a linked list and applies a callback function to each element, accumulating a
* single value.
* @param callback - The `callback` parameter is a function that takes two arguments: `accumulator` and `value`. It is
* used to perform a specific operation on each element of the linked list.
* @param {U} initialValue - The `initialValue` parameter is the initial value of the accumulator. It is the starting
* point for the reduction operation.
* @returns The `reduce` method is returning the final value of the accumulator after iterating through all the
* elements in the linked list.
*/
reduce<U>(callback: (accumulator: U, value: E) => U, initialValue: U): U {
let accumulator = initialValue;
let current = this.head;
while (current) {
accumulator = callback(accumulator, current.value);
current = current.next;
}
return accumulator;
}
/**
* The function returns an iterator that iterates over the values of a linked list.
*/
@ -781,4 +676,109 @@ export class SinglyLinkedList<E = any> {
current = current.next;
}
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(1)
*
* The `forEach` function iterates over each element in a linked list and applies a callback function to each element.
* @param callback - The callback parameter is a function that takes two arguments: value and index. The value argument
* represents the value of the current node in the linked list, and the index argument represents the index of the
* current node in the linked list.
*/
forEach(callback: (value: E, index: number, list: SinglyLinkedList<E>) => void): void {
let index = 0;
for (const el of this) {
callback(el, index, this);
index++;
}
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*
* The `filter` function iterates through a SinglyLinkedList and returns a new SinglyLinkedList containing only the
* elements that satisfy the given callback function.
* @param callback - The `callback` parameter is a function that takes a value of type `E` and returns a boolean value.
* It is used to determine whether a value should be included in the filtered list or not.
* @returns The filtered list, which is an instance of the SinglyLinkedList class.
*/
filter(callback: (value: E, index: number, list: SinglyLinkedList<E>) => boolean): SinglyLinkedList<E> {
const filteredList = new SinglyLinkedList<E>();
let index = 0;
for (const current of this) {
if (callback(current, index, this)) {
filteredList.push(current);
}
index++;
}
return filteredList;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*
* The `map` function takes a callback function and applies it to each element in the SinglyLinkedList, returning a new
* SinglyLinkedList with the transformed values.
* @param callback - The callback parameter is a function that takes a value of type E (the type of values stored in
* the original SinglyLinkedList) and returns a value of type T (the type of values that will be stored in the mapped
* SinglyLinkedList).
* @returns The `map` function is returning a new instance of `SinglyLinkedList<T>` that contains the mapped values.
*/
map<T>(callback: (value: E, index: number, list: SinglyLinkedList<E>) => T): SinglyLinkedList<T> {
const mappedList = new SinglyLinkedList<T>();
let index = 0;
for (const current of this) {
mappedList.push(callback(current, index, this));
index++;
}
return mappedList;
}
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n), where n is the number of elements in the linked list.
* Space Complexity: O(n)
*
* The `reduce` function iterates over a linked list and applies a callback function to each element, accumulating a
* single value.
* @param callback - The `callback` parameter is a function that takes two arguments: `accumulator` and `value`. It is
* used to perform a specific operation on each element of the linked list.
* @param {T} initialValue - The `initialValue` parameter is the initial value of the accumulator. It is the starting
* point for the reduction operation.
* @returns The `reduce` method is returning the final value of the accumulator after iterating through all the
* elements in the linked list.
*/
reduce<T>(callback: (accumulator: T, value: E, index: number, list: SinglyLinkedList<E>) => T, initialValue: T): T {
let accumulator = initialValue;
let index = 0;
for (const current of this) {
accumulator = callback(accumulator, current, index, this);
index++;
}
return accumulator;
}
}

View file

@ -628,26 +628,6 @@ export class Deque<E> {
this._buckets = newBuckets;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* The `forEach` function iterates over each element in a deque and applies a callback function to
* each element.
* @param callback - The callback parameter is a function that will be called for each element in the
* deque. It takes three parameters:
*/
forEach(callback: (element: E, index: number, deque: Deque<E>) => void) {
for (let i = 0; i < this.size; ++i) {
callback(this.getAt(i), i, this);
}
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
@ -674,101 +654,6 @@ export class Deque<E> {
return undefined;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `toArray` function converts the elements of a data structure into an array.
* @returns The `toArray()` method is returning an array of elements of type `E`.
*/
toArray(): E[] {
const arr: E[] = [];
for (let i = 0; i < this.size; ++i) {
arr.push(this.getAt(i));
}
return arr;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `map` function takes a callback function and applies it to each element in the deque,
* returning a new deque with the results.
* @param callback - The `callback` parameter is a function that takes three arguments:
* @returns The `map` method is returning a new `Deque` object with the transformed elements.
*/
map<T>(callback: (element: E, index: number, deque: Deque<E>) => T): Deque<T> {
const newDeque = new Deque<T>([], this._bucketSize);
for (let i = 0; i < this.size; ++i) {
newDeque.push(callback(this.getAt(i), i, this));
}
return newDeque;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `filter` function creates a new deque containing only the elements that satisfy the given
* predicate function.
* @param predicate - The `predicate` parameter is a function that takes three arguments: `element`,
* `index`, and `deque`.
* @returns The `filter` method is returning a new `Deque` object that contains only the elements
* that satisfy the given `predicate` function.
*/
filter(predicate: (element: E, index: number, deque: Deque<E>) => boolean): Deque<E> {
const newDeque = new Deque<E>([], this._bucketSize);
for (let i = 0; i < this.size; ++i) {
const element = this.getAt(i);
if (predicate(element, i, this)) {
newDeque.push(element);
}
}
return newDeque;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* The `reduce` function iterates over the elements of a deque and applies a callback function to
* each element, accumulating a single value.
* @param callback - The `callback` parameter is a function that takes four arguments:
* @param {T} initialValue - The `initialValue` parameter is the initial value of the accumulator. It
* is the value that will be passed as the first argument to the `callback` function when reducing
* the elements of the deque.
* @returns the final value of the accumulator after iterating over all elements in the deque and
* applying the callback function to each element.
*/
reduce<T>(callback: (accumulator: T, element: E, index: number, deque: Deque<E>) => T, initialValue: T): T {
let accumulator = initialValue;
for (let i = 0; i < this.size; ++i) {
accumulator = callback(accumulator, this.getAt(i), i, this);
}
return accumulator;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
@ -794,6 +679,26 @@ export class Deque<E> {
return -1;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `toArray` function converts the elements of a data structure into an array.
* @returns The `toArray()` method is returning an array of elements of type `E`.
*/
toArray(): E[] {
const arr: E[] = [];
for (let i = 0; i < this.size; ++i) {
arr.push(this.getAt(i));
}
return arr;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
@ -812,6 +717,108 @@ export class Deque<E> {
}
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* The `forEach` function iterates over each element in a deque and applies a callback function to
* each element.
* @param callback - The callback parameter is a function that will be called for each element in the
* deque. It takes three parameters:
*/
forEach(callback: (element: E, index: number, deque: this) => void) {
let index = 0;
for (const el of this) {
callback(el, index, this);
index++;
}
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `filter` function creates a new deque containing only the elements that satisfy the given
* predicate function.
* @param predicate - The `predicate` parameter is a function that takes three arguments: `element`,
* `index`, and `deque`.
* @returns The `filter` method is returning a new `Deque` object that contains only the elements
* that satisfy the given `predicate` function.
*/
filter(predicate: (element: E, index: number, deque: this) => boolean): Deque<E> {
const newDeque = new Deque<E>([], this._bucketSize);
let index = 0;
for (const el of this) {
if (predicate(el, index, this)) {
newDeque.push(el);
}
index++;
}
return newDeque;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `map` function takes a callback function and applies it to each element in the deque,
* returning a new deque with the results.
* @param callback - The `callback` parameter is a function that takes three arguments:
* @returns The `map` method is returning a new `Deque` object with the transformed elements.
*/
map<T>(callback: (element: E, index: number, deque: this) => T): Deque<T> {
const newDeque = new Deque<T>([], this._bucketSize);
let index = 0;
for (const el of this) {
newDeque.push(callback(el, index, this));
index++;
}
return newDeque;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* The `reduce` function iterates over the elements of a deque and applies a callback function to
* each element, accumulating a single value.
* @param callback - The `callback` parameter is a function that takes four arguments:
* @param {T} initialValue - The `initialValue` parameter is the initial value of the accumulator. It
* is the value that will be passed as the first argument to the `callback` function when reducing
* the elements of the deque.
* @returns the final value of the accumulator after iterating over all elements in the deque and
* applying the callback function to each element.
*/
reduce<T>(callback: (accumulator: T, element: E, index: number, deque: this) => T, initialValue: T): T {
let accumulator = initialValue;
let index = 0;
for (const el of this) {
accumulator = callback(accumulator, el, index, this);
index++;
}
return accumulator;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)

View file

@ -305,4 +305,88 @@ export class Queue<E = any> {
yield item;
}
}
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(1)
*
* The `forEach` function iterates over each element in a deque and applies a callback function to
* each element.
* @param callback - The callback parameter is a function that will be called for each element in the
* deque. It takes three parameters:
*/
forEach(callback: (element: E, index: number, queue: this) => void) {
let index = 0;
for (const el of this) {
callback(el, index, this);
index++;
}
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `filter` function creates a new deque containing only the elements that satisfy the given
* predicate function.
* @param predicate - The `predicate` parameter is a function that takes three arguments: `element`,
* `index`, and `deque`.
* @returns The `filter` method is returning a new `Queue` object that contains only the elements
* that satisfy the given `predicate` function.
*/
filter(predicate: (element: E, index: number, queue: this) => boolean): Queue<E> {
const newDeque = new Queue<E>([]);
let index = 0;
for (const el of this) {
if (predicate(el, index, this)) {
newDeque.push(el);
}
index++;
}
return newDeque;
}
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
/**
* Time Complexity: O(n)
* Space Complexity: O(n)
*
* The `map` function takes a callback function and applies it to each element in the deque,
* returning a new deque with the results.
* @param callback - The `callback` parameter is a function that takes three arguments:
* @returns The `map` method is returning a new `Queue` object with the transformed elements.
*/
map<T>(callback: (element: E, index: number, queue: this) => T): Queue<T> {
const newDeque = new Queue<T>([]);
let index = 0;
for (const el of this) {
newDeque.push(callback(el, index, this));
index++;
}
return newDeque;
}
reduce<T>(callback: (accumulator: T, element: E, index: number, queue: this) => T, initialValue: T): T {
let accumulator = initialValue;
let index = 0;
for (const el of this) {
accumulator = callback(accumulator, el, index, this);
index++;
}
return accumulator;
}
}

View file

@ -25,6 +25,14 @@ export class Stack<E = any> {
* Space Complexity: O(n), as it creates a new stack with the elements from the input array.
*/
/**
* The size() function returns the number of elements in an array.
* @returns The size of the elements array.
*/
get size(): number {
return this.elements.length;
}
/**
* Time Complexity: O(n), where n is the number of elements in the input array. Similar to the constructor, it requires iterating through each element.
* Space Complexity: O(n), as it creates a new stack with the elements from the input array.
@ -46,14 +54,6 @@ export class Stack<E = any> {
return this.elements.length === 0;
}
/**
* The size() function returns the number of elements in an array.
* @returns The size of the elements array.
*/
size(): number {
return this.elements.length;
}
/**
* Time Complexity: O(1), as it only involves accessing the last element of the array.
* Space Complexity: O(1), as it does not use any additional space.
@ -147,4 +147,60 @@ export class Stack<E = any> {
clone(): Stack<E> {
return new Stack(this.elements.slice());
}
/**
* Custom iterator for the Stack class.
* @returns An iterator object.
*/
* [Symbol.iterator]() {
for (let i = this.elements.length - 1; i >= 0; i--) {
yield this.elements[i];
}
}
/**
* Applies a function to each element of the stack.
* @param {function(E): void} callback - A function to apply to each element.
*/
forEach(callback: (element: E, index: number, stack: this) => void): void {
let index = 0;
for (const el of this) {
callback(el, index, this);
index++;
}
}
filter(predicate: (element: E, index: number, stack: this) => boolean): Stack<E> {
const newStack = new Stack<E>();
let index = 0;
for (const el of this) {
if (predicate(el, index, this)) {
newStack.push(el);
}
index++;
}
return newStack;
}
map<T>(callback: (element: E, index: number, stack: this) => T): Stack<T> {
const newStack = new Stack<T>();
let index = 0;
for (const el of this) {
newStack.push(callback(el, index, this));
index++;
}
return newStack;
}
reduce<T>(callback: (accumulator: T, element: E, index: number, stack: this) => T, initialValue: T): T {
let accumulator = initialValue;
let index = 0;
for (const el of this) {
accumulator = callback(accumulator, el, index, this);
index++;
}
return accumulator;
}
}

View file

@ -324,6 +324,59 @@ export class Trie {
return words;
}
* [Symbol.iterator](): IterableIterator<string> {
function* _dfs(node: TrieNode, path: string): IterableIterator<string> {
if (node.isEnd) {
yield path;
}
for (const [char, childNode] of node.children) {
yield* _dfs(childNode, path + char);
}
}
yield* _dfs(this.root, '');
}
forEach(callback: (word: string, index: number, trie: this) => void): void {
let index = 0;
for (const word of this) {
callback(word, index, this);
index++;
}
}
filter(predicate: (word: string, index: number, trie: this) => boolean): string[] {
const results: string[] = [];
let index = 0;
for (const word of this) {
if (predicate(word, index, this)) {
results.push(word);
}
index++;
}
return results;
}
map(callback: (word: string, index: number, trie: this) => string): Trie {
const newTrie = new Trie();
let index = 0;
for (const word of this) {
newTrie.add(callback(word, index, this));
index++;
}
return newTrie;
}
reduce<T>(callback: (accumulator: T, word: string, index: number, trie: this) => T, initialValue: T): T {
let accumulator = initialValue;
let index = 0;
for (const word of this) {
accumulator = callback(accumulator, word, index, this);
index++;
}
return accumulator;
}
/**
* Time Complexity: O(M), where M is the length of the input string.
* Space Complexity: O(1) - Constant space.

View file

View file

@ -3,20 +3,29 @@ import * as Benchmark from 'benchmark';
import { magnitude } from '../../../utils';
const suite = new Benchmark.Suite();
const { TEN_THOUSAND } = magnitude;
const { HUNDRED_THOUSAND, TEN_THOUSAND } = magnitude;
suite
.add(`${TEN_THOUSAND.toLocaleString()} add & pop`, () => {
.add(`${HUNDRED_THOUSAND.toLocaleString()} add & pop`, () => {
const heap = new Heap<number>({ comparator: (a, b) => b - a });
for (let i = 0; i < TEN_THOUSAND; i++) {
for (let i = 0; i < HUNDRED_THOUSAND; i++) {
heap.add(i);
}
for (let i = 0; i < TEN_THOUSAND; i++) {
for (let i = 0; i < HUNDRED_THOUSAND; i++) {
heap.pop();
}
})
.add(`${HUNDRED_THOUSAND.toLocaleString()} add & dfs`, () => {
const heap = new Heap<number>({ comparator: (a, b) => b - a });
for (let i = 0; i < HUNDRED_THOUSAND; i++) {
heap.add(i);
}
heap.dfs();
})
.add(`${TEN_THOUSAND.toLocaleString()} fib add & pop`, () => {
const fbHeap = new FibonacciHeap<number>();
for (let i = 1; i <= TEN_THOUSAND; i++) {

View file

@ -8,7 +8,7 @@ describe('HashNode', () => {
expect(hashNode.key).toBe(key);
expect(hashNode.value).toBe(value);
expect(hashNode.next).toBe(null);
expect(hashNode.next).toBe(undefined);
});
});
@ -16,7 +16,7 @@ describe('HashTable', () => {
it('should initialize with default capacity', () => {
const hashTable = new HashTable<string, string>();
expect(hashTable.capacity).toBe(16);
expect(hashTable.buckets).toEqual(new Array(16).fill(null));
expect(hashTable.buckets).toEqual(new Array(16).fill(undefined));
expect(hashTable.hashFn('a')).toBe(6);
expect(hashTable.capacity).toBe(16);
expect(hashTable.size).toBe(0);
@ -184,3 +184,59 @@ describe('HashTable performance', function () {
}
});
});
describe('HashTable methods', () => {
let hashTable: HashTable<string, string>;
beforeEach(() => {
hashTable = new HashTable();
for (let i = 0; i < 10; i++) {
hashTable.set(`key${i}`, `value${i}`);
}
});
test('should retrieve correct values with get method', () => {
for (let i = 0; i < 10; i++) {
expect(hashTable.get(`key${i}`)).toBe(`value${i}`);
}
});
// test('forEach should apply a function to each key-value pair', () => {
// const mockCallback = jest.fn();
// hashTable.forEach(mockCallback);
//
// expect(mockCallback.mock.calls.length).toBe(10);
// for (let i = 0; i < 10; i++) {
// // Check whether each key-value pair has been called before, regardless of the order
// const call = mockCallback.mock.calls.find(call => call[1] === `value${i}`);
// expect(call).toBeTruthy();
// expect(call[0]).toBe(`key${i}`);
// }
// });
test('filter should return a new HashTable with elements that satisfy the condition', () => {
const filtered = hashTable.filter(([key]) => key.endsWith('1') || key.endsWith('3'));
expect(filtered.size).toBe(2);
expect(filtered.get('key1')).toBe('value1');
expect(filtered.get('key3')).toBe('value3');
});
test('map should return a new HashTable with mapped values', () => {
const mapped = hashTable.map(([, value]) => value.toUpperCase());
for (let i = 0; i < 10; i++) {
expect(mapped.get(`key${i}`)).toBe(`value${i}`.toUpperCase());
}
});
test('reduce should accumulate values based on the reducer function', () => {
const result = hashTable.reduce((acc, [, value]) => `${acc}-${value}`, '');
expect(result).toBe('-value5-value7-value3-value4-value6-value0-value2-value8-value1-value9');
});
});

View file

@ -49,4 +49,52 @@ describe('MinHeap', () => {
minHeap.poll();
expect(minHeap.isEmpty()).toBe(true);
});
const n = 100000;
it('should push & dfs', () => {
for (let i = 0; i < n; i++) {
minHeap.push(i);
}
expect(minHeap.dfs()[0]).toBe(0)
expect(minHeap.dfs()[999]).toBe(4126)
});
});
describe('Heap iterative methods', () => {
let heap: MinHeap<number>;
beforeEach(() => {
heap = new MinHeap<number>();
for (let i = 1; i <= 10; i++) {
heap.add(i * 10); // Add 10, 20, ..., 100
}
});
test('Heap is iterable', () => {
expect([...heap]).toEqual([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]);
});
test('forEach method calls a function for each element', () => {
const mockCallback = jest.fn();
heap.forEach(mockCallback);
expect(mockCallback.mock.calls.length).toBe(10);
});
test('filter method returns filtered elements', () => {
const result = heap.filter(x => x > 50);
expect([...result]).toEqual([60, 70, 80, 90, 100]);
});
test('map method correctly maps elements', () => {
const result = heap.map(x => x / 10, (a: number, b: number) => a - b);
expect([...result]).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
});
test('reduce method correctly reduces elements', () => {
const result = heap.reduce((acc, curr) => acc + curr, 0);
expect(result).toBe(550); // 10+20+...+100 = 550
});
});

View file

@ -166,7 +166,7 @@ describe('DoublyLinkedList Operation Test', () => {
list.reverse();
expect(list.toArray()).toEqual([3, 2, 1]);
expect(list.toArrayBackward()).toEqual([1, 2, 3]);
expect(list.toReversedArray()).toEqual([1, 2, 3]);
});
it('should map elements using a callback function', () => {
@ -268,7 +268,7 @@ describe('DoublyLinkedList Operation Test', () => {
list.push(2);
list.push(3);
const reversedArray = list.toArrayBackward();
const reversedArray = list.toReversedArray();
expect(reversedArray).toEqual([3, 2, 1]);
});

View file

@ -205,3 +205,40 @@ describe('Queue Performance Test', () => {
expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100);
});
});
describe('Queue iterative methods', () => {
let queue: Queue<number>;
beforeEach(() => {
queue = new Queue();
for (let i = 0; i < 10; i++) {
queue.enqueue(i);
}
});
test('iterator should provide access to all elements', () => {
const elements = [];
for (const item of queue) {
elements.push(item);
}
expect(elements).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
});
test('forEach should apply the callback to each element', () => {
const elements: number[] = [];
queue.forEach((element) => elements.push(element * 2));
expect(elements).toEqual([0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
});
test('filter should return a new queue with only the elements that satisfy the predicate', () => {
const filteredQueue = queue.filter(element => element % 2 === 0);
expect([...filteredQueue]).toEqual([0, 2, 4, 6, 8]);
});
test('map should return a new queue with the transformed elements', () => {
const mappedQueue = queue.map(element => element * 2);
expect([...mappedQueue]).toEqual([0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
});
});

View file

@ -15,7 +15,7 @@ describe('Stack', () => {
stack.push(1);
stack.push(2);
stack.push(3);
expect(stack.size()).toBe(3);
expect(stack.size).toBe(3);
});
it('should peek at the top element without removing it', () => {
@ -23,7 +23,7 @@ describe('Stack', () => {
stack.push(2);
stack.push(3);
expect(stack.peek()).toBe(3);
expect(stack.size()).toBe(3);
expect(stack.size).toBe(3);
});
it('should pop elements from the stack', () => {
@ -32,7 +32,7 @@ describe('Stack', () => {
stack.push(3);
const poppedElement = stack.pop();
expect(poppedElement).toBe(3);
expect(stack.size()).toBe(2);
expect(stack.size).toBe(2);
});
it('should return null when popping from an empty stack', () => {
@ -53,7 +53,7 @@ describe('Stack', () => {
stack.push(2);
stack.push(3);
stack.clear();
expect(stack.size()).toBe(0);
expect(stack.size).toBe(0);
expect(stack.isEmpty()).toBe(true);
});
@ -61,7 +61,57 @@ describe('Stack', () => {
stack.push(1);
stack.push(2);
const clonedStack = stack.clone();
expect(clonedStack.size()).toBe(2);
expect(clonedStack.size).toBe(2);
expect(clonedStack.pop()).toBe(2);
});
});
describe('Stack iterative methods', () => {
let stack: Stack<number>; // Declare a Stack instance
beforeEach(() => {
stack = new Stack<number>(); // Create a new Stack instance before each test
stack.push(1);
stack.push(2);
stack.push(3);
});
test('should iterate through the stack', () => {
const result: number[] = [];
for (const element of stack) {
result.push(element);
}
expect(result).toEqual([3, 2, 1]); // iteration should start from the top of the stack
});
test('should apply forEach to the stack', () => {
const result: number[] = [];
stack.forEach((element) => {
result.push(element);
});
expect(result).toEqual([3, 2, 1]);
});
test('should filter elements in the stack', () => {
const filteredStack = stack.filter((element) => element > 1);
expect(filteredStack.size).toBe(2);
expect([...filteredStack]).toEqual([2, 3]);
});
test('should map elements in the stack', () => {
const mappedStack = stack.map((element) => element * 2);
expect(mappedStack.size).toBe(3);
expect([...mappedStack]).toEqual([2, 4, 6]);
});
test('should reduce elements in the stack', () => {
const sum = stack.reduce((accumulator, element) => accumulator + element, 0);
expect(sum).toBe(6);
});
});

View file

@ -823,3 +823,36 @@ describe('Trie operations', () => {
expect(trie.getHeight()).toBe(6); // Assuming 'apple' and 'banana' are the longest words.
});
});
describe('Trie class', () => {
let trie: Trie;
beforeEach(() => {
trie = new Trie(['apple', 'app', 'banana', 'band', 'bandana']);
});
test('[Symbol.iterator] should iterate over all words', () => {
const words = [...trie];
expect(words).toEqual(['app', 'apple', 'banana', 'band', 'bandana']);
});
test('forEach should execute a callback for each word', () => {
const mockCallback = jest.fn();
trie.forEach(mockCallback);
expect(mockCallback).toHaveBeenCalledTimes(5);
});
test('filter should return words that satisfy the predicate', () => {
const filteredWords = trie.filter(word => word.startsWith('ba'));
expect(filteredWords).toEqual(['banana', 'band', 'bandana']);
});
test('map should apply a function to each word', () => {
const mappedWords = trie.map(word => word.length.toString());
expect([...mappedWords]).toEqual(['3', '5', '6', '4', '7']);
});
test('reduce should reduce the words to a single value', () => {
const concatenatedWords = trie.reduce((acc, word) => acc + word, '');
expect(concatenatedWords).toEqual('appapplebananabandbandana');
});
});