npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

util.ds

v0.0.61

Published

Simple data structures

Downloads

100

Readme

util.ds

Simple data structures

build analysis code style: prettier testing NPM

This module contains my implementation of a few basic data structures using Typescript. I could have used other implementations, but what fun is that.

It contains the following data structures:

Class Diagram

(image created with Lucidchart)

Installation

This module uses yarn to manage dependencies and run scripts for development.

To install as an application dependency:

$ yarn add --dev util.ds

To build the app and run all tests:

$ yarn run all

Usage

BinaryTree

A binary search tree implemented using the Red/Black algorithm from Introduction to Algorithms - Cormen 3rd ed..

import {BinaryTree} from 'util.ds';

const bt = new BinaryTree<string>(['a', 'c', 'g']);

bt.insert('d');
bt.insert('k');

//     C
//    / \
//   A   G
//      / \
//     D   K

// bt.length === 5
// bt.first === 'a'
// bt.last === 'k'
// bt.height === 3

console.log(bt.contains('d'));

// true

console.log(bt.contains('z'));

// false

console.log(bt.breadthSearch('d'));

// true

console.log(bt.inorder);

// ['a', 'c', 'd', 'g', 'k']

console.log(bt.preorder);

// ['c', 'a', 'g', 'd', 'k']

console.log(bt.postorder);

// ['a', 'd', 'k', 'g', 'c']

console.log(bt.breadth);

// ['c', 'a', 'g', 'd', 'k']

for (const val of bt) {
    console.log(`${val} `);
}

// inorder, iterator though each node in the tree
// a c d g k

console.log(bt.find('g'));

// g

bt.delete('g');

//     C
//    / \
//   A   K
//      /
//     D

bt.delete('c');

//     D
//    / \
//   A   K

bt.clear();

// bt.length === 0
// bt.first === nil
// bt.last === nil
// bt.height === 0

The output above demonstrates all of the basic operations used to interact with the tree. When an element is added to the tree an insert event fires. When an element is deleted from the tree a remove event fires.

GeneralTree

A general tree is an implementation where each parent can have an arbitrany number of child nodes wtihin it (including the root node).

import {Comparator, GeneralTree, GeneralTreeItem} from 'util.ds';

interface TestData {
    field1? string;
}

const comparator: Comparator<TestData> = (
	o1: TestData,
	o2: TestData
): number => {
	if (o1.field1 === o2.field1) {
		return 0;
	}

	return -1;
};

const gt = new GeneralTree<TestData>({}, comparator);

gt.insert({id: 0, field1: "f1"});
gt.insert({id: 1, field1: "f2"});
gt.insert({id: 2, field1: "f3", parentId: 0});
gt.insert({id: 3, field1: "f4", parentId: 0});
gt.insert({id: 4, field1: "f5", parentId: 0});
gt.insert({id: 5, field1: "f6"});


//        f1, f2, f6     # root
//       / | \
//     f3 f4  f5         # children of f1

// gt.length === 6
// gt.first === {id: 0, field: "f1"}
// gt.last === {id: 5, field: "f6"}
// gt.height === 2

The methods above would create a simple general tree that holds an object of type TestData. The root of the three contains three nodes "f1, f2, f6". At each level of the tree an arbitrary number of children can be inserted into that parent node. In the example above the "f1" parent node contains three children "f3,f4,f5". Also note that the id fields are explicityly set for each node on insert. These can be omitted. When not given, then the tree will generate a UUID for each node. There are no balancing operations for this tree. The tree can be searched using the following methods:


console.log(gt.contains({field1: "f1"}));

// true

console.log(gt.contains({field1: "foo"}));

// false

console.log(gt.find(2));

// {id: 2, field1: "f3"}

console.log(gt.findByParent(0))

// [{field1: "f3"}, {field1: "f4}, {field1: "f5"}]
//
// note that the objects in the array above do NOT show all fields returned.  These objects contain a reference
// to their parent and associated id values.

console.log(gt.findByField({field1: "f3"}));

// [{field1: "f3", id: 2, parentId: 0}]

Nodes can be removed from the tree by their id value:


gt.remove(0, true);

// attempts to remove "f1", but this flag will prevent removal of nodes with children

gt.remove(0);

// removes "f1" and all of its child nodes

To iterate through the whole tree use the walk function. It will perform an inorder traversal of the nodes in the tree and execute a callback method as it touches each node in the tree:


gt.walk((it: GeneralTreeItem) => {
    // do something with node saved in "it"
});

Deque

A deque is a double ended queue. An element can be inserted at either end of the queue.

import {Deque} from 'util.ds';

const q = new Deque<number>();

q.enqueue(1);
let val = q.dequeue();

This example works like a typical queue. However this type allows one to add items at either end.

When an element is added to the deque an insert event fires. When an element is removed from the deque a remove event fires.

The deque can have items added to either end:

import {Deque} from 'util.ds';

const q = new Deque<number>();

q.pushFront(1);
q.pushBack(2);

let val = q.popFront();
val = q.popBack();

A deque can also be limited in size:

import {Deque} from 'util.ds';

const q = new Deque<number>(5);
for (let i: number = 0; i < 5; i++) {
	q.enqueue(i);
}

q.enqueue(6);

This will add 5 items to the queue. The last item adds a 6th element. That will cause the first item in the queue to be removed automatically before the new item is enqueued. This provides a way to "age" items within the queue. When the item is removed a remove event fires. The front item in the queue is considered the oldest item.

List

A doubly linked list structure. To create a list and insert elements use:

import {List} from 'util.ds';

const list = new List<string>();

list.insert('a');
list.insert('b');
list.insert('c');

// list.size === 3
// list.front === 'a'
// list.back === 'c'

console.log(list.array);

// ['a', 'b', 'c']

for (const it of list) {
    console.log(it);
}

// a
// b
// c

console.log(list.reverse);

// ['c', 'b', 'a']

list.remove('b');
console.log(list.array);

// ['a', 'c']

console.log(list.contains('a'));

// true

console.log(list.contains('b'));

// false

When an element is added to the list an insert event fires. When an element is removed from the list a remove event fires.

PriorityQueue

Uses a Red/Black binary tree to implement a priority queue. Items are inserted into a queue structure with an associatd priority number. The lower the number, the higher the priority. Items with a higher priority are chosen first in the queue. Priority values are numbers > 0. If a negative number is given, then it will be given a priority of 0.

To create a priority queue use:

import {PriorityQueue} from 'util.ds';

const pq = new PriorityQueue<string>();

pq.enqueue('a', 100);
pq.enqueue('b', 10);
pq.enqueue('c', 50);

console.log(pq.size);

// 3

console.log(pq.dequeue());

// 'b'

console.log(pq.dequeue());

// 'c'

console.log(pq.dequeue());

// 'a'

When an element is added to the queue an insert event fires. When an element is removed from the queue a remove event fires.

Queue

To create a simple queue use:

import {Queue} from 'util.ds';

const q = new Queue<number>();

q.enqueue(1);
let val = q.dequeue();

When an element is added to the queue an insert event fires. When an element is removed from the queue a remove event fires.

A queue can also be drained. This will dequeue all items from the list and place them into an array which is returned to the caller.

import {Queue} from 'util.ds';

const q = new Queue<number>([1, 2, 3, 4, 5]);

// q.size === 5

console.log(q.dequeue());

// 1

console.log(q.drain());

// [2, 3, 4, 5]
//
// q.size === 0

When the queue is drained a drain event is emitted.

SortedList

The SortedList is similar to the List structure above. The difference is that the insert operation will place the value its ascending order location. It presents all of the same properties and methods.

import {SortedList} from 'util.ds';

const list = new SortedList<string>();

list.insert('b');
list.insert('a');
list.insert('c');

console.log(list.array);

// ['a', 'b', 'c']

When an element is added to the list an insert event fires. When an element is removed from the list a remove event fires.

Stack

To create a simple stack use:

import {Stack} from 'util.ds';

const stack = new Stack<number>();

stack.push(1);
stack.push(2);
stack.push(3);

console.log(stack.top());

// 3

console.log(stack.pop());

// 3

When an element is added to the stack an insert event fires. When an element is removed from the stack a remove event fires.

Comparators

These data structures work with primitive data types by default. Each object can also accept a user defined data type. This requires a Comparator object to define how two user defined objects can be compared.

import {BinaryTree, Comparator} from 'util.ds'

interface TestData {
	key: string;
	data?: number;
}

const fn: Comparator<TestData> = (o1: TestData, o2: TestData): number => {
	if (o1.key === o2.key) {
		return 0;
	} else if (o1.key > o2.key) {
		return 1;
	}

	return -1;
};

const bt = new BinaryTree<TestData>(null, fn);

bt.insert({key: 'g', data: 1});
bt.insert({key: 'c', data: 2});
bt.insert({key: 'a', data: 3});
bt.insert({key: 'd', data: 4});
bt.insert({key: 'k', data: 5});

console.log(bt.find({key: 'g'}).data);

// 1