Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# string-dsa [![Build Status](https://travis-ci.com/thsubaku9/string-dsa.svg?branch=main)](https://travis-ci.com/thsubaku9/string-dsa) [![Coverage Status](https://coveralls.io/repos/github/thsubaku9/string-dsa/badge.svg?branch=main)](https://coveralls.io/github/thsubaku9/string-dsa?branch=main)
# string-dsa [![Build Status](https://travis-ci.com/thsubaku9/string-dsa.svg?branch=main)](https://travis-ci.com/thsubaku9/string-dsa) [![Coverage Status](https://coveralls.io/repos/github/thsubaku9/string-dsa/badge.svg?branch=main)](https://coveralls.io/github/thsubaku9/string-dsa?branch=main) ![npm](https://img.shields.io/npm/dt/string-dsa?color=fddb3a) ![npm](https://img.shields.io/badge/Niche-Yep!-ff69b4)
> String oriented Data Structures and Algorithms library for JavaScript

## Installation
Expand All @@ -18,6 +18,7 @@ List of supported Data Structures and Algorithms are :
- [Edit/Levenshtein Distance](https://github.com/thsubaku9/string-dsa/blob/main/src/edit_distance.js)
- [Knuth Morris Pratt](https://github.com/thsubaku9/string-dsa/blob/main/src/search/kmp.js)
- [Rabin Karp](https://github.com/thsubaku9/string-dsa/blob/main/src/search/rabin_karp.js)
- [Trie](https://github.com/thsubaku9/string-dsa/blob/main/src/Trie.js)
## Utilization

To utilize in Node.Js:
Expand All @@ -26,6 +27,17 @@ To utilize in Node.Js:
const stringDSA = require('string-dsa');
```

To utilize in Vanilla Js:

```sh
# follow cloning steps
$ npm run prod
$ cp ./dist/string-dsa.js /YOUR/DIRECTORY/FILENAME.js
```

```html
<script src="/YOUR/DIRECTORY/FILENAME.js"></script>
```
## Development Steps

Cloning:
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "string-dsa",
"version": "0.3.1",
"description": "String Data Structures and Algorithm Library in JavaScript",
"version": "1.0.0",
"description": "String Data Structures and Algorithms Library in JavaScript",
"main": "src/index.js",
"files": [
"dist",
"src"
],
"scripts": {
"coverage": "nyc mocha --recursive",
"coverage": "nyc mocha --recursive",
"dev": "webpack -d",
"test": "mocha --recursive",
"test:coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
Expand All @@ -23,7 +23,8 @@
"String Data Structures",
"Bloom Filter",
"Custom Sort",
"Levenshtein Distance"
"Levenshtein Distance",
"Trie"
],
"author": "Kaustubh J <kaustubhkj@gmail.com>",
"license": "ISC",
Expand Down
140 changes: 140 additions & 0 deletions src/Trie.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
"use strict";

/**Class implementation of Trie */
class Trie{
/**
* Instantiate your Trie class
*/
constructor(){
this._rootNode = new Object();
this._rootNode["$"] = false;
}

/**
*
* @param {String} searchTerm search term to be inserted into the trie
*/
insertSingle(searchTerm){
//use the root node, if current element isn't there add it; go down the node and iterate over the word
let currNode = this._rootNode;
for (let j=0; j<searchTerm.length; j++){
if(currNode[searchTerm.charAt(j)] == undefined){
let newNode = new Object();
newNode["$"] = false;
currNode[searchTerm.charAt(j)] = newNode;
}
currNode = currNode[searchTerm.charAt(j)]
}
currNode["$"] = true;
}

/**
*
* @param {Array} searchList list of search terms to be inserted into the trie
*/
insertList(searchList){
for(let i=0; i<searchList.length; i++){
this.insertSingle(searchList[i]);
}
}

/**
*
* @param {String} searchTerm the search term to be removed from the given trie
*
* @return {boolean} states whether all nodes related to search term were removed
*/
removeSingle(searchTerm){
this._removeSingle(searchTerm,this._rootNode,0);
}

_hasChildren(currentNode){
let thisNodeChildren = 0;
for (let x in currentNode){
if (x == "$") thisNodeChildren += currentNode[x];
else if(currentNode[x] != undefined) thisNodeChildren +=1;

}
return thisNodeChildren;
}

_removeSingle(searchTerm,currentNode,currentIter){
//recursively dive deep; at reaching depth check if any more elements branch out from this node, if they do return false, else delete the node and return true. recursive step out
if(currentIter<searchTerm.length){
if( currentNode[searchTerm[currentIter]] != undefined){
let canRemove = this._removeSingle(searchTerm,currentNode[searchTerm[currentIter]],currentIter +1)
if(canRemove){
currentNode[searchTerm[currentIter]] = undefined;
return this._hasChildren(currentNode) == 0;
}
}
} else if(currentIter == searchTerm.length) {
currentNode["$"] = false
if( this._hasChildren(currentNode) == 0) {
return true;
}
}
return false;
}

/**
*
* @param {Array} searchList pass a List of search terms to be removed from the Trie
*/
removeList(searchList){
for( let i=0; i<searchList.length; i++){
this.removeSingle(searchList[i]);
}
}

/**
*
* @param {String} searchSpace the text to be searched over
*
* @returns {Array[]} where each index gives a start position and end position of the term
*/
find(searchSpace){
//iterate over all elements and match according to the trie created
let returnArray = [];
for(let i=0;i<searchSpace.length;i++){
let currentNode = this._rootNode
for(let j=i;j<searchSpace.length;j++){
if( currentNode[searchSpace[j]] != undefined ){
currentNode = currentNode[searchSpace[j]]
} else {
break;
}

if(currentNode["$"] == true){
let position = []
position.push(i)
position.push(j)
returnArray.push(position)
}
}
}

return returnArray
}


/**
* @returns {String[]} returns the array of all inserted elements
*/
listAllElements(){
let elementList = []
this._listFromNode(this._rootNode,elementList,"");
return elementList;
}

_listFromNode(node,elementList,stringSeq){
if (node["$"] == true){
elementList.push(stringSeq);
}
for (let x in node){
if(x != "$" && (node[x] != undefined)) this._listFromNode(node[x],elementList,stringSeq + x);
}
}
}

module.exports = Trie;
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const diceCoeff = require('./dice_coeff');
const editDist = require('./edit_distance');
const kmp = require('./search').kmp;
const rabinKarp = require('./search').rabinKarp;
const Trie = require('./Trie');


module.exports = {
Expand All @@ -14,5 +15,6 @@ module.exports = {
diceCoeff,
editDist,
kmp,
rabinKarp
rabinKarp,
Trie
};
27 changes: 27 additions & 0 deletions test/test_Trie.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const assert = require('assert');
const Trie = require('../src').Trie;

describe("Trie based tests",() =>{

const myT = new Trie();
const st1 = "can"
const st2 = "candy"
const st3 = "ban"
const searchSpace = "can the real candy ban?"
const resultPos = [ [ 0, 2 ], [ 13, 15 ], [ 13, 17 ], [ 19, 21 ] ]
it("should insert a bunch of keys",() => {
myT.insertList([st1,st2,st3])
assert.deepStrictEqual(myT.listAllElements(),[st1,st2,st3])
});

it("should find correct locations of searchSpace",() => {
const res = myT.find(searchSpace)
assert.deepStrictEqual(res,resultPos)
});

it("remove given keys", () => {
myT.removeList([st2,st3])
assert.deepStrictEqual(myT.listAllElements(),[st1]);
myT.insertList([st2,st3])
})
})