feat: Provide convenient option of the raw data processing function 'toEntry' in HashMap.options.

This commit is contained in:
Revone 2023-12-25 20:24:41 +08:00
parent eb590d6c27
commit 689aa0f57e
24 changed files with 154 additions and 108 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.49.7](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
## [v1.49.8](https://github.com/zrwusa/data-structure-typed/compare/v1.35.0...main) (upcoming)
### Changes

View file

@ -1,6 +1,6 @@
{
"name": "data-structure-typed",
"version": "1.49.7",
"version": "1.49.8",
"description": "Data Structures of Javascript & TypeScript. Heap, Binary Tree, Red Black Tree, Linked List, Deque, Trie, HashMap, Directed Graph, Undirected Graph, Binary Search Tree(BST), AVL Tree, Priority Queue, Graph, Queue, Tree Multiset, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue, Stack. Benchmark compared with C++ STL. API aligned with ES6 and Java.util. Usability is comparable to Python",
"main": "dist/cjs/index.js",
"module": "dist/mjs/index.js",

View file

@ -15,7 +15,7 @@ export abstract class IterableEntryBase<K = any, V = any> {
* allows the function to accept any number of arguments as an array. In this case, the `args`
* parameter is used to pass any additional arguments to the `_getIterator` method.
*/
* [Symbol.iterator](...args: any[]): IterableIterator<[K, V]> {
*[Symbol.iterator](...args: any[]): IterableIterator<[K, V]> {
yield* this._getIterator(...args);
}
@ -30,7 +30,7 @@ export abstract class IterableEntryBase<K = any, V = any> {
* The function returns an iterator that yields key-value pairs from the object, where the value can
* be undefined.
*/
* entries(): IterableIterator<[K, V | undefined]> {
*entries(): IterableIterator<[K, V | undefined]> {
for (const item of this) {
yield item;
}
@ -46,7 +46,7 @@ export abstract class IterableEntryBase<K = any, V = any> {
*
* The function returns an iterator that yields the keys of a data structure.
*/
* keys(): IterableIterator<K> {
*keys(): IterableIterator<K> {
for (const item of this) {
yield item[0];
}
@ -62,7 +62,7 @@ export abstract class IterableEntryBase<K = any, V = any> {
*
* The function returns an iterator that yields the values of a collection.
*/
* values(): IterableIterator<V> {
*values(): IterableIterator<V> {
for (const item of this) {
yield item[1];
}
@ -212,7 +212,7 @@ export abstract class IterableElementBase<V> {
* allows the function to accept any number of arguments as an array. In this case, the `args`
* parameter is used to pass any number of arguments to the `_getIterator` method.
*/
* [Symbol.iterator](...args: any[]): IterableIterator<V> {
*[Symbol.iterator](...args: any[]): IterableIterator<V> {
yield* this._getIterator(...args);
}
@ -226,7 +226,7 @@ export abstract class IterableElementBase<V> {
*
* The function returns an iterator that yields all the values in the object.
*/
* values(): IterableIterator<V> {
*values(): IterableIterator<V> {
for (const item of this) {
yield item;
}

View file

@ -40,13 +40,14 @@ export class AVLTreeNode<K = any, V = any, N extends AVLTreeNode<K, V, N> = AVLT
* 7. Path Length: The path length from the root to any leaf is longer compared to an unbalanced BST, but shorter than a linear chain of nodes.
*/
export class AVLTree<
K = any,
V = any,
N extends AVLTreeNode<K, V, N> = AVLTreeNode<K, V, AVLTreeNodeNested<K, V>>,
TREE extends AVLTree<K, V, N, TREE> = AVLTree<K, V, N, AVLTreeNested<K, V, N>>
>
K = any,
V = any,
N extends AVLTreeNode<K, V, N> = AVLTreeNode<K, V, AVLTreeNodeNested<K, V>>,
TREE extends AVLTree<K, V, N, TREE> = AVLTree<K, V, N, AVLTreeNested<K, V, N>>
>
extends BST<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
implements IBinaryTree<K, V, N, TREE>
{
/**
* The constructor function initializes an AVLTree object with optional keysOrNodesOrEntries and options.
* @param [keysOrNodesOrEntries] - The `keysOrNodesOrEntries` parameter is an optional iterable of `KeyOrNodeOrEntry<K, V, N>`
@ -277,7 +278,7 @@ export class AVLTree<
// Balance Restoration: If a balance issue is discovered after inserting a node, it requires balance restoration operations. Balance restoration includes four basic cases where rotation operations need to be performed to fix the balance:
switch (
this._balanceFactor(A) // second O(1)
) {
) {
case -2:
if (A && A.left) {
if (this._balanceFactor(A.left) <= 0) {

View file

@ -101,13 +101,14 @@ export class BinaryTreeNode<
*/
export class BinaryTree<
K = any,
V = any,
N extends BinaryTreeNode<K, V, N> = BinaryTreeNode<K, V, BinaryTreeNodeNested<K, V>>,
TREE extends BinaryTree<K, V, N, TREE> = BinaryTree<K, V, N, BinaryTreeNested<K, V, N>>
>
K = any,
V = any,
N extends BinaryTreeNode<K, V, N> = BinaryTreeNode<K, V, BinaryTreeNodeNested<K, V>>,
TREE extends BinaryTree<K, V, N, TREE> = BinaryTree<K, V, N, BinaryTreeNested<K, V, N>>
>
extends IterableEntryBase<K, V | undefined>
implements IBinaryTree<K, V, N, TREE> {
implements IBinaryTree<K, V, N, TREE>
{
iterationType = IterationType.ITERATIVE;
/**
@ -1922,7 +1923,7 @@ export class BinaryTree<
display(beginRoot);
}
protected* _getIterator(node = this.root): IterableIterator<[K, V | undefined]> {
protected *_getIterator(node = this.root): IterableIterator<[K, V | undefined]> {
if (!node) return;
if (this.iterationType === IterationType.ITERATIVE) {

View file

@ -83,13 +83,14 @@ export class BSTNode<K = any, V = any, N extends BSTNode<K, V, N> = BSTNodeNeste
* 7. No Auto-Balancing: Standard BSTs don't automatically balance themselves.
*/
export class BST<
K = any,
V = any,
N extends BSTNode<K, V, N> = BSTNode<K, V, BSTNodeNested<K, V>>,
TREE extends BST<K, V, N, TREE> = BST<K, V, N, BSTNested<K, V, N>>
>
K = any,
V = any,
N extends BSTNode<K, V, N> = BSTNode<K, V, BSTNodeNested<K, V>>,
TREE extends BST<K, V, N, TREE> = BST<K, V, N, BSTNested<K, V, N>>
>
extends BinaryTree<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
implements IBinaryTree<K, V, N, TREE>
{
/**
* This is the constructor function for a binary search tree class in TypeScript, which initializes
* the tree with optional keysOrNodesOrEntries and options.

View file

@ -41,13 +41,14 @@ export class RedBlackTreeNode<
* 5. Black balance: Every path from any node to each of its leaf nodes contains the same number of black nodes.
*/
export class RedBlackTree<
K = any,
V = any,
N extends RedBlackTreeNode<K, V, N> = RedBlackTreeNode<K, V, RedBlackTreeNodeNested<K, V>>,
TREE extends RedBlackTree<K, V, N, TREE> = RedBlackTree<K, V, N, RedBlackTreeNested<K, V, N>>
>
K = any,
V = any,
N extends RedBlackTreeNode<K, V, N> = RedBlackTreeNode<K, V, RedBlackTreeNodeNested<K, V>>,
TREE extends RedBlackTree<K, V, N, TREE> = RedBlackTree<K, V, N, RedBlackTreeNested<K, V, N>>
>
extends BST<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
implements IBinaryTree<K, V, N, TREE>
{
Sentinel: N = new RedBlackTreeNode<K, V>(NaN as K) as unknown as N;
/**

View file

@ -45,13 +45,14 @@ export class TreeMultimapNode<
* The only distinction between a TreeMultimap and a AVLTree lies in the ability of the former to store duplicate nodes through the utilization of counters.
*/
export class TreeMultimap<
K = any,
V = any,
N extends TreeMultimapNode<K, V, N> = TreeMultimapNode<K, V, TreeMultimapNodeNested<K, V>>,
TREE extends TreeMultimap<K, V, N, TREE> = TreeMultimap<K, V, N, TreeMultimapNested<K, V, N>>
>
K = any,
V = any,
N extends TreeMultimapNode<K, V, N> = TreeMultimapNode<K, V, TreeMultimapNodeNested<K, V>>,
TREE extends TreeMultimap<K, V, N, TREE> = TreeMultimap<K, V, N, TreeMultimapNested<K, V, N>>
>
extends AVLTree<K, V, N, TREE>
implements IBinaryTree<K, V, N, TREE> {
implements IBinaryTree<K, V, N, TREE>
{
constructor(keysOrNodesOrEntries: Iterable<KeyOrNodeOrEntry<K, V, N>> = [], options?: TreeMultimapOptions<K>) {
super([], options);
if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries);

View file

@ -61,13 +61,14 @@ export abstract class AbstractEdge<E = any> {
}
export abstract class AbstractGraph<
V = any,
E = any,
VO extends AbstractVertex<V> = AbstractVertex<V>,
EO extends AbstractEdge<E> = AbstractEdge<E>
>
V = any,
E = any,
VO extends AbstractVertex<V> = AbstractVertex<V>,
EO extends AbstractEdge<E> = AbstractEdge<E>
>
extends IterableEntryBase<VertexKey, V | undefined>
implements IGraph<V, E, VO, EO> {
implements IGraph<V, E, VO, EO>
{
constructor() {
super();
}
@ -610,14 +611,14 @@ export abstract class AbstractGraph<
}
getMinDist &&
distMap.forEach((d, v) => {
if (v !== srcVertex) {
if (d < minDist) {
minDist = d;
if (genPaths) minDest = v;
distMap.forEach((d, v) => {
if (v !== srcVertex) {
if (d < minDist) {
minDist = d;
if (genPaths) minDest = v;
}
}
}
});
});
genPaths && getPaths(minDest);
@ -1272,7 +1273,7 @@ export abstract class AbstractGraph<
return mapped;
}
protected* _getIterator(): IterableIterator<[VertexKey, V | undefined]> {
protected *_getIterator(): IterableIterator<[VertexKey, V | undefined]> {
for (const vertex of this._vertexMap.values()) {
yield [vertex.key, vertex.value];
}

View file

@ -46,13 +46,14 @@ export class DirectedEdge<E = any> extends AbstractEdge<E> {
}
export class DirectedGraph<
V = any,
E = any,
VO extends DirectedVertex<V> = DirectedVertex<V>,
EO extends DirectedEdge<E> = DirectedEdge<E>
>
V = any,
E = any,
VO extends DirectedVertex<V> = DirectedVertex<V>,
EO extends DirectedEdge<E> = DirectedEdge<E>
>
extends AbstractGraph<V, E, VO, EO>
implements IGraph<V, E, VO, EO> {
implements IGraph<V, E, VO, EO>
{
/**
* The constructor function initializes an instance of a class.
*/

View file

@ -43,13 +43,14 @@ export class UndirectedEdge<E = number> extends AbstractEdge<E> {
}
export class UndirectedGraph<
V = any,
E = any,
VO extends UndirectedVertex<V> = UndirectedVertex<V>,
EO extends UndirectedEdge<E> = UndirectedEdge<E>
>
V = any,
E = any,
VO extends UndirectedVertex<V> = UndirectedVertex<V>,
EO extends UndirectedEdge<E> = UndirectedEdge<E>
>
extends AbstractGraph<V, E, VO, EO>
implements IGraph<V, E, VO, EO> {
implements IGraph<V, E, VO, EO>
{
/**
* The constructor initializes a new Map object to store edgeMap.
*/

View file

@ -21,28 +21,48 @@ import { isWeakKey, rangeCheck } from '../../utils';
* 3. Unique Keys: Keys are unique. If you try to insert another entry with the same key, the old entry will be replaced by the new one.
* 4. Unordered Collection: HashMap does not guarantee the order of entries, and the order may change over time.
*/
export class HashMap<K = any, V = any> extends IterableEntryBase<K, V> {
export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K, V> {
protected _store: { [key: string]: HashMapStoreItem<K, V> } = {};
protected _objMap: Map<object, V> = new Map();
protected _toEntryFn: (rawElement: R) => [K, V] = (rawElement: R) => {
if (this.isEntry(rawElement)) {
// TODO, For performance optimization, it may be necessary to only inspect the first element traversed.
return rawElement;
} else {
throw new Error(
"If the provided rawCollection does not adhere to the [key, value] type format, the toEntryFn in the constructor's options parameter needs to specified."
);
}
};
get toEntryFn() {
return this._toEntryFn;
}
isEntry(rawElement: any): rawElement is [K, V] {
return Array.isArray(rawElement) && rawElement.length === 2;
}
/**
* The constructor function initializes a new instance of a class with optional entries and options.
* @param entries - The `entries` parameter is an iterable containing key-value pairs `[K, V]`. It
* is optional and defaults to an empty array `[]`. This parameter is used to initialize the map with
* key-value pairs.
* @param [options] - The `options` parameter is an optional object that can contain additional
* configuration options for the constructor. In this case, it has one property:
* The constructor function initializes a HashMap object with an optional initial collection and
* options.
* @param rawCollection - The `rawCollection` parameter is an iterable collection of elements of type
* `T`. It is an optional parameter and its default value is an empty array `[]`.
* @param [options] - The `options` parameter is an optional object that can contain two properties:
*/
constructor(entries: Iterable<[K, V]> = [], options?: HashMapOptions<K>) {
constructor(rawCollection: Iterable<R> = [], options?: HashMapOptions<K, V, R>) {
super();
if (options) {
const { hashFn } = options;
const { hashFn, toEntryFn } = options;
if (hashFn) {
this._hashFn = hashFn;
}
if (toEntryFn) {
this._toEntryFn = toEntryFn;
}
}
if (entries) {
this.setMany(entries);
if (rawCollection) {
this.setMany(rawCollection);
}
}
@ -88,13 +108,18 @@ export class HashMap<K = any, V = any> extends IterableEntryBase<K, V> {
}
/**
* The function "setMany" sets multiple key-value pairs in a map.
* @param entries - The `entries` parameter is an iterable containing key-value pairs. Each
* key-value pair is represented as an array with two entries: the key and the value.
* The function `setMany` takes an iterable collection of objects, maps each object to a key-value
* pair using a mapping function, and sets each key-value pair in the current object.
* @param rawCollection - The `rawCollection` parameter is an iterable collection of elements of type
* `T`.
* @returns The `setMany` function is returning an array of booleans.
*/
setMany(entries: Iterable<[K, V]>): boolean[] {
setMany(rawCollection: Iterable<R>): boolean[] {
const results: boolean[] = [];
for (const [key, value] of entries) results.push(this.set(key, value));
for (const rawEle of rawCollection) {
const [key, value] = this.toEntryFn(rawEle);
results.push(this.set(key, value));
}
return results;
}
@ -223,7 +248,7 @@ export class HashMap<K = any, V = any> extends IterableEntryBase<K, V> {
* The function returns an iterator that yields key-value pairs from both an object store and an
* object map.
*/
protected* _getIterator(): IterableIterator<[K, V]> {
protected *_getIterator(): IterableIterator<[K, V]> {
for (const node of Object.values(this._store)) {
yield [node.key, node.value] as [K, V];
}
@ -322,7 +347,7 @@ export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
/**
* The `begin()` function in TypeScript iterates over a linked list and yields key-value pairs.
*/
* begin() {
*begin() {
let node = this._head;
while (node !== this._sentinel) {
yield [node.key, node.value];
@ -334,7 +359,7 @@ export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
* The function `reverseBegin()` iterates over a linked list in reverse order, yielding each node's
* key and value.
*/
* reverseBegin() {
*reverseBegin() {
let node = this._tail;
while (node !== this._sentinel) {
yield [node.key, node.value];
@ -635,7 +660,7 @@ export class LinkedHashMap<K = any, V = any> extends IterableEntryBase<K, V> {
*
* The above function is an iterator that yields key-value pairs from a linked list.
*/
protected* _getIterator() {
protected *_getIterator() {
let node = this._head;
while (node !== this._sentinel) {
yield [node.key, node.value] as [K, V];

View file

@ -391,7 +391,7 @@ export class Heap<E = any> extends IterableElementBase<E> {
return mappedHeap;
}
protected* _getIterator(): IterableIterator<E> {
protected *_getIterator(): IterableIterator<E> {
for (const element of this.elements) {
yield element;
}

View file

@ -808,7 +808,7 @@ export class DoublyLinkedList<E = any> extends IterableElementBase<E> {
/**
* The function returns an iterator that iterates over the values of a linked list.
*/
protected* _getIterator(): IterableIterator<E> {
protected *_getIterator(): IterableIterator<E> {
let current = this.head;
while (current) {

View file

@ -741,7 +741,7 @@ export class SinglyLinkedList<E = any> extends IterableElementBase<E> {
return mappedList;
}
protected* _getIterator(): IterableIterator<E> {
protected *_getIterator(): IterableIterator<E> {
let current = this.head;
while (current) {

View file

@ -232,7 +232,7 @@ export class Deque<E> extends IterableElementBase<E> {
/**
* The below function is a generator that yields elements from a collection one by one.
*/
* begin(): Generator<E> {
*begin(): Generator<E> {
let index = 0;
while (index < this.size) {
yield this.getAt(index);
@ -244,7 +244,7 @@ export class Deque<E> extends IterableElementBase<E> {
* The function `reverseBegin()` is a generator that yields elements in reverse order starting from
* the last element.
*/
* reverseBegin(): Generator<E> {
*reverseBegin(): Generator<E> {
let index = this.size - 1;
while (index >= 0) {
yield this.getAt(index);
@ -735,7 +735,7 @@ export class Deque<E> extends IterableElementBase<E> {
* The above function is an implementation of the iterator protocol in TypeScript, allowing the
* object to be iterated over using a for...of loop.
*/
protected* _getIterator(): IterableIterator<E> {
protected *_getIterator(): IterableIterator<E> {
for (let i = 0; i < this.size; ++i) {
yield this.getAt(i);
}

View file

@ -345,7 +345,7 @@ export class Queue<E = any> extends IterableElementBase<E> {
* Space Complexity: O(n)
*/
protected* _getIterator(): IterableIterator<E> {
protected *_getIterator(): IterableIterator<E> {
for (const item of this.elements) {
yield item;
}

View file

@ -229,7 +229,7 @@ export class Stack<E = any> extends IterableElementBase<E> {
* Custom iterator for the Stack class.
* @returns An iterator object.
*/
protected* _getIterator(): IterableIterator<E> {
protected *_getIterator(): IterableIterator<E> {
for (let i = 0; i < this.elements.length; i++) {
yield this.elements[i];
}

View file

@ -410,7 +410,7 @@ export class Trie extends IterableElementBase<string> {
return newTrie;
}
protected* _getIterator(): IterableIterator<string> {
protected *_getIterator(): IterableIterator<string> {
function* _dfs(node: TrieNode, path: string): IterableIterator<string> {
if (node.isEnd) {
yield path;

View file

@ -2,12 +2,12 @@ export type VertexKey = string | number;
export type DijkstraResult<V> =
| {
distMap: Map<V, number>;
distPaths?: Map<V, V[]>;
preMap: Map<V, V | undefined>;
seen: Set<V>;
paths: V[][];
minDist: number;
minPath: V[];
}
distMap: Map<V, number>;
distPaths?: Map<V, V[]>;
preMap: Map<V, V | undefined>;
seen: Set<V>;
paths: V[][];
minDist: number;
minPath: V[];
}
| undefined;

View file

@ -10,8 +10,9 @@ export type LinkedHashMapOptions<K> = {
objHashFn?: (key: K) => object;
};
export type HashMapOptions<K> = {
export type HashMapOptions<K, V, T> = {
hashFn?: (key: K) => string;
toEntryFn?: (rawElement: T) => [K, V];
};
export type HashMapStoreItem<K, V> = { key: K; value: V };

View file

@ -85,8 +85,7 @@ class MyGraph<
describe('AbstractGraph Operation Test', () => {
const myGraph: MyGraph<number, string> = new MyGraph<number, string>();
beforeEach(() => {
});
beforeEach(() => {});
it('should edge cases', function () {
myGraph.addVertex('A', 1);
myGraph.addVertex('B', 2);

View file

@ -128,6 +128,20 @@ describe('HashMap Test2', () => {
expect(eHM.get('one')).toBe(1);
});
test('should raw elements toEntry', () => {
const rawCollection = [
{ id: 1, name: 'item 1' },
{ id: 2, name: 'item 2' }
];
const hm = new HashMap<number, string, { id: number; name: string }>(rawCollection, {
toEntryFn: rawElement => [rawElement.id, rawElement.name]
});
expect(hm.has(1)).toBe(true);
expect(hm.get(2)).toBe('item 2');
expect(hm.size).toBe(2);
});
it('should update the value for an existing key', () => {
hashMap.set('key1', 'value1');
hashMap.set('key1', 'newValue');

View file

@ -114,8 +114,7 @@ describe('Deque - Complex Operations', () => {
expect([...deque]).toEqual([1, 2, 3]);
});
test('shrinkToFit should reduce the memory footprint', () => {
});
test('shrinkToFit should reduce the memory footprint', () => {});
});
describe('Deque - Utility Operations', () => {
let deque: Deque<number>;