esds
Version:
ES6 JS lightweight data structures (Priority Queue, Binary Search Tree (BST), Graph, Bloom Filters, Trie, Queue, Stack, Linked-List)
551 lines (435 loc) β’ 14.3 kB
Markdown
[![Contributors][contributors-shield]][contributors-url]
[![Downloads][downloads-shield]][downloads-url]
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![MIT License][license-shield]][license-url]
<!-- PROJECT LOGO -->
<br />
<p align="center">
<h3 align="center">ESDS</h3>
<p align="center">
ES Javascript Data Structures (Priority Queue, Binary Search Tree (BST), Graph, Trie, Bloom Filter, Queue, Stack, Linked List)
<br />
<a href="https://jayalbo.github.io/ESDS/"><strong>Explore the docs Β»</strong></a>
<br />
<br />
<a href="https://github.com/jayalbo/ESDS/issues">Report Bug π</a>
Β·
<a href="https://github.com/jayalbo/ESDS/discussions">Request Feature π³</a>
</p>
</p>
<!-- TABLE OF CONTENTS -->
<details open="open">
<summary>Table of Contents</summary>
<ol>
<li>
<a href="#getting-started">Getting Started π¦ </a>
<ul>
<li><a href="#installation">Installation πΎ</a></li>
</ul>
</li>
<li><a href="#examples">Usage π</a>
<ul>
<li><a href="#examples">Examples π</a>
<ul>
<li><a href="#priority-queue">Priority Queue π</a></li>
<li><a href="#binary-search-tree">Binary Search Tree (BST) π</a></li>
<li><a href="#graph">Graph α¨</a>
<ul>
<li><a href="#undirected">Undirected β</a></li>
<li><a href="#directed">Directed β</a></li>
</ul>
</li>
<li><a href="#trie">Trie π²</a></li>
<li><a href="#bloom-filter">Bloom Filter β</a></li>
<li><a href="#queue-stack">Queue π / Stack π₯</a></li>
<li><a href="#linked-list">Linked Listπ</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#roadmap">Roadmap π</a></li>
<li><a href="#contributing">Contributing πͺ</a></li>
<li><a href="#license">License π</a></li>
<li><a href="#contact">Contact π§</a></li>
</ol>
</details>
<!-- GETTING STARTED -->
## Getting Started
To get a local copy up and running follow these simple steps.
### Installation
1. Install NPM package
```sh
npm i esds
```
<!-- USAGE EXAMPLES -->
## Examples
### Priority Queue
```javascript
import { PriorityQueue } from "esds";
const minPQ = new PriorityQueue("min"); // min PQ (default if not provided)
const maxPQ = new PriorityQueue("max"); // max PQ
const arr = [20, 40, 30, 50, 15, 10, 5];
arr.forEach((element) => {
minPQ.add(element);
maxPQ.add(element);
});
console.log(minPQ.peek); // 5
console.log(maxPQ.peek); // 50
while (!minPQ.isEmpty) {
console.log(minPQ.poll());
}
/*
5
10
15
20
30
40
50
*/
while (!maxPQ.isEmpty) {
console.log(maxPQ.poll());
}
/*
50
40
30
20
15
10
5
*/
// Custom comparator
const comparator = (a, b) => a.age - b.age; // Using age (min)
const customPQ = new PriorityQueue("custom", comparator);
const Person = function (name, age, role) {
this.name = name;
this.age = age;
this.role = role;
};
customPQ.add(new Person("Jane Doe", 26, "Software Engineer"));
customPQ.add(new Person("John Doe", 28, "Cloud Engineer"));
customPQ.add(new Person("Ordinary Joe", 42, "QA"));
customPQ.add(new Person("Janie Doe", 20, "Support Engineer"));
customPQ.add(new Person("Fred Bloggs", 19, "Intern"));
// Contains function using object
console.log(customPQ.contains(new Person("Janie Doe", 20, "Support Engineer"))); // true
// Poll
while (!customPQ.isEmpty) {
console.log(customPQ.poll());
}
/*
{ name: 'Fred Bloggs', age: 19, role: 'Intern' }
{ name: 'Janie Doe', age: 20, role: 'Support Engineer' }
{ name: 'Jane Doe', age: 26, role: 'Software Engineer' }
{ name: 'John Doe', age: 28, role: 'Cloud Engineer' }
{ name: 'Ordinary Joe', age: 42, role: 'QA' }
*/
```
### Binary Search Tree
```Javascript
import { BST, List} from "esds";
const bst = new BST();
// Add Elements
bst.add(10, "Ten");
bst.add(1, "One");
bst.add(5, "Five");
bst.add(7, "Seven");
bst.add(2, "Two");
bst.add(0, "Zero");
bst.add(8, "Eight");
console.log(bst.toArray("in-order"));
// [ 0, 1, 2, 5, 7, 8, 10 ]
console.log(bst.toArray("pre-order"));
// [ 10, 1, 0, 5, 2, 7, 8 ]
console.log(bst.toArray("post-order"));
// [ 0, 2, 8, 7, 5, 1, 10 ]
console.log(bst.toArray("reverse-in-order"));
// [ 10, 8, 7, 5, 2, 1, 0 ]
// Get Element
console.log(bst.get(7));
/*
TreeElement {
key: 7,
value: 'Seven',
left: null,
right: TreeElement { key: 8, value: 'Eight', left: null, right: null }
}
*/
// Update Element
bst.update(8, "γ―γ‘");
console.log(bst.get(8).value); // γ―γ‘
// Has
console.log(bst.has(5)); // true
// Remove Element
bst.remove(5);
console.log(bst.has(5)); // false
// Handling duplicates (Using Linked-list)
if (bst.has(7)) { // true
const list = new List();
list.add(bst.get(7).value); // Added "Seven"
list.add(["Siete", "γγ‘", "δΈ", "Sieben"]);
bst.update(7, list);
}
console.log(bst.get(7).value.toArray());
// [ 'Seven', 'Siete', 'γγ‘', 'δΈ', 'Sieben' ]
// Remove Elements using an iterator in order to prevent a significant unbalanced BST
// JS Generator function
function* iterator(arr) {
let i = 0;
while (true) {
yield arr[i++];
if (i === arr.length) i = 0;
}
}
const removeType = iterator(["predecessor", "successor"]); // in-order predecessor, and in-order successor
[10, 1, 0, 5, 7].forEach((value) => bst.remove(value, removeType.next().value));
console.log(bst.toArray()); // Default: in-order
// [ 2, 8 ]
```
### Graph
#### Undirected
```Javascript
import { Graph } from "esds";
const graph = new Graph(); // Creates a new undirected graph
// Add nodes (Users)
graph.addNode(1, "Randall");
graph.addNode(2, "Mellisa");
graph.addNode(3, "Cecelia");
graph.addNode(4, "Velda");
graph.addNode(5, "Rossie");
//Add Edges (Friendships)
graph.addEdge(1, 2); // Randall - Mellisa
graph.addEdge(2, 5); // Mellisa - Rossie
graph.addEdge(3, 4); // Cecelia - Velda
graph.addEdge(4, 1); // Velda - Randall
graph.addEdge(5, 1); // Rossie - Randall
// Check if connection exists (Users are friends)
console.log(graph.nodesConnected(2, 3)); // Mellisa & Cecelia: Output: false
console.log(graph.nodesConnected(1, 5)); // Randall & Rossie: Output: true
// Check distance between two nodes (Users)
console.log(graph.getWeight(2, 3)); // Mellisa & Cecelia: Output: 3rd level friends
console.log(graph.getWeight(1, 5)); // Randall & Rossie: Output: 1st level friends (Users are friends)
// Get Path between nodes (Friendship relation between two users)
console.log(graph.getPath(2, 3).map((value) => graph.getNode(value.node)));
// [ 'Mellisa', 'Randall', 'Velda', 'Cecelia' ]
console.log(graph.getPath(1, 5).map((value) => graph.getNode(value.node)));
// [ 'Randall', 'Rossie' ]
```
#### Directed
```Javascript
import { Graph } from "esds";
const graph = new Graph(true); // Creates a new directed graph
// Add Nodes (Cities)
graph.addNode(1, "City Ξ");
graph.addNode(2, "City Ξ²");
graph.addNode(3, "City Ξ");
graph.addNode(4, "City Ξ");
graph.addNode(5, "City Ξ΅");
// Add Edges (Routes between cities (one-way))
graph.addEdge(1, 3, 75); // Alpha (Ξ) -> Gamma (Ξ), distance 75 miles
graph.addEdge(2, 5, 325); // Beta (Ξ²) -> Epsilon (Ξ΅), distance 325 miles
graph.addEdge(3, 1, 125); // Gamma (Ξ) -> Alpha (Ξ±), distance 125 miles
graph.addEdge(4, 2, 100); // Delta (Ξ) -> Beta (Ξ²), distance 100 miles
graph.addEdge(5, 1, 415); // Epsilon (Ξ΅) -> Alpha (Ξ), distance 415 miles
graph.addEdge(5, 3, 550); // Epsilon (Ξ΅) -> Gamma (Ξ), distance 550 miles
// Check if connection exists (Route between cities exists)
console.log(graph.nodesConnected(2, 3)); // Beta & Gamma: Output: false
console.log(graph.nodesConnected(5, 1)); // Epsilon & Alpha: Output: true
// Get Path between nodes (Route between two cities) by lowest number of intermediate nodes
let routeADistance = 0;
const routeA = graph.getPath(2, 3).map((value) => {
// Between Beta (Ξ²) & Gamma (Ξ)
routeADistance += value.weight;
return graph.getNode(value.node);
});
console.log(routeA, routeADistance);
/*
([Route] Total distance in miles)
[ 'City Ξ²', 'City Ξ΅', 'City Ξ' ] 875
*/
// Get Path between nodes (Route between two cities) by lowest distance (Dijkstra algorithm)
let routeB = graph.getPathWeighted(2, 3); // Between Beta (Ξ²) & Gamma (Ξ)
let routeBDistance = routeB.distance;
routeB = routeB.nodes.map((value) => graph.getNode(value));
console.log(routeB, routeBDistance);
/*
([Route] Total distance in miles)
[ 'City Ξ²', 'City Ξ΅', 'City Ξ', 'City Ξ' ] 815
*/
```
#### Use cases: Social graphs, recommendation engines, navigation, supply chain, etc.
#### Flight Routes Example: https://bit.ly/3ApNrDA

### Trie
```Javascript
import { Trie } from "esds";
const countries = [
"United States of America",
"Canada",
"Argentina",
"Japan",
"Italy",
"Germany",
"Brazil",
"Armenia",
"Aruba",
];
const trie = new Trie();
countries.forEach((element) => trie.add(element)); // Add countries to the Trie
// Check if element exists
console.log(trie.contains("Argentina")); // True
console.log(trie.contains("Spain")); // False
// Update content
trie.update("Argentina", "π¦π·");
trie.update("United States of America", "πΊπΈ");
// Get content
console.log(trie.get("Argentina")); // π¦π·
console.log(trie.get("United States of America")); // πΊπΈ
// Retrieve Sub-Trie (DFS)
const prefix = "Ar";
const result = trie.find(prefix);
const dfs = (word, node) => {
if (node.child?.size > 0) {
node.child.forEach((value) => {
dfs(`${word}${value.key}`, value);
});
}
if (node.end) console.log(`${word}`);
/*
Argentina
Armenia
Aruba
*/
};
dfs(prefix, result);
// toArray()
console.log(trie.toArray());
/*
['United States of America','Canada', 'Argentina', 'Armenia',
'Aruba', 'Japan', 'Italy', 'Germany','Brazil']
*/
```
#### Use cases: Auto-complete, string search, word suggestion, prefix, IP routing, etc.
#### Word Prediction Example: https://bit.ly/37hYhPL

### Bloom Filter
#### A Bloom filter is a space-efficient probabilistic data structure that is used to test whether an element is a member of a set. More Info: https://bit.ly/3D9RmH8
```Javascript
import { BloomFilter } from "esds";
// Create BF
const BF = new BloomFilter();
// Add individual users
BF.add("john_89@email.com");
BF.add("jane2020@email.com");
BF.add("Joe@email.com");
BF.add("Doe.Jane@email.com");
BF.add("Jim.1990@email.com");
// Check individual users
console.log(BF.has("Doe.Jane@email.com")); // True (User may exists)
console.log(BF.has("Jimmy@email.com")); // False
// Create second BF
const BF2 = new BloomFilter(2048, 4); // Size 2048, Num. Hash rounds 4
const users = [
"almost",
"dopey",
"eritrean",
"struggle",
"hospitable",
"factor",
"quail",
];
// Add multiple users
BF2.add(users);
// Check individual users
console.log(BF2.has("struggle")); // True (User may exists)
console.log(BF2.has("house")); // False
// Check multiple values
console.log(
BF2.has(["hospitable", "monitor", "dopey", "factor", "fan", "quail"])
);
// [ true, false, true, true, false, true ]
```
#### Bloom Filters use cases:
- Value lookup when false positives are acceptable
- Avoid expensive lookup operations against DB
- Content filtering
### Queue-Stack
```JavaScript
import {Queue, Stack} from 'esds';
const queue = new Queue();
const stack = new Stack();
queue.add([1,2,3]);
queue.add(4);
queue.add(5);
stack.push([1,2]);
stack.add(3);
stack.add([4,5]);
while (!queue.isEmpty){
console.log(queue.poll()); // 1, 2, 3, 4, 5
}
while (!stack.isEmpty){
console.log(stack.pop()); // 5, 4, 3, 2, 1
}
```
#### Linked List
```JavaScript
import {List} from 'esds';
const a = new List();
a.add([1, 2, 3, 4]);
console.log(a.toArray()); // [1, 2, 3, 4]
a.add(5);
a.add("Six");
a.add({value: 7, str: "Seven"});
console.log(a.get(6).val); // "Six"
let head = a.get();
while(head){
console.log(head.val);
head = head.next;
}
/*
1
2
3
4
5
"Six"
{value: 7, str: "Seven"}
*/
let b = a.subList(2, 5);
console.log(b.toArray()); // [2, 3, 4, 5]
```
<!-- ROADMAP -->
## Roadmap
See the [open issues](https://github.com/jayalbo/ESDS/issues) for a list of proposed features (and known issues).
<!-- CONTRIBUTING -->
## Contributing
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
<!-- LICENSE -->
## License
Distributed under the MIT License. See `LICENSE` for more information.
<!-- CONTACT -->
## Contact
Project Link: [https://github.com/jayalbo/ESDS](https://github.com/jayalbo/ESDS)
<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[contributors-shield]: https://img.shields.io/github/contributors/jayalbo/ESDS.svg?style=for-the-badge
[contributors-url]: https://github.com/jayalbo/ESDS/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/jayalbo/ESDS.svg?style=for-the-badge
[forks-url]: https://github.com/jayalbo/ESDS/network/members
[stars-shield]: https://img.shields.io/github/stars/jayalbo/ESDS.svg?style=for-the-badge
[stars-url]: https://github.com/jayalbo/ESDS/stargazers
[issues-shield]: https://img.shields.io/github/issues/jayalbo/ESDS.svg?style=for-the-badge
[issues-url]: https://github.com/jayalbo/ESDS/issues
[license-shield]: https://img.shields.io/github/license/jayalbo/ESDS.svg?style=for-the-badge
[license-url]: https://github.com/jayalbo/ESDS/blob/master/LICENSE.txt
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
[downloads-shield]: https://img.shields.io/npm/dw/esds?style=for-the-badge
[downloads-url]: https://www.npmjs.com/package/esds