├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── browserify-umd.js ├── examples ├── CollectionsDemo │ ├── CollectionsDemo.csproj │ ├── Content │ │ ├── bootstrap-responsive.min.css │ │ ├── bootstrap.min.css │ │ ├── images │ │ │ ├── glyphicons-halflings-white.png │ │ │ └── glyphicons-halflings.png │ │ └── prettify.css │ ├── Scripts │ │ ├── bootstrap.min.js │ │ ├── jquery-1.9.1.min.js │ │ ├── lang-basic.js │ │ ├── prettify.js │ │ └── run_prettify.js │ ├── app.css │ ├── app.js │ ├── app.ts │ ├── index.htm │ ├── packages.config │ ├── web.Debug.config │ ├── web.Release.config │ └── web.config └── SimpleDemo │ └── index.ts ├── minify-umd.js ├── package-lock.json ├── package.json ├── src ├── lib │ ├── BSTree.ts │ ├── BSTreeKV.ts │ ├── Bag.ts │ ├── Dictionary.ts │ ├── FactoryDictionary.ts │ ├── Heap.ts │ ├── LinkedDictionary.ts │ ├── LinkedList.ts │ ├── MultiDictionary.ts │ ├── MultiRootTree.ts │ ├── PriorityQueue.ts │ ├── Queue.ts │ ├── Set.ts │ ├── Stack.ts │ ├── arrays.ts │ ├── index.ts │ └── util.ts └── test │ ├── arraysTest.ts │ ├── bagTest.ts │ ├── bsTreeKVTest.ts │ ├── bsTreeTest.ts │ ├── dictionaryTest.ts │ ├── factoryDictionaryTest.ts │ ├── heapTest.ts │ ├── linkedListTest.ts │ ├── multiDictionaryTest.ts │ ├── multiRootTreeTest.ts │ ├── priorityQueueTest.ts │ ├── queueTest.ts │ ├── runTests.html │ ├── setTest.ts │ ├── stackTest.ts │ └── utilTest.ts ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Compile time directories 2 | node_modules 3 | /temp 4 | /coverage 5 | /dist 6 | /typings 7 | 8 | # Editor configuration 9 | .vscode 10 | .settings 11 | .idea 12 | 13 | # Logs and compressed files 14 | *.log 15 | *.tgz 16 | 17 | # Demos 18 | collections.js.map 19 | demo/CollectionsDemo/app.js.map 20 | demo/packages/ 21 | demo/CollectionsDemo/obj/Debug/ 22 | lessons.txt 23 | demo/CollectionsDemo/collections.js 24 | 25 | # Windows image file caches 26 | Thumbs.db 27 | ehthumbs.db 28 | 29 | # Folder config file 30 | Desktop.ini 31 | 32 | # Recycle Bin used on file shares 33 | $RECYCLE.BIN/ 34 | 35 | # Windows Installer files 36 | *.cab 37 | *.msi 38 | *.msm 39 | *.msp 40 | 41 | # Windows shortcuts 42 | *.lnk 43 | 44 | # ========================= 45 | # Operating System Files 46 | # ========================= 47 | 48 | # OSX 49 | # ========================= 50 | 51 | .DS_Store 52 | .AppleDouble 53 | .LSOverride 54 | 55 | # Thumbnails 56 | ._* 57 | 58 | # Files that might appear on external disk 59 | .Spotlight-V100 60 | .Trashes 61 | 62 | # Directories potentially created on remote AFP share 63 | .AppleDB 64 | .AppleDesktop 65 | Network Trash Folder 66 | Temporary Items 67 | .apdisk 68 | 69 | # Logs 70 | logs 71 | *.log 72 | npm-debug.log* 73 | 74 | # Runtime data 75 | pids 76 | *.pid 77 | *.seed 78 | 79 | # Directory for instrumented libs generated by jscoverage/JSCover 80 | lib-cov 81 | 82 | # Coverage directory used by tools like istanbul 83 | coverage 84 | 85 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 86 | .grunt 87 | 88 | # node-waf configuration 89 | .lock-wscript 90 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .settings/ 2 | .vscode/ 3 | coverage/ 4 | dist/test/ 5 | examples/ 6 | node_modules/ 7 | src/ 8 | temp/ 9 | typings/ 10 | 11 | .gitattributes 12 | .gitignore 13 | .editorconfig 14 | browserify-karma.js 15 | browserify-umd.js 16 | DEVELOPING.md 17 | karma.conf.js 18 | minify-umd.js 19 | tsconfig.json 20 | tsd.json 21 | tslint.json 22 | *.tgz -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2010-2017 Tomasz Ciborski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [TypeScript Collections](https://github.com/basarat/typescript-collections/) 2 | ==================== 3 | 4 | It is a complete, fully tested data structure library written in TypeScript. 5 | 6 | This project uses TypeScript Generics so you need TS 0.9 and above. 7 | 8 | [This projects supports UMD (Universal Module Definition)](https://github.com/umdjs/umd) 9 | 10 | [![NPM](https://nodei.co/npm-dl/typescript-collections.png?height=3)](https://nodei.co/npm/typescript-collections/) 11 | 12 | Included data structures 13 | --------------------- 14 | 15 | - Linked List 16 | - Dictionary - [Example](#a-sample-on-dictionary) 17 | - Multi Dictionary 18 | - Linked Dictionary 19 | - Default Dictionary - [Info](#default-dictionary) 20 | - Binary Search Tree 21 | - Binary Search Tree for Key-Value pairs 22 | - Stack 23 | - Queue 24 | - Set - [Example](#example) 25 | - Bag 26 | - Binary Heap 27 | - Priority Queue 28 | 29 | It also includes several functions for manipulating arrays. 30 | 31 | Usage 32 | -------------------- 33 | 34 | `npm install typescript-collections --save` 35 | 36 | ES6 `import ... from` 37 | 38 | ```typescript 39 | import * as Collections from 'typescript-collections'; 40 | ``` 41 | 42 | or TypeScript `import ... require` 43 | 44 | ```typescript 45 | import Collections = require('typescript-collections'); 46 | ``` 47 | 48 | or JavaScript `var ... require` 49 | 50 | ```js 51 | var Collections = require('typescript-collections'); 52 | ``` 53 | 54 | ![](https://zippy.gfycat.com/SeriousPointlessCob.gif) 55 | 56 | Visual Studio or other TypeScript IDE, will provide you with complete Intellisense (autocomplete) for your types. 57 | The compiler will ensure that the collections contain the correct elements. 58 | 59 | A sample Visual Studio project is in the demo folder. 60 | 61 | Also available on NuGet : 62 | Thanks to 63 | 64 | Example 65 | -------------------- 66 | 67 | ```typescript 68 | import * as Collections from 'typescript-collections'; 69 | 70 | var mySet = new Collections.Set(); 71 | mySet.add(123); 72 | mySet.add(123); // Duplicates not allowed in a set 73 | // The following will give error due to wrong type: 74 | // mySet.add("asdf"); // Can only add numbers since that is the type argument. 75 | 76 | var myQueue = new Collections.Queue(); 77 | myQueue.enqueue(1); 78 | myQueue.enqueue(2); 79 | 80 | console.log(myQueue.dequeue()); // prints 1 81 | console.log(myQueue.dequeue()); // prints 2 82 | ``` 83 | 84 | Typings resolution 85 | ------------------- 86 | 87 | Remember to set `"moduleResolution": "node"`, so TypeScript compiler can resolve typings in the `node_modules/typescript-collections` directory. 88 | 89 | In browser usage 90 | ------------------- 91 | 92 | You should include `umd.js` or `umd.min.js` from `dist/lib/` directory. 93 | 94 | ```html 95 | 96 | ``` 97 | 98 | A note on Equality 99 | ------------------- 100 | 101 | Equality is important for hashing (e.g. dictionary / sets). Javascript only allows strings to be keys for the base dictionary {}. 102 | This is why the implementation for these data structures uses the item's toString() method. 103 | 104 | makeString utility function (aka. JSON.stringify) 105 | ------------------- 106 | 107 | A simple function is provided for you when you need a quick toString that uses all properties. E.g: 108 | 109 | ```typescript 110 | import * as Collections from 'typescript-collections'; 111 | 112 | class Car { 113 | constructor(public company: string, public type: string, public year: number) { 114 | } 115 | toString() { 116 | // Short hand. Adds each own property 117 | return Collections.util.makeString(this); 118 | } 119 | } 120 | 121 | console.log(new Car("BMW", "A", 2016).toString()); 122 | ``` 123 | 124 | Output: 125 | 126 | ```text 127 | {company:BMW,type:A,year:2016} 128 | ``` 129 | 130 | A Sample on Dictionary 131 | --------------------- 132 | 133 | ```typescript 134 | import * as Collections from 'typescript-collections'; 135 | 136 | class Person { 137 | constructor(public name: string, public yearOfBirth: number,public city?:string) { 138 | } 139 | toString() { 140 | return this.name + "-" + this.yearOfBirth; // City is not a part of the key. 141 | } 142 | } 143 | 144 | class Car { 145 | constructor(public company: string, public type: string, public year: number) { 146 | } 147 | toString() { 148 | // Short hand. Adds each own property 149 | return Collections.util.makeString(this); 150 | } 151 | } 152 | var dict = new Collections.Dictionary(); 153 | dict.setValue(new Person("john", 1970,"melbourne"), new Car("honda", "city", 2002)); 154 | dict.setValue(new Person("gavin", 1984), new Car("ferrari", "F50", 2006)); 155 | console.log("Orig"); 156 | console.log(dict); 157 | 158 | // Changes the same john, since city is not part of key 159 | dict.setValue(new Person("john", 1970, "sydney"), new Car("honda", "accord", 2006)); 160 | // Add a new john 161 | dict.setValue(new Person("john", 1971), new Car("nissan", "micra", 2010)); 162 | console.log("Updated"); 163 | console.log(dict); 164 | 165 | // Showing getting / setting a single car: 166 | console.log("Single Item"); 167 | var person = new Person("john", 1970); 168 | console.log("-Person:"); 169 | console.log(person); 170 | 171 | var car = dict.getValue(person); 172 | console.log("-Car:"); 173 | console.log(car.toString()); 174 | ``` 175 | 176 | Output: 177 | 178 | ```text 179 | Orig 180 | { 181 | john-1970 : {company:honda,type:city,year:2002} 182 | gavin-1984 : {company:ferrari,type:F50,year:2006} 183 | } 184 | Updated 185 | { 186 | john-1970 : {company:honda,type:accord,year:2006} 187 | gavin-1984 : {company:ferrari,type:F50,year:2006} 188 | john-1971 : {company:nissan,type:micra,year:2010} 189 | } 190 | Single Item 191 | -Person: 192 | john-1970 193 | -Car: 194 | {company:honda,type:accord,year:2006} 195 | ``` 196 | 197 | Default Dictionary 198 | --------------------- 199 | 200 | Also known as `Factory Dictionary` [[ref.](https://github.com/basarat/typescript-collections/pull/47)] 201 | 202 | If a key doesn't exist, the Default Dictionary automatically creates it with `setDefault(defaultValue)`. 203 | 204 | Default Dictionary is a @michaelneu contribution which copies Python's [defaultDict](https://docs.python.org/2/library/collections.html#collections.defaultdict). 205 | 206 | Development and contributions 207 | -------------------- 208 | 209 | Compile, test and check coverage 210 | `npm run all` 211 | 212 | Supported platforms 213 | -------------------- 214 | 215 | - Every desktop and mobile browser (including IE6) 216 | - Node.js 217 | 218 | ```text 219 | If it supports JavaScript, it probably supports this library. 220 | ``` 221 | 222 | Contact 223 | -------------------- 224 | 225 | bas AT basarat.com 226 | 227 | Project is based on the excellent original javascript version called [buckets](https://github.com/mauriciosantos/buckets) 228 | -------------------------------------------------------------------------------- /browserify-umd.js: -------------------------------------------------------------------------------- 1 | "use strict;" 2 | 3 | var browserify = require("browserify"); 4 | var fs = require("fs"); 5 | var glob = require("glob"); 6 | var mkdirp = require("mkdirp"); 7 | var Umd = require("browserify-umdify"); 8 | var util = require("util"); 9 | 10 | mkdirp.sync("./temp"); 11 | 12 | var packageJson = require("./package.json"); 13 | var distOutFileUnversioned = "./dist/lib/umd.js"; 14 | var distOutUnversioned = fs.createWriteStream(distOutFileUnversioned, { encoding: "utf-8", flags: "w"}) 15 | 16 | var bundled = browserify({ 17 | extensions: [".js", ".json"], 18 | debug: true 19 | }) 20 | .require("./dist/lib/index.js", { expose: "typescript-collections" }) 21 | .bundle() 22 | .pipe(new Umd()); 23 | 24 | bundled.pipe(distOutUnversioned); 25 | -------------------------------------------------------------------------------- /examples/CollectionsDemo/CollectionsDemo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | {7CD2B711-89CB-4495-994C-7CF98D3C5025} 6 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 7 | Library 8 | bin 9 | v4.5 10 | full 11 | true 12 | true 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | collections.js 21 | collections.ts 22 | 23 | 24 | collections.ts 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | app.ts 34 | 35 | 36 | 37 | collections.js.map 38 | collections.ts 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | web.config 49 | 50 | 51 | web.config 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | app.ts 60 | 61 | 62 | 63 | 10.0 64 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 65 | 66 | 67 | CollectionsDemo 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | True 76 | True 77 | 0 78 | / 79 | http://localhost:3657/ 80 | False 81 | False 82 | 83 | 84 | False 85 | 86 | 87 | 88 | 89 | 90 | ES3 91 | true 92 | true 93 | AMD 94 | 95 | 96 | ES3 97 | false 98 | false 99 | AMD 100 | 101 | 102 | 103 | copy "$(ProjectDir)..\\..\\collections.js" "$(ProjectDir)\\collections.js" /y 104 | 105 | -------------------------------------------------------------------------------- /examples/CollectionsDemo/Content/images/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basarat/typescript-collections/309bb1b6955b403b212309531607b8d17df152e5/examples/CollectionsDemo/Content/images/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /examples/CollectionsDemo/Content/images/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basarat/typescript-collections/309bb1b6955b403b212309531607b8d17df152e5/examples/CollectionsDemo/Content/images/glyphicons-halflings.png -------------------------------------------------------------------------------- /examples/CollectionsDemo/Content/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} -------------------------------------------------------------------------------- /examples/CollectionsDemo/Scripts/lang-basic.js: -------------------------------------------------------------------------------- 1 | var a=null; 2 | PR.registerLangHandler(PR.createSimpleLexer([["str",/^"(?:[^\n\r"\\]|\\.)*(?:"|$)/,a,'"'],["pln",/^\s+/,a," \r\n\t\u00a0"]],[["com",/^REM[^\n\r]*/,a],["kwd",/^\b(?:AND|CLOSE|CLR|CMD|CONT|DATA|DEF ?FN|DIM|END|FOR|GET|GOSUB|GOTO|IF|INPUT|LET|LIST|LOAD|NEW|NEXT|NOT|ON|OPEN|OR|POKE|PRINT|READ|RESTORE|RETURN|RUN|SAVE|STEP|STOP|SYS|THEN|TO|VERIFY|WAIT)\b/,a],["pln",/^[a-z][^\W_]?(?:\$|%)?/i,a],["lit",/^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?/i,a,"0123456789"],["pun", 3 | /^.[^\s\w"$%.]*/,a]]),["basic","cbm"]); 4 | -------------------------------------------------------------------------------- /examples/CollectionsDemo/Scripts/prettify.js: -------------------------------------------------------------------------------- 1 | !function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a= 3 | b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;ah[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, 10 | q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d.push(["com", 11 | /^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+ 12 | s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/, 13 | q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d= 14 | c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], 18 | "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"], 19 | O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], 20 | Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/, 21 | V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-", 22 | /^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/], 23 | ["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}), 24 | ["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);p(v({keywords:Q, 25 | hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]); 26 | p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="
"+a+"
";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1}); 27 | return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i 2 | ///////////////////////////////////Dictionary 3 | console.log("------"); 4 | console.log("Dictionary demo"); 5 | var Person = (function () { 6 | function Person(name, yearOfBirth, city) { 7 | this.name = name; 8 | this.yearOfBirth = yearOfBirth; 9 | this.city = city; 10 | } 11 | Person.prototype.toString = function () { 12 | return this.name + "-" + this.yearOfBirth; // City is not a part of the key. 13 | }; 14 | return Person; 15 | })(); 16 | var Car = (function () { 17 | function Car(company, type, year) { 18 | this.company = company; 19 | this.type = type; 20 | this.year = year; 21 | } 22 | Car.prototype.toString = function () { 23 | // Short hand. Adds each own property 24 | return collections.makeString(this); 25 | }; 26 | return Car; 27 | })(); 28 | var dict = new collections.Dictionary(); 29 | dict.setValue(new Person("john", 1970, "melbourne"), new Car("honda", "city", 2002)); 30 | dict.setValue(new Person("gavin", 1984), new Car("ferrari", "F50", 2006)); 31 | console.log("Orig"); 32 | console.log(dict); 33 | // Changes the same john, since city is not part of key 34 | dict.setValue(new Person("john", 1970, "sydney"), new Car("honda", "accord", 2006)); 35 | // Add a new john 36 | dict.setValue(new Person("john", 1971), new Car("nissan", "micra", 2010)); 37 | console.log("Updated"); 38 | console.log(dict); 39 | // Showing getting / setting a single car: 40 | console.log("Single Item"); 41 | var person = new Person("john", 1970); 42 | console.log("-Person:"); 43 | console.log(person); 44 | var car = dict.getValue(person); 45 | console.log("-Car:"); 46 | console.log(car.toString()); 47 | ///////////////////////////////////LinkedDictionary 48 | console.log("------"); 49 | console.log("Linked Dictionary Demo"); 50 | var joe = new Person("Joe", 1994, "Gainesville"); 51 | var elena = new Person("Elena", 1995, "Gainesville"); 52 | var chris = new Person("Chris", 1994, "Ocala"); 53 | var linkedDictionary = new collections.LinkedDictionary(); 54 | // Add Three People, print them in order of insertion. 55 | console.log("==Insertion Order Preserved for Insertions and Iterations=="); 56 | console.log("Inserting Joe"); 57 | linkedDictionary.setValue(joe, new Car("Mazda", "3", 2010)); 58 | console.log(linkedDictionary.toString()); 59 | console.log("Inserting Elena"); 60 | linkedDictionary.setValue(elena, new Car("Mazda", "Tribute", 2002)); 61 | console.log(linkedDictionary.toString()); 62 | console.log("Inserting Chris"); 63 | linkedDictionary.setValue(chris, new Car("Honda", "Accord", 2006)); 64 | console.log(linkedDictionary.toString()); 65 | // Update Preserves order 66 | console.log("==Update Preserves order=="); 67 | console.log("Giving Elena a Maserati"); 68 | linkedDictionary.setValue(elena, new Car("Maserati", "Granturismo", 2015)); 69 | console.log(linkedDictionary.toString()); 70 | // Removal, then adding back does not. 71 | console.log("==Removal, then adding entry with same key back does not preserve order=="); 72 | console.log("Removing Joe"); 73 | linkedDictionary.remove(joe); 74 | console.log(linkedDictionary.toString()); 75 | console.log("Contains Joe? : " + linkedDictionary.containsKey(joe)); 76 | console.log("Replacing Joe, and he will have Lamborghini, and be at end of the ordering"); 77 | linkedDictionary.setValue(joe, new Car("Lamborghini", "Huracan", 2015)); 78 | console.log(linkedDictionary.toString()); 79 | console.log("Contains Joe? : " + linkedDictionary.containsKey(joe)); 80 | // Printing cars in order of insertion 81 | console.log("Printing cars in order of insertion"); 82 | linkedDictionary.values().forEach(function (car) { 83 | console.log("TypeOfCar: " + car.type); 84 | }); 85 | ///////////////////////////////////Set 86 | console.log("------"); 87 | console.log("Set Demo"); 88 | var x = new collections.Set(); 89 | x.add(123); 90 | x.add(123); // Duplicates not allowed in a set 91 | var y = new collections.Set(); 92 | y.add(456); 93 | x.union(y); 94 | console.log(x.toString()); // [123,456] 95 | ///////////////////////////////////Linked list 96 | console.log("------"); 97 | console.log("Linked List demo"); 98 | var ll = new collections.LinkedList(); 99 | ll.add(123); 100 | ll.add(456); 101 | console.log(ll); 102 | //console.log("------"); 103 | //console.log("PriorityQueue demo"); 104 | //# sourceMappingURL=app.js.map -------------------------------------------------------------------------------- /examples/CollectionsDemo/app.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | ///////////////////////////////////Dictionary 4 | console.log("------"); 5 | console.log("Dictionary demo"); 6 | 7 | class Person { 8 | constructor(public name: string, public yearOfBirth: number,public city?:string) { 9 | } 10 | toString() { 11 | return this.name + "-" + this.yearOfBirth; // City is not a part of the key. 12 | } 13 | } 14 | 15 | class Car { 16 | constructor(public company: string, public type: string, public year: number) { 17 | } 18 | toString() { 19 | // Short hand. Adds each own property 20 | return collections.makeString(this); 21 | } 22 | } 23 | var dict = new collections.Dictionary(); 24 | dict.setValue(new Person("john", 1970,"melbourne"), new Car("honda", "city", 2002)); 25 | dict.setValue(new Person("gavin", 1984), new Car("ferrari", "F50", 2006)); 26 | console.log("Orig"); 27 | console.log(dict); 28 | 29 | // Changes the same john, since city is not part of key 30 | dict.setValue(new Person("john", 1970, "sydney"), new Car("honda", "accord", 2006)); 31 | // Add a new john 32 | dict.setValue(new Person("john", 1971), new Car("nissan", "micra", 2010)); 33 | console.log("Updated"); 34 | console.log(dict); 35 | 36 | // Showing getting / setting a single car: 37 | console.log("Single Item"); 38 | var person = new Person("john", 1970); 39 | console.log("-Person:"); 40 | console.log(person); 41 | 42 | var car = dict.getValue(person); 43 | console.log("-Car:"); 44 | console.log(car.toString()); 45 | 46 | ///////////////////////////////////LinkedDictionary 47 | console.log("------"); 48 | console.log("Linked Dictionary Demo"); 49 | 50 | var joe = new Person("Joe", 1994, "Gainesville"); 51 | var elena = new Person("Elena", 1995, "Gainesville"); 52 | var chris = new Person("Chris", 1994, "Ocala"); 53 | 54 | var linkedDictionary = new collections.LinkedDictionary(); 55 | 56 | // Add Three People, print them in order of insertion. 57 | console.log("==Insertion Order Preserved for Insertions and Iterations=="); 58 | console.log("Inserting Joe"); 59 | linkedDictionary.setValue(joe, new Car("Mazda", "3", 2010)); 60 | console.log(linkedDictionary.toString()); 61 | 62 | console.log("Inserting Elena"); 63 | linkedDictionary.setValue(elena, new Car("Mazda", "Tribute", 2002)); 64 | console.log(linkedDictionary.toString()); 65 | 66 | console.log("Inserting Chris"); 67 | linkedDictionary.setValue(chris, new Car("Honda", "Accord", 2006)); 68 | console.log(linkedDictionary.toString()); 69 | 70 | // Update Preserves order 71 | console.log("==Update Preserves order=="); 72 | console.log("Giving Elena a Maserati"); 73 | linkedDictionary.setValue(elena, new Car("Maserati", "Granturismo", 2015)); 74 | console.log(linkedDictionary.toString()); 75 | 76 | // Removal, then adding back does not. 77 | console.log("==Removal, then adding entry with same key back does not preserve order=="); 78 | console.log("Removing Joe"); 79 | linkedDictionary.remove(joe); 80 | console.log(linkedDictionary.toString()); 81 | console.log("Contains Joe? : " + linkedDictionary.containsKey(joe)); 82 | 83 | console.log("Replacing Joe, and he will have Lamborghini, and be at end of the ordering"); 84 | linkedDictionary.setValue(joe, new Car("Lamborghini", "Huracan", 2015)); 85 | console.log(linkedDictionary.toString()); 86 | console.log("Contains Joe? : " + linkedDictionary.containsKey(joe)); 87 | 88 | // Printing cars in order of insertion 89 | console.log("Printing cars in order of insertion"); 90 | linkedDictionary.values().forEach(car => { 91 | console.log("TypeOfCar: " + car.type); 92 | }); 93 | 94 | ///////////////////////////////////Set 95 | console.log("------"); 96 | console.log("Set Demo"); 97 | var x = new collections.Set(); 98 | x.add(123); 99 | x.add(123); // Duplicates not allowed in a set 100 | 101 | var y = new collections.Set(); 102 | y.add(456); 103 | x.union(y); 104 | 105 | console.log(x.toString()); // [123,456] 106 | 107 | ///////////////////////////////////Linked list 108 | console.log("------"); 109 | console.log("Linked List demo"); 110 | var ll = new collections.LinkedList(); 111 | ll.add(123); 112 | ll.add(456); 113 | console.log(ll); 114 | 115 | 116 | //console.log("------"); 117 | //console.log("PriorityQueue demo"); 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /examples/CollectionsDemo/index.htm: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | TypeScript Collections Demo 7 | 8 | 9 | 10 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 |
26 |

TypeScript Collections Demo

27 |
28 |
29 |

Application Log

30 |

31 |     
32 | 33 | Fork me on GitHub 34 | 35 | 36 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/CollectionsDemo/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/CollectionsDemo/web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /examples/CollectionsDemo/web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /examples/CollectionsDemo/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/SimpleDemo/index.ts: -------------------------------------------------------------------------------- 1 | import * as Collections from '../../src/lib'; 2 | 3 | var bsTree = new Collections.BSTreeKV(); 4 | bsTree.add('1'); 5 | bsTree.add('2'); -------------------------------------------------------------------------------- /minify-umd.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var UglifyJS = require("uglify-js"); 3 | var util = require("util"); 4 | var packageJson = require("./package.json"); 5 | 6 | var inputFilePath = "./dist/lib/umd.js"; 7 | var outputFilePath = "./dist/lib/umd.min.js"; 8 | 9 | var code = fs.readFileSync(inputFilePath, { encoding: "utf-8" }); 10 | var result = UglifyJS.minify(code); 11 | if (result.error) { 12 | console.log('Error during minification: '); 13 | console.log(result) 14 | throw result.error; 15 | } 16 | fs.writeFileSync(outputFilePath, result.code, { encoding: "utf-8" }); 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-collections", 3 | "version": "1.3.2", 4 | "description": "A complete, fully tested data structure library written in TypeScript.", 5 | "main": "./dist/lib/umd.js", 6 | "browser": "./dist/lib/index.js", 7 | "jsnext:main": "./dist/lib/index.js", 8 | "typings": "./dist/lib/index.d.ts", 9 | "scripts": { 10 | "clean": "rimraf ./dist", 11 | "lint": "tslint -c ./tslint.json ./src/**/*.ts", 12 | "tsc": "tsc", 13 | "umd": "node browserify-umd.js", 14 | "minify": "node minify-umd.js", 15 | "build": "npm run clean && npm run lint && npm run tsc && npm run umd && npm run minify", 16 | "test": "mocha ./dist/test/*Test.js", 17 | "cover": "istanbul cover ./node_modules/mocha/bin/_mocha -- ./dist/test/*.js", 18 | "publish_to_npm": "npm publish", 19 | "all": "npm run build && npm run test && npm run cover" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/basarat/typescript-collections.git" 24 | }, 25 | "keywords": [ 26 | "typescript", 27 | "generics", 28 | "data", 29 | "structures", 30 | "collections", 31 | "linked", 32 | "list", 33 | "dictionary", 34 | "default", 35 | "dictionary", 36 | "multi", 37 | "dictionary", 38 | "binary", 39 | "search", 40 | "tree", 41 | "key", 42 | "value", 43 | "stack", 44 | "queue", 45 | "set", 46 | "bag", 47 | "binary", 48 | "heap", 49 | "priority", 50 | "queue", 51 | "array" 52 | ], 53 | "author": "Basarat Ali Syed (http://basarat.com)", 54 | "contributors": [ 55 | "Tomasz Ciborski (http://ciborski.com)" 56 | ], 57 | "license": "MIT", 58 | "bugs": { 59 | "url": "https://github.com/basarat/typescript-collections/issues" 60 | }, 61 | "homepage": "https://github.com/basarat/typescript-collections", 62 | "devDependencies": { 63 | "@types/chai": "^4.0.4", 64 | "@types/mocha": "^2.2.32", 65 | "@types/node": "^8.0.28", 66 | "@types/power-assert": "^1.4.29", 67 | "@types/source-map-support": "^0.4.0", 68 | "browserify": "^14.4.0", 69 | "browserify-umdify": "^1.0.3", 70 | "chai": "^4.1.2", 71 | "glob": "^7.0.6", 72 | "istanbul": "^0.4.5", 73 | "jasmine": "^2.5.1", 74 | "karma": "^1.3.0", 75 | "karma-chrome-launcher": "^2.0.0", 76 | "karma-firefox-launcher": "^1.0.0", 77 | "karma-mocha": "^1.1.1", 78 | "karma-mocha-reporter": "^2.1.0", 79 | "mkdirp": "^0.5.1", 80 | "mocha": "^3.5.3", 81 | "power-assert": "^1.4.4", 82 | "rimraf": "^2.5.4", 83 | "source-map-support": "^0.4.2", 84 | "tslint": "^5.7.0", 85 | "typescript": "^2.5.2", 86 | "uglify-js": "^3.3.9", 87 | "util": "^0.10.3" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/lib/BSTree.ts: -------------------------------------------------------------------------------- 1 | import BSTreeKV from './BSTreeKV'; 2 | 3 | /** 4 | * Special-case of the binary search tree in which the search key is equal to the element type. 5 | * This definition is suitable when the element type can not be split between what defines its order 6 | * and what does not (eg. primitive types as opposed to indexed records). 7 | * 8 | * The table below shows some use-case examples for both interfaces: 9 | * 10 | * element type | most suitable interface 11 | * ------------------------------------|---------------------------- 12 | * number | BSTree 13 | * string | BSTree 14 | * { order: number, data: string } | BSTreeKV<{order: number}, {order: number, data: string}> 15 | * 16 | * @see BSTreeKV 17 | */ 18 | export default class BSTree extends BSTreeKV { 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/Bag.ts: -------------------------------------------------------------------------------- 1 | import * as util from './util'; 2 | import Dictionary from './Dictionary'; 3 | import Set from './Set'; 4 | 5 | export default class Bag { 6 | 7 | private toStrF: (item: T) => string; 8 | private dictionary: Dictionary; 9 | private nElements: number; 10 | 11 | /** 12 | * Creates an empty bag. 13 | * @class

A bag is a special kind of set in which members are 14 | * allowed to appear more than once.

15 | *

If the inserted elements are custom objects a function 16 | * which converts elements to unique strings must be provided. Example:

17 | * 18 | *
 19 |      * function petToString(pet) {
 20 |      *  return pet.name;
 21 |      * }
 22 |      * 
23 | * 24 | * @constructor 25 | * @param {function(Object):string=} toStrFunction optional function used 26 | * to convert elements to strings. If the elements aren't strings or if toString() 27 | * is not appropriate, a custom function which receives an object and returns a 28 | * unique string must be provided. 29 | */ 30 | constructor(toStrFunction?: (item: T) => string) { 31 | this.toStrF = toStrFunction || util.defaultToString; 32 | this.dictionary = new Dictionary(this.toStrF); 33 | this.nElements = 0; 34 | } 35 | 36 | 37 | /** 38 | * Adds nCopies of the specified object to this bag. 39 | * @param {Object} element element to add. 40 | * @param {number=} nCopies the number of copies to add, if this argument is 41 | * undefined 1 copy is added. 42 | * @return {boolean} true unless element is undefined. 43 | */ 44 | add(element: T, nCopies: number = 1): boolean { 45 | 46 | if (util.isUndefined(element) || nCopies <= 0) { 47 | return false; 48 | } 49 | 50 | if (!this.contains(element)) { 51 | const node = { 52 | value: element, 53 | copies: nCopies 54 | }; 55 | this.dictionary.setValue(element, node); 56 | } else { 57 | this.dictionary.getValue(element).copies += nCopies; 58 | } 59 | this.nElements += nCopies; 60 | return true; 61 | } 62 | 63 | /** 64 | * Counts the number of copies of the specified object in this bag. 65 | * @param {Object} element the object to search for.. 66 | * @return {number} the number of copies of the object, 0 if not found 67 | */ 68 | count(element: T): number { 69 | 70 | if (!this.contains(element)) { 71 | return 0; 72 | } else { 73 | return this.dictionary.getValue(element).copies; 74 | } 75 | } 76 | 77 | /** 78 | * Returns true if this bag contains the specified element. 79 | * @param {Object} element element to search for. 80 | * @return {boolean} true if this bag contains the specified element, 81 | * false otherwise. 82 | */ 83 | contains(element: T): boolean { 84 | return this.dictionary.containsKey(element); 85 | } 86 | 87 | /** 88 | * Removes nCopies of the specified object to this bag. 89 | * If the number of copies to remove is greater than the actual number 90 | * of copies in the Bag, all copies are removed. 91 | * @param {Object} element element to remove. 92 | * @param {number=} nCopies the number of copies to remove, if this argument is 93 | * undefined 1 copy is removed. 94 | * @return {boolean} true if at least 1 element was removed. 95 | */ 96 | remove(element: T, nCopies: number = 1) { 97 | 98 | if (util.isUndefined(element) || nCopies <= 0) { 99 | return false; 100 | } 101 | 102 | if (!this.contains(element)) { 103 | return false; 104 | } else { 105 | const node = this.dictionary.getValue(element); 106 | if (nCopies > node.copies) { 107 | this.nElements -= node.copies; 108 | } else { 109 | this.nElements -= nCopies; 110 | } 111 | node.copies -= nCopies; 112 | if (node.copies <= 0) { 113 | this.dictionary.remove(element); 114 | } 115 | return true; 116 | } 117 | } 118 | 119 | /** 120 | * Returns an array containing all of the elements in this big in arbitrary order, 121 | * including multiple copies. 122 | * @return {Array} an array containing all of the elements in this bag. 123 | */ 124 | toArray(): T[] { 125 | const a: Array = []; 126 | const values = this.dictionary.values(); 127 | for (const node of values) { 128 | const element = node.value; 129 | const copies = node.copies; 130 | for (let j = 0; j < copies; j++) { 131 | a.push(element); 132 | } 133 | } 134 | return a; 135 | } 136 | 137 | /** 138 | * Returns a set of unique elements in this bag. 139 | * @return {collections.Set} a set of unique elements in this bag. 140 | */ 141 | toSet(): Set { 142 | const toret = new Set(this.toStrF); 143 | const elements = this.dictionary.values(); 144 | for (const ele of elements) { 145 | const value = ele.value; 146 | toret.add(value); 147 | } 148 | return toret; 149 | } 150 | 151 | /** 152 | * Executes the provided function once for each element 153 | * present in this bag, including multiple copies. 154 | * @param {function(Object):*} callback function to execute, it is 155 | * invoked with one argument: the element. To break the iteration you can 156 | * optionally return false. 157 | */ 158 | forEach(callback: util.ILoopFunction) { 159 | this.dictionary.forEach(function(k, v) { 160 | const value = v.value; 161 | const copies = v.copies; 162 | for (let i = 0; i < copies; i++) { 163 | if (callback(value) === false) { 164 | return false; 165 | } 166 | } 167 | return true; 168 | }); 169 | } 170 | /** 171 | * Returns the number of elements in this bag. 172 | * @return {number} the number of elements in this bag. 173 | */ 174 | size(): number { 175 | return this.nElements; 176 | } 177 | 178 | /** 179 | * Returns true if this bag contains no elements. 180 | * @return {boolean} true if this bag contains no elements. 181 | */ 182 | isEmpty(): boolean { 183 | return this.nElements === 0; 184 | } 185 | 186 | /** 187 | * Removes all of the elements from this bag. 188 | */ 189 | clear(): void { 190 | this.nElements = 0; 191 | this.dictionary.clear(); 192 | } 193 | 194 | }// End of bag 195 | -------------------------------------------------------------------------------- /src/lib/Dictionary.ts: -------------------------------------------------------------------------------- 1 | import * as util from './util'; 2 | import * as arrays from './arrays'; 3 | 4 | // Used internally by dictionary 5 | export interface IDictionaryPair { 6 | key: K; 7 | value: V; 8 | } 9 | 10 | export default class Dictionary { 11 | 12 | /** 13 | * Object holding the key-value pairs. 14 | * @type {Object} 15 | * @private 16 | */ 17 | protected table: { [key: string]: IDictionaryPair }; 18 | //: [key: K] will not work since indices can only by strings in javascript and typescript enforces this. 19 | 20 | /** 21 | * Number of elements in the list. 22 | * @type {number} 23 | * @private 24 | */ 25 | protected nElements: number; 26 | 27 | /** 28 | * Function used to convert keys to strings. 29 | * @type {function(Object):string} 30 | * @protected 31 | */ 32 | protected toStr: (key: K) => string; 33 | 34 | 35 | /** 36 | * Creates an empty dictionary. 37 | * @class

Dictionaries map keys to values; each key can map to at most one value. 38 | * This implementation accepts any kind of objects as keys.

39 | * 40 | *

If the keys are custom objects a function which converts keys to unique 41 | * strings must be provided. Example:

42 | *
 43 |      * function petToString(pet) {
 44 |      *  return pet.name;
 45 |      * }
 46 |      * 
47 | * @constructor 48 | * @param {function(Object):string=} toStrFunction optional function used 49 | * to convert keys to strings. If the keys aren't strings or if toString() 50 | * is not appropriate, a custom function which receives a key and returns a 51 | * unique string must be provided. 52 | */ 53 | constructor(toStrFunction?: (key: K) => string) { 54 | this.table = {}; 55 | this.nElements = 0; 56 | this.toStr = toStrFunction || util.defaultToString; 57 | } 58 | 59 | 60 | /** 61 | * Returns the value to which this dictionary maps the specified key. 62 | * Returns undefined if this dictionary contains no mapping for this key. 63 | * @param {Object} key key whose associated value is to be returned. 64 | * @return {*} the value to which this dictionary maps the specified key or 65 | * undefined if the map contains no mapping for this key. 66 | */ 67 | getValue(key: K): V | undefined { 68 | const pair: IDictionaryPair = this.table['$' + this.toStr(key)]; 69 | if (util.isUndefined(pair)) { 70 | return undefined; 71 | } 72 | return pair.value; 73 | } 74 | 75 | 76 | /** 77 | * Associates the specified value with the specified key in this dictionary. 78 | * If the dictionary previously contained a mapping for this key, the old 79 | * value is replaced by the specified value. 80 | * @param {Object} key key with which the specified value is to be 81 | * associated. 82 | * @param {Object} value value to be associated with the specified key. 83 | * @return {*} previous value associated with the specified key, or undefined if 84 | * there was no mapping for the key or if the key/value are undefined. 85 | */ 86 | setValue(key: K, value: V): V | undefined { 87 | 88 | if (util.isUndefined(key) || util.isUndefined(value)) { 89 | return undefined; 90 | } 91 | 92 | let ret: V | undefined; 93 | const k = '$' + this.toStr(key); 94 | const previousElement: IDictionaryPair = this.table[k]; 95 | if (util.isUndefined(previousElement)) { 96 | this.nElements++; 97 | ret = undefined; 98 | } else { 99 | ret = previousElement.value; 100 | } 101 | this.table[k] = { 102 | key: key, 103 | value: value 104 | }; 105 | return ret; 106 | } 107 | 108 | /** 109 | * Removes the mapping for this key from this dictionary if it is present. 110 | * @param {Object} key key whose mapping is to be removed from the 111 | * dictionary. 112 | * @return {*} previous value associated with specified key, or undefined if 113 | * there was no mapping for key. 114 | */ 115 | remove(key: K): V | undefined { 116 | const k = '$' + this.toStr(key); 117 | const previousElement: IDictionaryPair = this.table[k]; 118 | if (!util.isUndefined(previousElement)) { 119 | delete this.table[k]; 120 | this.nElements--; 121 | return previousElement.value; 122 | } 123 | return undefined; 124 | } 125 | 126 | /** 127 | * Returns an array containing all of the keys in this dictionary. 128 | * @return {Array} an array containing all of the keys in this dictionary. 129 | */ 130 | keys(): K[] { 131 | const array: K[] = []; 132 | for (const name in this.table) { 133 | if (util.has(this.table, name)) { 134 | const pair: IDictionaryPair = this.table[name]; 135 | array.push(pair.key); 136 | } 137 | } 138 | return array; 139 | } 140 | 141 | /** 142 | * Returns an array containing all of the values in this dictionary. 143 | * @return {Array} an array containing all of the values in this dictionary. 144 | */ 145 | values(): V[] { 146 | const array: V[] = []; 147 | for (const name in this.table) { 148 | if (util.has(this.table, name)) { 149 | const pair: IDictionaryPair = this.table[name]; 150 | array.push(pair.value); 151 | } 152 | } 153 | return array; 154 | } 155 | 156 | /** 157 | * Executes the provided function once for each key-value pair 158 | * present in this dictionary. 159 | * @param {function(Object,Object):*} callback function to execute, it is 160 | * invoked with two arguments: key and value. To break the iteration you can 161 | * optionally return false. 162 | */ 163 | forEach(callback: (key: K, value: V) => any): void { 164 | for (const name in this.table) { 165 | if (util.has(this.table, name)) { 166 | const pair: IDictionaryPair = this.table[name]; 167 | const ret = callback(pair.key, pair.value); 168 | if (ret === false) { 169 | return; 170 | } 171 | } 172 | } 173 | } 174 | 175 | /** 176 | * Returns true if this dictionary contains a mapping for the specified key. 177 | * @param {Object} key key whose presence in this dictionary is to be 178 | * tested. 179 | * @return {boolean} true if this dictionary contains a mapping for the 180 | * specified key. 181 | */ 182 | containsKey(key: K): boolean { 183 | return !util.isUndefined(this.getValue(key)); 184 | } 185 | 186 | /** 187 | * Removes all mappings from this dictionary. 188 | * @this {collections.Dictionary} 189 | */ 190 | clear() { 191 | this.table = {}; 192 | this.nElements = 0; 193 | } 194 | 195 | /** 196 | * Returns the number of keys in this dictionary. 197 | * @return {number} the number of key-value mappings in this dictionary. 198 | */ 199 | size(): number { 200 | return this.nElements; 201 | } 202 | 203 | /** 204 | * Returns true if this dictionary contains no mappings. 205 | * @return {boolean} true if this dictionary contains no mappings. 206 | */ 207 | isEmpty(): boolean { 208 | return this.nElements <= 0; 209 | } 210 | 211 | toString(): string { 212 | let toret = '{'; 213 | this.forEach((k, v) => { 214 | toret += `\n\t${k} : ${v}`; 215 | }); 216 | return toret + '\n}'; 217 | } 218 | } // End of dictionary 219 | -------------------------------------------------------------------------------- /src/lib/FactoryDictionary.ts: -------------------------------------------------------------------------------- 1 | import Dictionary from './Dictionary'; 2 | import * as util from './util'; 3 | 4 | export default class FactoryDictionary extends Dictionary { 5 | 6 | /** 7 | * Factory to create default values. 8 | * @type {function(Object):string} 9 | * @protected 10 | */ 11 | protected defaultFactoryFunction: () => V; 12 | 13 | /** 14 | * Creates an empty dictionary. 15 | * @class

Dictionaries map keys to values; each key can map to at most one value. 16 | * This implementation accepts any kind of objects as keys.

17 | * 18 | *

The default factory function should return a new object of the provided 19 | * type. Example:

20 | *
21 |      * function petFactory() {
22 |      *  return new Pet();
23 |      * }
24 |      * 
25 | * 26 | *

If the keys are custom objects a function which converts keys to unique 27 | * strings must be provided. Example:

28 | *
29 |      * function petToString(pet) {
30 |      *  return pet.name;
31 |      * }
32 |      * 
33 | * @constructor 34 | * @param {function():V=} defaultFactoryFunction function used to create a 35 | * default object. 36 | * @param {function(Object):string=} toStrFunction optional function used 37 | * to convert keys to strings. If the keys aren't strings or if toString() 38 | * is not appropriate, a custom function which receives a key and returns a 39 | * unique string must be provided. 40 | */ 41 | constructor(defaultFactoryFunction: () => V, toStrFunction?: (key: K) => string) { 42 | super(toStrFunction); 43 | 44 | this.defaultFactoryFunction = defaultFactoryFunction; 45 | } 46 | 47 | 48 | /** 49 | * Associates the specified default value with the specified key in this dictionary, 50 | * if it didn't contain the key yet. If the key existed, the existing value will be used. 51 | * @param {Object} key key with which the specified value is to be 52 | * associated. 53 | * @param {Object} defaultValue default value to be associated with the specified key. 54 | * @return {*} previous value associated with the specified key, or the default value, 55 | * if the key didn't exist yet. 56 | */ 57 | setDefault(key: K, defaultValue: V): V { 58 | const currentValue: V | undefined = super.getValue(key); 59 | 60 | if (util.isUndefined(currentValue)) { 61 | this.setValue(key, defaultValue); 62 | 63 | return defaultValue; 64 | } 65 | 66 | return currentValue; 67 | } 68 | 69 | /** 70 | * Returns the value to which this dictionary maps the specified key. 71 | * Returns a default value created by the factory passed in the constructor, 72 | * if this dictionary contains no mapping for this key. The missing key will 73 | * automatically be added to the dictionary. 74 | * @param {Object} key key whose associated value is to be returned. 75 | * @return {*} the value to which this dictionary maps the specified key or 76 | * a default value if the map contains no mapping for this key. 77 | */ 78 | getValue(key: K): V { 79 | return this.setDefault(key, this.defaultFactoryFunction()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/lib/Heap.ts: -------------------------------------------------------------------------------- 1 | import * as collections from './util'; 2 | import * as arrays from './arrays'; 3 | 4 | export default class Heap { 5 | /** 6 | * Array used to store the elements of the heap. 7 | * @type {Array.} 8 | * @private 9 | */ 10 | private data: T[] = []; 11 | /** 12 | * Function used to compare elements. 13 | * @type {function(Object,Object):number} 14 | * @private 15 | */ 16 | private compare: collections.ICompareFunction; 17 | /** 18 | * Creates an empty Heap. 19 | * @class 20 | *

A heap is a binary tree, where the nodes maintain the heap property: 21 | * each node is smaller than each of its children and therefore a MinHeap 22 | * This implementation uses an array to store elements.

23 | *

If the inserted elements are custom objects a compare function must be provided, 24 | * at construction time, otherwise the <=, === and >= operators are 25 | * used to compare elements. Example:

26 | * 27 | *
 28 |      * function compare(a, b) {
 29 |      *  if (a is less than b by some ordering criterion) {
 30 |      *     return -1;
 31 |      *  } if (a is greater than b by the ordering criterion) {
 32 |      *     return 1;
 33 |      *  }
 34 |      *  // a must be equal to b
 35 |      *  return 0;
 36 |      * }
 37 |      * 
38 | * 39 | *

If a Max-Heap is wanted (greater elements on top) you can a provide a 40 | * reverse compare function to accomplish that behavior. Example:

41 | * 42 | *
 43 |      * function reverseCompare(a, b) {
 44 |      *  if (a is less than b by some ordering criterion) {
 45 |      *     return 1;
 46 |      *  } if (a is greater than b by the ordering criterion) {
 47 |      *     return -1;
 48 |      *  }
 49 |      *  // a must be equal to b
 50 |      *  return 0;
 51 |      * }
 52 |      * 
53 | * 54 | * @constructor 55 | * @param {function(Object,Object):number=} compareFunction optional 56 | * function used to compare two elements. Must return a negative integer, 57 | * zero, or a positive integer as the first argument is less than, equal to, 58 | * or greater than the second. 59 | */ 60 | constructor(compareFunction?: collections.ICompareFunction) { 61 | this.compare = compareFunction || collections.defaultCompare; 62 | } 63 | 64 | /** 65 | * Returns the index of the left child of the node at the given index. 66 | * @param {number} nodeIndex The index of the node to get the left child 67 | * for. 68 | * @return {number} The index of the left child. 69 | * @private 70 | */ 71 | private leftChildIndex(nodeIndex: number): number { 72 | return (2 * nodeIndex) + 1; 73 | } 74 | /** 75 | * Returns the index of the right child of the node at the given index. 76 | * @param {number} nodeIndex The index of the node to get the right child 77 | * for. 78 | * @return {number} The index of the right child. 79 | * @private 80 | */ 81 | private rightChildIndex(nodeIndex: number): number { 82 | return (2 * nodeIndex) + 2; 83 | } 84 | /** 85 | * Returns the index of the parent of the node at the given index. 86 | * @param {number} nodeIndex The index of the node to get the parent for. 87 | * @return {number} The index of the parent. 88 | * @private 89 | */ 90 | private parentIndex(nodeIndex: number): number { 91 | return Math.floor((nodeIndex - 1) / 2); 92 | } 93 | /** 94 | * Returns the index of the smaller child node (if it exists). 95 | * @param {number} leftChild left child index. 96 | * @param {number} rightChild right child index. 97 | * @return {number} the index with the minimum value or -1 if it doesn't 98 | * exists. 99 | * @private 100 | */ 101 | private minIndex(leftChild: number, rightChild: number): number { 102 | 103 | if (rightChild >= this.data.length) { 104 | if (leftChild >= this.data.length) { 105 | return -1; 106 | } else { 107 | return leftChild; 108 | } 109 | } else { 110 | if (this.compare(this.data[leftChild], this.data[rightChild]) <= 0) { 111 | return leftChild; 112 | } else { 113 | return rightChild; 114 | } 115 | } 116 | } 117 | /** 118 | * Moves the node at the given index up to its proper place in the heap. 119 | * @param {number} index The index of the node to move up. 120 | * @private 121 | */ 122 | private siftUp(index: number): void { 123 | 124 | let parent = this.parentIndex(index); 125 | while (index > 0 && this.compare(this.data[parent], this.data[index]) > 0) { 126 | arrays.swap(this.data, parent, index); 127 | index = parent; 128 | parent = this.parentIndex(index); 129 | } 130 | } 131 | /** 132 | * Moves the node at the given index down to its proper place in the heap. 133 | * @param {number} nodeIndex The index of the node to move down. 134 | * @private 135 | */ 136 | private siftDown(nodeIndex: number): void { 137 | 138 | //smaller child index 139 | let min = this.minIndex(this.leftChildIndex(nodeIndex), 140 | this.rightChildIndex(nodeIndex)); 141 | 142 | while (min >= 0 && this.compare(this.data[nodeIndex], 143 | this.data[min]) > 0) { 144 | arrays.swap(this.data, min, nodeIndex); 145 | nodeIndex = min; 146 | min = this.minIndex(this.leftChildIndex(nodeIndex), 147 | this.rightChildIndex(nodeIndex)); 148 | } 149 | } 150 | /** 151 | * Retrieves but does not remove the root element of this heap. 152 | * @return {*} The value at the root of the heap. Returns undefined if the 153 | * heap is empty. 154 | */ 155 | peek(): T | undefined { 156 | 157 | if (this.data.length > 0) { 158 | return this.data[0]; 159 | } else { 160 | return undefined; 161 | } 162 | } 163 | /** 164 | * Adds the given element into the heap. 165 | * @param {*} element the element. 166 | * @return true if the element was added or fals if it is undefined. 167 | */ 168 | add(element: T): boolean { 169 | if (collections.isUndefined(element)) { 170 | return false; 171 | } 172 | this.data.push(element); 173 | this.siftUp(this.data.length - 1); 174 | return true; 175 | } 176 | 177 | /** 178 | * Retrieves and removes the root element of this heap. 179 | * @return {*} The value removed from the root of the heap. Returns 180 | * undefined if the heap is empty. 181 | */ 182 | removeRoot(): T | undefined { 183 | 184 | if (this.data.length > 0) { 185 | const obj = this.data[0]; 186 | this.data[0] = this.data[this.data.length - 1]; 187 | this.data.splice(this.data.length - 1, 1); 188 | if (this.data.length > 0) { 189 | this.siftDown(0); 190 | } 191 | return obj; 192 | } 193 | return undefined; 194 | } 195 | /** 196 | * Returns true if this heap contains the specified element. 197 | * @param {Object} element element to search for. 198 | * @return {boolean} true if this Heap contains the specified element, false 199 | * otherwise. 200 | */ 201 | contains(element: T): boolean { 202 | const equF = collections.compareToEquals(this.compare); 203 | return arrays.contains(this.data, element, equF); 204 | } 205 | /** 206 | * Returns the number of elements in this heap. 207 | * @return {number} the number of elements in this heap. 208 | */ 209 | size(): number { 210 | return this.data.length; 211 | } 212 | /** 213 | * Checks if this heap is empty. 214 | * @return {boolean} true if and only if this heap contains no items; false 215 | * otherwise. 216 | */ 217 | isEmpty(): boolean { 218 | return this.data.length <= 0; 219 | } 220 | /** 221 | * Removes all of the elements from this heap. 222 | */ 223 | clear(): void { 224 | this.data.length = 0; 225 | } 226 | 227 | /** 228 | * Executes the provided function once for each element present in this heap in 229 | * no particular order. 230 | * @param {function(Object):*} callback function to execute, it is 231 | * invoked with one argument: the element value, to break the iteration you can 232 | * optionally return false. 233 | */ 234 | forEach(callback: collections.ILoopFunction) { 235 | arrays.forEach(this.data, callback); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/lib/LinkedDictionary.ts: -------------------------------------------------------------------------------- 1 | import {IDictionaryPair, default as Dictionary} from './Dictionary'; 2 | 3 | import * as util from './util'; 4 | 5 | /** 6 | * This class is used by the LinkedDictionary Internally 7 | * Has to be a class, not an interface, because it needs to have 8 | * the 'unlink' function defined. 9 | */ 10 | class LinkedDictionaryPair implements IDictionaryPair { 11 | prev: LinkedDictionaryPair | HeadOrTailLinkedDictionaryPair; 12 | next: LinkedDictionaryPair | HeadOrTailLinkedDictionaryPair; 13 | 14 | constructor(public key: K, public value: V) { } 15 | 16 | unlink() { 17 | this.prev.next = this.next; 18 | this.next.prev = this.prev; 19 | } 20 | } 21 | 22 | /** 23 | * The head and tail elements of the list have null key and value properties but they 24 | * usually link to normal nodes. 25 | */ 26 | class HeadOrTailLinkedDictionaryPair implements IDictionaryPair { 27 | prev: LinkedDictionaryPair | HeadOrTailLinkedDictionaryPair; 28 | next: LinkedDictionaryPair | HeadOrTailLinkedDictionaryPair; 29 | key: null = null; 30 | value: null = null; 31 | 32 | unlink() { 33 | this.prev.next = this.next; 34 | this.next.prev = this.prev; 35 | } 36 | } 37 | 38 | function isHeadOrTailLinkedDictionaryPair(p: HeadOrTailLinkedDictionaryPair | LinkedDictionaryPair) 39 | : p is HeadOrTailLinkedDictionaryPair { 40 | return !p.next; 41 | } 42 | 43 | export default class LinkedDictionary extends Dictionary { 44 | private head: HeadOrTailLinkedDictionaryPair; // Head Identifier of the list. holds no Key or Value 45 | private tail: HeadOrTailLinkedDictionaryPair; // Tail Identifier of the list. holds no Key or Value 46 | 47 | constructor(toStrFunction?: (key: K) => string) { 48 | super(toStrFunction); 49 | this.head = new HeadOrTailLinkedDictionaryPair(); 50 | this.tail = new HeadOrTailLinkedDictionaryPair(); 51 | this.head.next = this.tail; 52 | this.tail.prev = this.head; 53 | } 54 | 55 | /** 56 | * Inserts the new node to the 'tail' of the list, updating the 57 | * neighbors, and moving 'this.tail' (the End of List indicator) that 58 | * to the end. 59 | */ 60 | private appendToTail(entry: LinkedDictionaryPair) { 61 | const lastNode = this.tail.prev; 62 | lastNode.next = entry; 63 | entry.prev = lastNode; 64 | entry.next = this.tail; 65 | this.tail.prev = entry; 66 | } 67 | 68 | /** 69 | * Retrieves a linked dictionary from the table internally 70 | */ 71 | private getLinkedDictionaryPair(key: K): LinkedDictionaryPair | undefined { 72 | if (util.isUndefined(key)) { 73 | return undefined; 74 | } 75 | const k = '$' + this.toStr(key); 76 | const pair = >(this.table[k]); 77 | return pair; 78 | } 79 | 80 | /** 81 | * Returns the value to which this dictionary maps the specified key. 82 | * Returns undefined if this dictionary contains no mapping for this key. 83 | * @param {Object} key key whose associated value is to be returned. 84 | * @return {*} the value to which this dictionary maps the specified key or 85 | * undefined if the map contains no mapping for this key. 86 | */ 87 | getValue(key: K): V | undefined { 88 | const pair = this.getLinkedDictionaryPair(key); 89 | if (!util.isUndefined(pair)) { 90 | return pair.value; 91 | } 92 | return undefined; 93 | } 94 | 95 | /** 96 | * Removes the mapping for this key from this dictionary if it is present. 97 | * Also, if a value is present for this key, the entry is removed from the 98 | * insertion ordering. 99 | * @param {Object} key key whose mapping is to be removed from the 100 | * dictionary. 101 | * @return {*} previous value associated with specified key, or undefined if 102 | * there was no mapping for key. 103 | */ 104 | remove(key: K): V | undefined { 105 | const pair = this.getLinkedDictionaryPair(key); 106 | if (!util.isUndefined(pair)) { 107 | super.remove(key); // This will remove it from the table 108 | pair.unlink(); // This will unlink it from the chain 109 | return pair.value; 110 | } 111 | return undefined; 112 | } 113 | 114 | /** 115 | * Removes all mappings from this LinkedDictionary. 116 | * @this {collections.LinkedDictionary} 117 | */ 118 | clear() { 119 | super.clear(); 120 | this.head.next = this.tail; 121 | this.tail.prev = this.head; 122 | } 123 | 124 | /** 125 | * Internal function used when updating an existing KeyValue pair. 126 | * It places the new value indexed by key into the table, but maintains 127 | * its place in the linked ordering. 128 | */ 129 | private replace(oldPair: LinkedDictionaryPair, newPair: LinkedDictionaryPair) { 130 | const k = '$' + this.toStr(newPair.key); 131 | 132 | // set the new Pair's links to existingPair's links 133 | newPair.next = oldPair.next; 134 | newPair.prev = oldPair.prev; 135 | 136 | // Delete Existing Pair from the table, unlink it from chain. 137 | // As a result, the nElements gets decremented by this operation 138 | this.remove(oldPair.key); 139 | 140 | // Link new Pair in place of where oldPair was, 141 | // by pointing the old pair's neighbors to it. 142 | newPair.prev.next = newPair; 143 | newPair.next.prev = newPair; 144 | 145 | this.table[k] = newPair; 146 | 147 | // To make up for the fact that the number of elements was decremented, 148 | // We need to increase it by one. 149 | ++this.nElements; 150 | 151 | } 152 | 153 | /** 154 | * Associates the specified value with the specified key in this dictionary. 155 | * If the dictionary previously contained a mapping for this key, the old 156 | * value is replaced by the specified value. 157 | * Updating of a key that already exists maintains its place in the 158 | * insertion order into the map. 159 | * @param {Object} key key with which the specified value is to be 160 | * associated. 161 | * @param {Object} value value to be associated with the specified key. 162 | * @return {*} previous value associated with the specified key, or undefined if 163 | * there was no mapping for the key or if the key/value are undefined. 164 | */ 165 | setValue(key: K, value: V): V | undefined { 166 | 167 | if (util.isUndefined(key) || util.isUndefined(value)) { 168 | return undefined; 169 | } 170 | 171 | const existingPair = this.getLinkedDictionaryPair(key); 172 | const newPair = new LinkedDictionaryPair(key, value); 173 | 174 | const k = '$' + this.toStr(key); 175 | 176 | // If there is already an element for that key, we 177 | // keep it's place in the LinkedList 178 | if (!util.isUndefined(existingPair)) { 179 | this.replace(existingPair, newPair); 180 | 181 | return existingPair.value; 182 | } else { 183 | this.appendToTail(newPair); 184 | this.table[k] = newPair; 185 | ++this.nElements; 186 | 187 | return undefined; 188 | } 189 | 190 | } 191 | 192 | /** 193 | * Returns an array containing all of the keys in this LinkedDictionary, ordered 194 | * by insertion order. 195 | * @return {Array} an array containing all of the keys in this LinkedDictionary, 196 | * ordered by insertion order. 197 | */ 198 | keys(): K[] { 199 | const array: K[] = []; 200 | this.forEach((key, value) => { 201 | array.push(key); 202 | }); 203 | return array; 204 | } 205 | 206 | /** 207 | * Returns an array containing all of the values in this LinkedDictionary, ordered by 208 | * insertion order. 209 | * @return {Array} an array containing all of the values in this LinkedDictionary, 210 | * ordered by insertion order. 211 | */ 212 | values(): V[] { 213 | const array: V[] = []; 214 | this.forEach((key, value) => { 215 | array.push(value); 216 | }); 217 | return array; 218 | } 219 | 220 | /** 221 | * Executes the provided function once for each key-value pair 222 | * present in this LinkedDictionary. It is done in the order of insertion 223 | * into the LinkedDictionary 224 | * @param {function(Object,Object):*} callback function to execute, it is 225 | * invoked with two arguments: key and value. To break the iteration you can 226 | * optionally return false. 227 | */ 228 | forEach(callback: (key: K, value: V) => any): void { 229 | let crawlNode = this.head.next; 230 | while (!isHeadOrTailLinkedDictionaryPair(crawlNode)) { 231 | const ret = callback(crawlNode.key, crawlNode.value); 232 | if (ret === false) { 233 | return; 234 | } 235 | crawlNode = crawlNode.next; 236 | } 237 | } 238 | 239 | } // End of LinkedDictionary 240 | // /** 241 | // * Returns true if this dictionary is equal to the given dictionary. 242 | // * Two dictionaries are equal if they contain the same mappings. 243 | // * @param {collections.Dictionary} other the other dictionary. 244 | // * @param {function(Object,Object):boolean=} valuesEqualFunction optional 245 | // * function used to check if two values are equal. 246 | // * @return {boolean} true if this dictionary is equal to the given dictionary. 247 | // */ 248 | // collections.Dictionary.prototype.equals = function(other,valuesEqualFunction) { 249 | // const eqF = valuesEqualFunction || collections.defaultEquals; 250 | // if(!(other instanceof collections.Dictionary)){ 251 | // return false; 252 | // } 253 | // if(this.size() !== other.size()){ 254 | // return false; 255 | // } 256 | // return this.equalsAux(this.firstNode,other.firstNode,eqF); 257 | // } 258 | -------------------------------------------------------------------------------- /src/lib/MultiDictionary.ts: -------------------------------------------------------------------------------- 1 | import * as util from './util'; 2 | import Dictionary from './Dictionary'; 3 | import * as arrays from './arrays'; 4 | 5 | export default class MultiDictionary { 6 | 7 | // Cannot do: 8 | // class MultiDictionary extends Dictionary> { 9 | // Since we want to reuse the function name setValue and types in signature become incompatible 10 | // Therefore we are using composition instead of inheritance 11 | private dict: Dictionary>; 12 | private equalsF: util.IEqualsFunction; 13 | private allowDuplicate: boolean; 14 | 15 | /** 16 | * Creates an empty multi dictionary. 17 | * @class

A multi dictionary is a special kind of dictionary that holds 18 | * multiple values against each key. Setting a value into the dictionary will 19 | * add the value to an array at that key. Getting a key will return an array, 20 | * holding all the values set to that key. 21 | * You can configure to allow duplicates in the values. 22 | * This implementation accepts any kind of objects as keys.

23 | * 24 | *

If the keys are custom objects a function which converts keys to strings must be 25 | * provided. Example:

26 | * 27 | *
 28 |      * function petToString(pet) {
 29 |      *     return pet.name;
 30 |      * }
 31 |      * 
32 | *

If the values are custom objects a function to check equality between values 33 | * must be provided. Example:

34 | * 35 | *
 36 |      * function petsAreEqualByAge(pet1,pet2) {
 37 |      *     return pet1.age === pet2.age;
 38 |      * }
 39 |      * 
40 | * @constructor 41 | * @param {function(Object):string=} toStrFunction optional function 42 | * to convert keys to strings. If the keys aren't strings or if toString() 43 | * is not appropriate, a custom function which receives a key and returns a 44 | * unique string must be provided. 45 | * @param {function(Object,Object):boolean=} valuesEqualsFunction optional 46 | * function to check if two values are equal. 47 | * 48 | * @param allowDuplicateValues 49 | */ 50 | constructor(toStrFunction?: (key: K) => string, valuesEqualsFunction?: util.IEqualsFunction, allowDuplicateValues = false) { 51 | this.dict = new Dictionary>(toStrFunction); 52 | this.equalsF = valuesEqualsFunction || util.defaultEquals; 53 | this.allowDuplicate = allowDuplicateValues; 54 | } 55 | /** 56 | * Returns an array holding the values to which this dictionary maps 57 | * the specified key. 58 | * Returns an empty array if this dictionary contains no mappings for this key. 59 | * @param {Object} key key whose associated values are to be returned. 60 | * @return {Array} an array holding the values to which this dictionary maps 61 | * the specified key. 62 | */ 63 | getValue(key: K): V[] { 64 | const values = this.dict.getValue(key); 65 | if (util.isUndefined(values)) { 66 | return []; 67 | } 68 | return arrays.copy(values); 69 | } 70 | 71 | /** 72 | * Adds the value to the array associated with the specified key, if 73 | * it is not already present. 74 | * @param {Object} key key with which the specified value is to be 75 | * associated. 76 | * @param {Object} value the value to add to the array at the key 77 | * @return {boolean} true if the value was not already associated with that key. 78 | */ 79 | setValue(key: K, value: V): boolean { 80 | 81 | if (util.isUndefined(key) || util.isUndefined(value)) { 82 | return false; 83 | } 84 | const array = this.dict.getValue(key); 85 | if (util.isUndefined(array)) { 86 | this.dict.setValue(key, [value]); 87 | return true; 88 | } 89 | if (!this.allowDuplicate) { 90 | if (arrays.contains(array, value, this.equalsF)) { 91 | return false; 92 | } 93 | } 94 | array.push(value); 95 | return true; 96 | } 97 | 98 | /** 99 | * Removes the specified values from the array of values associated with the 100 | * specified key. If a value isn't given, all values associated with the specified 101 | * key are removed. 102 | * @param {Object} key key whose mapping is to be removed from the 103 | * dictionary. 104 | * @param {Object=} value optional argument to specify the value to remove 105 | * from the array associated with the specified key. 106 | * @return {*} true if the dictionary changed, false if the key doesn't exist or 107 | * if the specified value isn't associated with the specified key. 108 | */ 109 | remove(key: K, value?: V): boolean { 110 | if (util.isUndefined(value)) { 111 | const v = this.dict.remove(key); 112 | return !util.isUndefined(v); 113 | } 114 | const array = this.dict.getValue(key); 115 | if (!util.isUndefined(array) && arrays.remove(array, value, this.equalsF)) { 116 | if (array.length === 0) { 117 | this.dict.remove(key); 118 | } 119 | return true; 120 | } 121 | return false; 122 | } 123 | 124 | /** 125 | * Returns an array containing all of the keys in this dictionary. 126 | * @return {Array} an array containing all of the keys in this dictionary. 127 | */ 128 | keys(): K[] { 129 | return this.dict.keys(); 130 | } 131 | 132 | /** 133 | * Returns an array containing all of the values in this dictionary. 134 | * @return {Array} an array containing all of the values in this dictionary. 135 | */ 136 | values(): V[] { 137 | const values = this.dict.values(); 138 | const array: Array = []; 139 | for (const v of values) { 140 | for (const w of v) { 141 | array.push(w); 142 | } 143 | } 144 | return array; 145 | } 146 | 147 | /** 148 | * Returns true if this dictionary at least one value associatted the specified key. 149 | * @param {Object} key key whose presence in this dictionary is to be 150 | * tested. 151 | * @return {boolean} true if this dictionary at least one value associatted 152 | * the specified key. 153 | */ 154 | containsKey(key: K): boolean { 155 | return this.dict.containsKey(key); 156 | } 157 | 158 | /** 159 | * Removes all mappings from this dictionary. 160 | */ 161 | clear(): void { 162 | this.dict.clear(); 163 | } 164 | 165 | /** 166 | * Returns the number of keys in this dictionary. 167 | * @return {number} the number of key-value mappings in this dictionary. 168 | */ 169 | size(): number { 170 | return this.dict.size(); 171 | } 172 | 173 | /** 174 | * Returns true if this dictionary contains no mappings. 175 | * @return {boolean} true if this dictionary contains no mappings. 176 | */ 177 | isEmpty(): boolean { 178 | return this.dict.isEmpty(); 179 | } 180 | }// end of multi dictionary 181 | -------------------------------------------------------------------------------- /src/lib/PriorityQueue.ts: -------------------------------------------------------------------------------- 1 | import * as util from './util'; 2 | import Heap from './Heap'; 3 | 4 | export default class PriorityQueue { 5 | 6 | private heap: Heap; 7 | /** 8 | * Creates an empty priority queue. 9 | * @class

In a priority queue each element is associated with a "priority", 10 | * elements are dequeued in highest-priority-first order (the elements with the 11 | * highest priority are dequeued first). Priority Queues are implemented as heaps. 12 | * If the inserted elements are custom objects a compare function must be provided, 13 | * otherwise the <=, === and >= operators are used to compare object priority.

14 | *
 15 |      * function compare(a, b) {
 16 |      *  if (a is less than b by some ordering criterion) {
 17 |      *     return -1;
 18 |      *  } if (a is greater than b by the ordering criterion) {
 19 |      *     return 1;
 20 |      *  }
 21 |      *  // a must be equal to b
 22 |      *  return 0;
 23 |      * }
 24 |      * 
25 | * @constructor 26 | * @param {function(Object,Object):number=} compareFunction optional 27 | * function used to compare two element priorities. Must return a negative integer, 28 | * zero, or a positive integer as the first argument is less than, equal to, 29 | * or greater than the second. 30 | */ 31 | constructor(compareFunction?: util.ICompareFunction) { 32 | this.heap = new Heap(util.reverseCompareFunction(compareFunction)); 33 | } 34 | 35 | /** 36 | * Inserts the specified element into this priority queue. 37 | * @param {Object} element the element to insert. 38 | * @return {boolean} true if the element was inserted, or false if it is undefined. 39 | */ 40 | enqueue(element: T): boolean { 41 | return this.heap.add(element); 42 | } 43 | 44 | /** 45 | * Inserts the specified element into this priority queue. 46 | * @param {Object} element the element to insert. 47 | * @return {boolean} true if the element was inserted, or false if it is undefined. 48 | */ 49 | add(element: T): boolean { 50 | return this.heap.add(element); 51 | } 52 | 53 | /** 54 | * Retrieves and removes the highest priority element of this queue. 55 | * @return {*} the the highest priority element of this queue, 56 | * or undefined if this queue is empty. 57 | */ 58 | dequeue(): T | undefined { 59 | if (this.heap.size() !== 0) { 60 | const el = this.heap.peek(); 61 | this.heap.removeRoot(); 62 | return el; 63 | } 64 | return undefined; 65 | } 66 | 67 | /** 68 | * Retrieves, but does not remove, the highest priority element of this queue. 69 | * @return {*} the highest priority element of this queue, or undefined if this queue is empty. 70 | */ 71 | peek(): T | undefined { 72 | return this.heap.peek(); 73 | } 74 | 75 | /** 76 | * Returns true if this priority queue contains the specified element. 77 | * @param {Object} element element to search for. 78 | * @return {boolean} true if this priority queue contains the specified element, 79 | * false otherwise. 80 | */ 81 | contains(element: T): boolean { 82 | return this.heap.contains(element); 83 | } 84 | 85 | /** 86 | * Checks if this priority queue is empty. 87 | * @return {boolean} true if and only if this priority queue contains no items; false 88 | * otherwise. 89 | */ 90 | isEmpty(): boolean { 91 | return this.heap.isEmpty(); 92 | } 93 | 94 | /** 95 | * Returns the number of elements in this priority queue. 96 | * @return {number} the number of elements in this priority queue. 97 | */ 98 | size(): number { 99 | return this.heap.size(); 100 | } 101 | 102 | /** 103 | * Removes all of the elements from this priority queue. 104 | */ 105 | clear(): void { 106 | this.heap.clear(); 107 | } 108 | 109 | /** 110 | * Executes the provided function once for each element present in this queue in 111 | * no particular order. 112 | * @param {function(Object):*} callback function to execute, it is 113 | * invoked with one argument: the element value, to break the iteration you can 114 | * optionally return false. 115 | */ 116 | forEach(callback: util.ILoopFunction) { 117 | this.heap.forEach(callback); 118 | } 119 | 120 | } // end of priority queue 121 | -------------------------------------------------------------------------------- /src/lib/Queue.ts: -------------------------------------------------------------------------------- 1 | import * as util from './util'; 2 | import LinkedList from './LinkedList'; 3 | 4 | export default class Queue { 5 | 6 | /** 7 | * List containing the elements. 8 | * @type collections.LinkedList 9 | * @private 10 | */ 11 | private list: LinkedList; 12 | 13 | /** 14 | * Creates an empty queue. 15 | * @class A queue is a First-In-First-Out (FIFO) data structure, the first 16 | * element added to the queue will be the first one to be removed. This 17 | * implementation uses a linked list as a container. 18 | * @constructor 19 | */ 20 | constructor() { 21 | this.list = new LinkedList(); 22 | } 23 | 24 | 25 | /** 26 | * Inserts the specified element into the end of this queue. 27 | * @param {Object} elem the element to insert. 28 | * @return {boolean} true if the element was inserted, or false if it is undefined. 29 | */ 30 | enqueue(elem: T): boolean { 31 | return this.list.add(elem); 32 | } 33 | /** 34 | * Inserts the specified element into the end of this queue. 35 | * @param {Object} elem the element to insert. 36 | * @return {boolean} true if the element was inserted, or false if it is undefined. 37 | */ 38 | add(elem: T): boolean { 39 | return this.list.add(elem); 40 | } 41 | /** 42 | * Retrieves and removes the head of this queue. 43 | * @return {*} the head of this queue, or undefined if this queue is empty. 44 | */ 45 | dequeue(): T | undefined { 46 | if (this.list.size() !== 0) { 47 | const el = this.list.first(); 48 | this.list.removeElementAtIndex(0); 49 | return el; 50 | } 51 | return undefined; 52 | } 53 | /** 54 | * Retrieves, but does not remove, the head of this queue. 55 | * @return {*} the head of this queue, or undefined if this queue is empty. 56 | */ 57 | peek(): T | undefined { 58 | 59 | if (this.list.size() !== 0) { 60 | return this.list.first(); 61 | } 62 | return undefined; 63 | } 64 | 65 | /** 66 | * Returns the number of elements in this queue. 67 | * @return {number} the number of elements in this queue. 68 | */ 69 | size(): number { 70 | return this.list.size(); 71 | } 72 | 73 | /** 74 | * Returns true if this queue contains the specified element. 75 | *

If the elements inside this stack are 76 | * not comparable with the === operator, a custom equals function should be 77 | * provided to perform searches, the function must receive two arguments and 78 | * return true if they are equal, false otherwise. Example:

79 | * 80 | *
 81 |      * const petsAreEqualByName (pet1, pet2) {
 82 |      *  return pet1.name === pet2.name;
 83 |      * }
 84 |      * 
85 | * @param {Object} elem element to search for. 86 | * @param {function(Object,Object):boolean=} equalsFunction optional 87 | * function to check if two elements are equal. 88 | * @return {boolean} true if this queue contains the specified element, 89 | * false otherwise. 90 | */ 91 | contains(elem: T, equalsFunction?: util.IEqualsFunction): boolean { 92 | return this.list.contains(elem, equalsFunction); 93 | } 94 | 95 | /** 96 | * Checks if this queue is empty. 97 | * @return {boolean} true if and only if this queue contains no items; false 98 | * otherwise. 99 | */ 100 | isEmpty(): boolean { 101 | return this.list.size() <= 0; 102 | } 103 | 104 | /** 105 | * Removes all of the elements from this queue. 106 | */ 107 | clear(): void { 108 | this.list.clear(); 109 | } 110 | 111 | /** 112 | * Executes the provided function once for each element present in this queue in 113 | * FIFO order. 114 | * @param {function(Object):*} callback function to execute, it is 115 | * invoked with one argument: the element value, to break the iteration you can 116 | * optionally return false. 117 | */ 118 | forEach(callback: util.ILoopFunction) { 119 | this.list.forEach(callback); 120 | } 121 | 122 | } // End of queue 123 | -------------------------------------------------------------------------------- /src/lib/Set.ts: -------------------------------------------------------------------------------- 1 | import * as util from './util'; 2 | 3 | import * as arrays from './arrays'; 4 | 5 | import Dictionary from './Dictionary'; 6 | 7 | export default class Set { 8 | 9 | /** 10 | * Dictionary key and value holds the elements in the set. 11 | * @type {Object} 12 | * @protected 13 | */ 14 | protected dictionary: Dictionary; 15 | 16 | /** 17 | * Creates an empty set. 18 | * @class

A set is a data structure that contains no duplicate items.

19 | *

If the inserted elements are custom objects a function 20 | * which converts elements to strings must be provided. Example:

21 | * 22 | *
 23 |      * function petToString(pet) {
 24 |      *  return pet.name;
 25 |      * }
 26 |      * 
27 | * 28 | * @constructor 29 | * @param {function(Object):string=} toStringFunction optional function used 30 | * to convert elements to strings. If the elements aren't strings or if toString() 31 | * is not appropriate, a custom function which receives an object and returns a 32 | * unique string must be provided. 33 | */ 34 | constructor(toStringFunction?: (item: T) => string) { 35 | this.dictionary = new Dictionary(toStringFunction); 36 | } 37 | 38 | 39 | 40 | /** 41 | * Returns true if this set contains the specified element. 42 | * @param {Object} element element to search for. 43 | * @return {boolean} true if this set contains the specified element, 44 | * false otherwise. 45 | */ 46 | contains(element: T): boolean { 47 | return this.dictionary.containsKey(element); 48 | } 49 | 50 | /** 51 | * Adds the specified element to this set if it is not already present. 52 | * @param {Object} element the element to insert. 53 | * @return {boolean} true if this set did not already contain the specified element. 54 | */ 55 | add(element: T): boolean { 56 | if (this.contains(element) || util.isUndefined(element)) { 57 | return false; 58 | } else { 59 | this.dictionary.setValue(element, element); 60 | return true; 61 | } 62 | } 63 | 64 | /** 65 | * Performs an intersection between this and another set. 66 | * Removes all values that are not present this set and the given set. 67 | * @param {collections.Set} otherSet other set. 68 | */ 69 | intersection(otherSet: Set): void { 70 | const set = this; 71 | this.forEach(function(element: T): boolean { 72 | if (!otherSet.contains(element)) { 73 | set.remove(element); 74 | } 75 | return true; 76 | }); 77 | } 78 | 79 | /** 80 | * Performs a union between this and another set. 81 | * Adds all values from the given set to this set. 82 | * @param {collections.Set} otherSet other set. 83 | */ 84 | union(otherSet: Set): void { 85 | const set = this; 86 | otherSet.forEach(function(element: T): boolean { 87 | set.add(element); 88 | return true; 89 | }); 90 | } 91 | 92 | /** 93 | * Performs a difference between this and another set. 94 | * Removes from this set all the values that are present in the given set. 95 | * @param {collections.Set} otherSet other set. 96 | */ 97 | difference(otherSet: Set): void { 98 | const set = this; 99 | otherSet.forEach(function(element: T): boolean { 100 | set.remove(element); 101 | return true; 102 | }); 103 | } 104 | 105 | /** 106 | * Checks whether the given set contains all the elements in this set. 107 | * @param {collections.Set} otherSet other set. 108 | * @return {boolean} true if this set is a subset of the given set. 109 | */ 110 | isSubsetOf(otherSet: Set): boolean { 111 | 112 | if (this.size() > otherSet.size()) { 113 | return false; 114 | } 115 | 116 | let isSub = true; 117 | this.forEach(function(element) { 118 | if (!otherSet.contains(element)) { 119 | isSub = false; 120 | return false; 121 | } 122 | return true; 123 | }); 124 | return isSub; 125 | } 126 | 127 | /** 128 | * Removes the specified element from this set if it is present. 129 | * @return {boolean} true if this set contained the specified element. 130 | */ 131 | remove(element: T): boolean { 132 | if (!this.contains(element)) { 133 | return false; 134 | } else { 135 | this.dictionary.remove(element); 136 | return true; 137 | } 138 | } 139 | 140 | /** 141 | * Executes the provided function once for each element 142 | * present in this set. 143 | * @param {function(Object):*} callback function to execute, it is 144 | * invoked with one arguments: the element. To break the iteration you can 145 | * optionally return false. 146 | */ 147 | forEach(callback: util.ILoopFunction): void { 148 | this.dictionary.forEach(function(k, v) { 149 | return callback(v); 150 | }); 151 | } 152 | 153 | /** 154 | * Returns an array containing all of the elements in this set in arbitrary order. 155 | * @return {Array} an array containing all of the elements in this set. 156 | */ 157 | toArray(): T[] { 158 | return this.dictionary.values(); 159 | } 160 | 161 | /** 162 | * Returns true if this set contains no elements. 163 | * @return {boolean} true if this set contains no elements. 164 | */ 165 | isEmpty(): boolean { 166 | return this.dictionary.isEmpty(); 167 | } 168 | 169 | /** 170 | * Returns the number of elements in this set. 171 | * @return {number} the number of elements in this set. 172 | */ 173 | size(): number { 174 | return this.dictionary.size(); 175 | } 176 | 177 | /** 178 | * Removes all of the elements from this set. 179 | */ 180 | clear(): void { 181 | this.dictionary.clear(); 182 | } 183 | 184 | /* 185 | * Provides a string representation for display 186 | */ 187 | toString(): string { 188 | return arrays.toString(this.toArray()); 189 | } 190 | }// end of Set 191 | -------------------------------------------------------------------------------- /src/lib/Stack.ts: -------------------------------------------------------------------------------- 1 | import LinkedList from './LinkedList'; 2 | import * as util from './util'; 3 | 4 | export default class Stack { 5 | /** 6 | * List containing the elements. 7 | * @type collections.LinkedList 8 | * @private 9 | */ 10 | private list: LinkedList; 11 | /** 12 | * Creates an empty Stack. 13 | * @class A Stack is a Last-In-First-Out (LIFO) data structure, the last 14 | * element added to the stack will be the first one to be removed. This 15 | * implementation uses a linked list as a container. 16 | * @constructor 17 | */ 18 | constructor() { 19 | this.list = new LinkedList(); 20 | } 21 | 22 | /** 23 | * Pushes an item onto the top of this stack. 24 | * @param {Object} elem the element to be pushed onto this stack. 25 | * @return {boolean} true if the element was pushed or false if it is undefined. 26 | */ 27 | push(elem: T) { 28 | return this.list.add(elem, 0); 29 | } 30 | /** 31 | * Pushes an item onto the top of this stack. 32 | * @param {Object} elem the element to be pushed onto this stack. 33 | * @return {boolean} true if the element was pushed or false if it is undefined. 34 | */ 35 | add(elem: T) { 36 | return this.list.add(elem, 0); 37 | } 38 | /** 39 | * Removes the object at the top of this stack and returns that object. 40 | * @return {*} the object at the top of this stack or undefined if the 41 | * stack is empty. 42 | */ 43 | pop(): T | undefined { 44 | return this.list.removeElementAtIndex(0); 45 | } 46 | /** 47 | * Looks at the object at the top of this stack without removing it from the 48 | * stack. 49 | * @return {*} the object at the top of this stack or undefined if the 50 | * stack is empty. 51 | */ 52 | peek(): T | undefined { 53 | return this.list.first(); 54 | } 55 | /** 56 | * Returns the number of elements in this stack. 57 | * @return {number} the number of elements in this stack. 58 | */ 59 | size(): number { 60 | return this.list.size(); 61 | } 62 | 63 | /** 64 | * Returns true if this stack contains the specified element. 65 | *

If the elements inside this stack are 66 | * not comparable with the === operator, a custom equals function should be 67 | * provided to perform searches, the function must receive two arguments and 68 | * return true if they are equal, false otherwise. Example:

69 | * 70 | *
 71 |      * const petsAreEqualByName (pet1, pet2) {
 72 |      *  return pet1.name === pet2.name;
 73 |      * }
 74 |      * 
75 | * @param {Object} elem element to search for. 76 | * @param {function(Object,Object):boolean=} equalsFunction optional 77 | * function to check if two elements are equal. 78 | * @return {boolean} true if this stack contains the specified element, 79 | * false otherwise. 80 | */ 81 | contains(elem: T, equalsFunction?: util.IEqualsFunction) { 82 | return this.list.contains(elem, equalsFunction); 83 | } 84 | /** 85 | * Checks if this stack is empty. 86 | * @return {boolean} true if and only if this stack contains no items; false 87 | * otherwise. 88 | */ 89 | isEmpty(): boolean { 90 | return this.list.isEmpty(); 91 | } 92 | /** 93 | * Removes all of the elements from this stack. 94 | */ 95 | clear(): void { 96 | this.list.clear(); 97 | } 98 | 99 | /** 100 | * Executes the provided function once for each element present in this stack in 101 | * LIFO order. 102 | * @param {function(Object):*} callback function to execute, it is 103 | * invoked with one argument: the element value, to break the iteration you can 104 | * optionally return false. 105 | */ 106 | forEach(callback: util.ILoopFunction) { 107 | this.list.forEach(callback); 108 | } 109 | } // End of stack 110 | -------------------------------------------------------------------------------- /src/lib/arrays.ts: -------------------------------------------------------------------------------- 1 | import * as util from './util'; 2 | 3 | /** 4 | * Returns the position of the first occurrence of the specified item 5 | * within the specified array.4 6 | * @param {*} array the array in which to search the element. 7 | * @param {Object} item the element to search. 8 | * @param {function(Object,Object):boolean=} equalsFunction optional function used to 9 | * check equality between 2 elements. 10 | * @return {number} the position of the first occurrence of the specified element 11 | * within the specified array, or -1 if not found. 12 | */ 13 | export function indexOf(array: T[], item: T, equalsFunction?: util.IEqualsFunction): number { 14 | const equals = equalsFunction || util.defaultEquals; 15 | const length = array.length; 16 | for (let i = 0; i < length; i++) { 17 | if (equals(array[i], item)) { 18 | return i; 19 | } 20 | } 21 | return -1; 22 | } 23 | 24 | /** 25 | * Returns the position of the last occurrence of the specified element 26 | * within the specified array. 27 | * @param {*} array the array in which to search the element. 28 | * @param {Object} item the element to search. 29 | * @param {function(Object,Object):boolean=} equalsFunction optional function used to 30 | * check equality between 2 elements. 31 | * @return {number} the position of the last occurrence of the specified element 32 | * within the specified array or -1 if not found. 33 | */ 34 | export function lastIndexOf(array: T[], item: T, equalsFunction?: util.IEqualsFunction): number { 35 | const equals = equalsFunction || util.defaultEquals; 36 | const length = array.length; 37 | for (let i = length - 1; i >= 0; i--) { 38 | if (equals(array[i], item)) { 39 | return i; 40 | } 41 | } 42 | return -1; 43 | } 44 | 45 | /** 46 | * Returns true if the specified array contains the specified element. 47 | * @param {*} array the array in which to search the element. 48 | * @param {Object} item the element to search. 49 | * @param {function(Object,Object):boolean=} equalsFunction optional function to 50 | * check equality between 2 elements. 51 | * @return {boolean} true if the specified array contains the specified element. 52 | */ 53 | export function contains(array: T[], item: T, equalsFunction?: util.IEqualsFunction): boolean { 54 | return indexOf(array, item, equalsFunction) >= 0; 55 | } 56 | 57 | 58 | /** 59 | * Removes the first ocurrence of the specified element from the specified array. 60 | * @param {*} array the array in which to search element. 61 | * @param {Object} item the element to search. 62 | * @param {function(Object,Object):boolean=} equalsFunction optional function to 63 | * check equality between 2 elements. 64 | * @return {boolean} true if the array changed after this call. 65 | */ 66 | export function remove(array: T[], item: T, equalsFunction?: util.IEqualsFunction): boolean { 67 | const index = indexOf(array, item, equalsFunction); 68 | if (index < 0) { 69 | return false; 70 | } 71 | array.splice(index, 1); 72 | return true; 73 | } 74 | 75 | /** 76 | * Returns the number of elements in the specified array equal 77 | * to the specified object. 78 | * @param {Array} array the array in which to determine the frequency of the element. 79 | * @param {Object} item the element whose frequency is to be determined. 80 | * @param {function(Object,Object):boolean=} equalsFunction optional function used to 81 | * check equality between 2 elements. 82 | * @return {number} the number of elements in the specified array 83 | * equal to the specified object. 84 | */ 85 | export function frequency(array: T[], item: T, equalsFunction?: util.IEqualsFunction): number { 86 | const equals = equalsFunction || util.defaultEquals; 87 | const length = array.length; 88 | let freq = 0; 89 | for (let i = 0; i < length; i++) { 90 | if (equals(array[i], item)) { 91 | freq++; 92 | } 93 | } 94 | return freq; 95 | } 96 | 97 | /** 98 | * Returns true if the two specified arrays are equal to one another. 99 | * Two arrays are considered equal if both arrays contain the same number 100 | * of elements, and all corresponding pairs of elements in the two 101 | * arrays are equal and are in the same order. 102 | * @param {Array} array1 one array to be tested for equality. 103 | * @param {Array} array2 the other array to be tested for equality. 104 | * @param {function(Object,Object):boolean=} equalsFunction optional function used to 105 | * check equality between elemements in the arrays. 106 | * @return {boolean} true if the two arrays are equal 107 | */ 108 | export function equals(array1: T[], array2: T[], equalsFunction?: util.IEqualsFunction): boolean { 109 | const equals = equalsFunction || util.defaultEquals; 110 | 111 | if (array1.length !== array2.length) { 112 | return false; 113 | } 114 | const length = array1.length; 115 | for (let i = 0; i < length; i++) { 116 | if (!equals(array1[i], array2[i])) { 117 | return false; 118 | } 119 | } 120 | return true; 121 | } 122 | 123 | /** 124 | * Returns shallow a copy of the specified array. 125 | * @param {*} array the array to copy. 126 | * @return {Array} a copy of the specified array 127 | */ 128 | export function copy(array: T[]): T[] { 129 | return array.concat(); 130 | } 131 | 132 | /** 133 | * Swaps the elements at the specified positions in the specified array. 134 | * @param {Array} array The array in which to swap elements. 135 | * @param {number} i the index of one element to be swapped. 136 | * @param {number} j the index of the other element to be swapped. 137 | * @return {boolean} true if the array is defined and the indexes are valid. 138 | */ 139 | export function swap(array: T[], i: number, j: number): boolean { 140 | if (i < 0 || i >= array.length || j < 0 || j >= array.length) { 141 | return false; 142 | } 143 | const temp = array[i]; 144 | array[i] = array[j]; 145 | array[j] = temp; 146 | return true; 147 | } 148 | 149 | export function toString(array: T[]): string { 150 | return '[' + array.toString() + ']'; 151 | } 152 | 153 | /** 154 | * Executes the provided function once for each element present in this array 155 | * starting from index 0 to length - 1. 156 | * @param {Array} array The array in which to iterate. 157 | * @param {function(Object):*} callback function to execute, it is 158 | * invoked with one argument: the element value, to break the iteration you can 159 | * optionally return false. 160 | */ 161 | export function forEach(array: T[], callback: util.ILoopFunction): void { 162 | for (const ele of array) { 163 | if (callback(ele) === false) { 164 | return; 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Basarat Ali Syed. All Rights Reserved. 2 | // 3 | // Licensed under MIT open source license http://opensource.org/licenses/MIT 4 | // 5 | // Orginal javascript code was by Mauricio Santos 6 | // 7 | import * as _arrays from './arrays'; 8 | export var arrays = _arrays; 9 | export {default as Bag} from './Bag'; 10 | export {default as BSTree} from './BSTree'; 11 | export {default as BSTreeKV} from './BSTreeKV'; 12 | export {default as Dictionary} from './Dictionary'; 13 | export {default as Heap} from './Heap'; 14 | export {default as LinkedDictionary} from './LinkedDictionary'; 15 | export {default as LinkedList} from './LinkedList'; 16 | export {default as MultiDictionary} from './MultiDictionary'; 17 | export {default as FactoryDictionary} from './FactoryDictionary'; 18 | export {default as DefaultDictionary} from './FactoryDictionary'; 19 | export {default as Queue} from './Queue'; 20 | export {default as PriorityQueue} from './PriorityQueue'; 21 | export {default as Set} from './Set'; 22 | export {default as Stack} from './Stack'; 23 | export {default as MultiRootTree} from './MultiRootTree'; 24 | import * as _util from './util'; 25 | export var util = _util; 26 | -------------------------------------------------------------------------------- /src/lib/util.ts: -------------------------------------------------------------------------------- 1 | const _hasOwnProperty = Object.prototype.hasOwnProperty; 2 | export const has = function(obj: any, prop: any) { 3 | return _hasOwnProperty.call(obj, prop); 4 | }; 5 | 6 | /** 7 | * Function signature for comparing 8 | * <0 means a is smaller 9 | * = 0 means they are equal 10 | * >0 means a is larger 11 | */ 12 | export interface ICompareFunction { 13 | (a: T, b: T): number; 14 | } 15 | 16 | /** 17 | * Function signature for checking equality 18 | */ 19 | export interface IEqualsFunction { 20 | (a: T, b: T): boolean; 21 | } 22 | 23 | /** 24 | * Function signature for Iterations. Return false to break from loop 25 | */ 26 | export interface ILoopFunction { 27 | (a: T): boolean | void; 28 | } 29 | 30 | /** 31 | * Default function to compare element order. 32 | * @function 33 | */ 34 | export function defaultCompare(a: T, b: T): number { 35 | if (a < b) { 36 | return -1; 37 | } else if (a === b) { 38 | return 0; 39 | } else { 40 | return 1; 41 | } 42 | } 43 | 44 | /** 45 | * Default function to test equality. 46 | * @function 47 | */ 48 | export function defaultEquals(a: T, b: T): boolean { 49 | return a === b; 50 | } 51 | 52 | /** 53 | * Default function to convert an object to a string. 54 | * @function 55 | */ 56 | export function defaultToString(item: any): string { 57 | if (item === null) { 58 | return 'COLLECTION_NULL'; 59 | } else if (isUndefined(item)) { 60 | return 'COLLECTION_UNDEFINED'; 61 | } else if (isString(item)) { 62 | return '$s' + item; 63 | } else { 64 | return '$o' + item.toString(); 65 | } 66 | } 67 | 68 | /** 69 | * Joins all the properies of the object using the provided join string 70 | */ 71 | export function makeString(item: T, join: string = ','): string { 72 | if (item === null) { 73 | return 'COLLECTION_NULL'; 74 | } else if (isUndefined(item)) { 75 | return 'COLLECTION_UNDEFINED'; 76 | } else if (isString(item)) { 77 | return item.toString(); 78 | } else { 79 | let toret = '{'; 80 | let first = true; 81 | for (const prop in item) { 82 | if (has(item, prop)) { 83 | if (first) { 84 | first = false; 85 | } else { 86 | toret = toret + join; 87 | } 88 | toret = toret + prop + ':' + (item)[prop]; 89 | } 90 | } 91 | return toret + '}'; 92 | } 93 | } 94 | 95 | /** 96 | * Checks if the given argument is a function. 97 | * @function 98 | */ 99 | export function isFunction(func: any): boolean { 100 | return (typeof func) === 'function'; 101 | } 102 | 103 | /** 104 | * Checks if the given argument is undefined. 105 | * @function 106 | */ 107 | export function isUndefined(obj: any): obj is undefined { 108 | return (typeof obj) === 'undefined'; 109 | } 110 | 111 | /** 112 | * Checks if the given argument is a string. 113 | * @function 114 | */ 115 | export function isString(obj: any): boolean { 116 | return Object.prototype.toString.call(obj) === '[object String]'; 117 | } 118 | 119 | /** 120 | * Reverses a compare function. 121 | * @function 122 | */ 123 | export function reverseCompareFunction(compareFunction?: ICompareFunction): ICompareFunction { 124 | if (isUndefined(compareFunction) || !isFunction(compareFunction)) { 125 | return function(a, b) { 126 | if (a < b) { 127 | return 1; 128 | } else if (a === b) { 129 | return 0; 130 | } else { 131 | return -1; 132 | } 133 | }; 134 | } else { 135 | return function(d: T, v: T) { 136 | return compareFunction(d, v) * -1; 137 | }; 138 | } 139 | } 140 | 141 | /** 142 | * Returns an equal function given a compare function. 143 | * @function 144 | */ 145 | export function compareToEquals(compareFunction: ICompareFunction): IEqualsFunction { 146 | return function(a: T, b: T) { 147 | return compareFunction(a, b) === 0; 148 | }; 149 | } 150 | -------------------------------------------------------------------------------- /src/test/arraysTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Arrays', 7 | function() { 8 | 9 | it('IndexOf returns the right position', 10 | function() { 11 | var a = [1, 8, 10]; 12 | expect(collections.arrays.indexOf(a, 1)).equals(0); 13 | expect(collections.arrays.indexOf(a, 8)).equals(1); 14 | expect(collections.arrays.indexOf(a, 10)).equals(2); 15 | expect(collections.arrays.indexOf(a, 11)).equals(- 1); 16 | expect(collections.arrays.indexOf([], 8)).equals(- 1); 17 | }); 18 | 19 | it('IndexOf with custom equals function returns the right position', 20 | function() { 21 | var b = { 22 | val: 1 23 | }; 24 | var c = { 25 | val: 8 26 | }; 27 | var d = { 28 | val: 10 29 | }; 30 | var e = { 31 | val: 11 32 | }; 33 | var a = [b, c, d]; 34 | 35 | var eq = function(arg1: any, arg2: any) { 36 | return arg1.val === arg2.val; 37 | }; 38 | 39 | expect(collections.arrays.indexOf(a, { 40 | val: 1 41 | })).equals(- 1); 42 | expect(collections.arrays.indexOf(a, { 43 | val: 1 44 | }, 45 | eq)).equals(0); 46 | expect(collections.arrays.indexOf(a, c, eq)).equals(1); 47 | expect(collections.arrays.indexOf(a, { 48 | val: 10 49 | }, 50 | eq)).equals(2); 51 | expect(collections.arrays.indexOf(a, e, eq)).equals(- 1); 52 | expect(collections.arrays.indexOf([], b)).equals(- 1); 53 | }); 54 | 55 | it('lastIndexOf returns the right position', 56 | function() { 57 | var a = [1, 8, 8, 8, 10, 10]; 58 | expect(collections.arrays.lastIndexOf(a, 1)).equals(0); 59 | expect(collections.arrays.lastIndexOf(a, 8)).equals(3); 60 | expect(collections.arrays.lastIndexOf(a, 10)).equals(5); 61 | expect(collections.arrays.lastIndexOf(a, 11)).equals(- 1); 62 | expect(collections.arrays.lastIndexOf([], 8)).equals(- 1); 63 | }); 64 | 65 | it('lastIndexOf with custom equals function returns the right position', 66 | function() { 67 | var b = { 68 | val: 1 69 | }; 70 | var c = { 71 | val: 8 72 | }; 73 | var d = { 74 | val: 10 75 | }; 76 | var e = { 77 | val: 11 78 | }; 79 | var a = [b, b, c, d]; 80 | 81 | var eq = function(arg1: any, arg2: any) { 82 | return arg1.val === arg2.val; 83 | }; 84 | 85 | expect(collections.arrays.lastIndexOf(a, { 86 | val: 1 87 | })).equals(- 1); 88 | expect(collections.arrays.lastIndexOf(a, { 89 | val: 1 90 | }, 91 | eq)).equals(1); 92 | }); 93 | 94 | it('Contains existing elements', 95 | function() { 96 | var a = [1, 8, 8, 8, 10, 10]; 97 | expect(collections.arrays.contains(a, 1)).equals(true); 98 | expect(collections.arrays.contains(a, 8)).equals(true); 99 | expect(collections.arrays.contains(a, 10)).equals(true); 100 | expect(collections.arrays.contains(a, 11)).equals(false); 101 | expect(collections.arrays.contains([], 8)).equals(false); 102 | }); 103 | 104 | it('Contains existing elements with custom equals function', 105 | function() { 106 | var b = { 107 | val: 1 108 | }; 109 | var c = { 110 | val: 8 111 | }; 112 | var d = { 113 | val: 10 114 | }; 115 | var e = { 116 | val: 11 117 | }; 118 | var a = [b, b, c, d]; 119 | 120 | var eq = function(arg1: any, arg2: any) { 121 | return arg1.val === arg2.val; 122 | }; 123 | 124 | expect(collections.arrays.contains(a, { 125 | val: 1 126 | })).equals(false); 127 | expect(collections.arrays.contains(a, { 128 | val: 1 129 | }, 130 | eq)).equals(true); 131 | expect(collections.arrays.contains(a, { 132 | val: 8 133 | }, 134 | eq)).equals(true); 135 | expect(collections.arrays.contains(a, { 136 | val: 10 137 | }, 138 | eq)).equals(true); 139 | expect(collections.arrays.contains(a, { 140 | val: 11 141 | }, 142 | eq)).equals(false); 143 | expect(collections.arrays.contains([], { 144 | val: 11 145 | }, 146 | eq)).equals(false); 147 | }); 148 | 149 | it('Gives the right frequency', 150 | function() { 151 | var a = [1, 8, 8, 8, 10, 10]; 152 | expect(collections.arrays.frequency(a, 1)).equals(1); 153 | expect(collections.arrays.frequency(a, 8)).equals(3); 154 | expect(collections.arrays.frequency(a, 10)).equals(2); 155 | expect(collections.arrays.frequency(a, 11)).equals(0); 156 | }); 157 | 158 | it('Gives the right frequency with custom equals', 159 | function() { 160 | var b = { 161 | val: 1 162 | }; 163 | var c = { 164 | val: 8 165 | }; 166 | var d = { 167 | val: 10 168 | }; 169 | var e = { 170 | val: 11 171 | }; 172 | var a = [b, b, c, d]; 173 | 174 | var eq = function(arg1: any, arg2: any) { 175 | return arg1.val === arg2.val; 176 | }; 177 | expect(collections.arrays.frequency(a, { 178 | val: 1 179 | })).equals(0); 180 | expect(collections.arrays.frequency(a, { 181 | val: 1 182 | }, 183 | eq)).equals(2); 184 | expect(collections.arrays.frequency(a, { 185 | val: 8 186 | }, 187 | eq)).equals(1); 188 | }); 189 | 190 | it('Equal arrays are equal', 191 | function() { 192 | var a = [1, 8, 8, 8, 10, 10]; 193 | var b = [1, 8, 8, 8, 10, 10]; 194 | var c = [1, 8, 5, 8, 10, 10]; 195 | var d = [1, 8, 8, 8, 10]; 196 | 197 | expect(collections.arrays.equals(a, a)).equals(true); 198 | expect(collections.arrays.equals(a, b)).equals(true); 199 | expect(collections.arrays.equals(a, [])).equals(false); 200 | expect(collections.arrays.equals(a, c)).equals(false); 201 | expect(collections.arrays.equals(a, d)).equals(false); 202 | expect(collections.arrays.equals(a, [])).equals(false); 203 | }); 204 | 205 | it('Equal arrays are equal with custom equals function', 206 | function() { 207 | var a = [{ 208 | val: 8 209 | }]; 210 | var b = [{ 211 | val: 8 212 | }]; 213 | 214 | var eq = function(arg1: any, arg2: any) { 215 | return arg1.val === arg2.val; 216 | }; 217 | 218 | expect(collections.arrays.equals(a, a)).equals(true); 219 | expect(collections.arrays.equals(a, a, eq)).equals(true); 220 | expect(collections.arrays.equals(a, b, eq)).equals(true); 221 | expect(collections.arrays.equals(a, b)).equals(false); 222 | }); 223 | 224 | it('Removes elements', 225 | function() { 226 | var a: any = []; 227 | expect(collections.arrays.remove(a, 1)).equals(false); 228 | a = [4, 9, 9, 10]; 229 | expect(collections.arrays.remove(a, 9)).equals(true); 230 | expect(collections.arrays.indexOf(a, 9)).equals(1); 231 | expect(collections.arrays.indexOf(a, 10)).equals(2); 232 | expect(collections.arrays.remove(a, 9)).equals(true); 233 | expect(collections.arrays.remove(a, 9)).equals(false); 234 | expect(collections.arrays.remove(a, 9)).equals(false); 235 | }); 236 | 237 | it('Removes elements with custom equals function', 238 | function() { 239 | var c = { 240 | val: 8 241 | }; 242 | var d = { 243 | val: 10 244 | }; 245 | var eq = function(arg1: any, arg2: any) { 246 | return arg1.val === arg2.val; 247 | }; 248 | 249 | var a = [c, d]; 250 | expect(collections.arrays.remove(a, { 251 | val: 10 252 | })).equals(false); 253 | expect(collections.arrays.remove(a, { 254 | val: 10 255 | }, 256 | eq)).equals(true); 257 | }); 258 | 259 | it('For each gives the right ordering', 260 | function() { 261 | var a: any = []; 262 | collections.arrays.forEach(a, function(e) { 263 | expect(true).equals(false); // should not enter here 264 | }); 265 | 266 | for (var i = 0; i < 10; i++) { 267 | a.push(i); 268 | } 269 | 270 | var j = 0; 271 | collections.arrays.forEach(a, function(e) { 272 | expect(e).equals(j); 273 | j++; 274 | }); 275 | }); 276 | 277 | it('For each can be interrupted', 278 | function() { 279 | var a: any = []; 280 | var b: any = []; 281 | for (var i = 0; i < 5; i++) { 282 | a.push(i); 283 | } 284 | collections.arrays.forEach(a, function(e) { 285 | b.push(e); 286 | if (e === 3) { 287 | return false; 288 | } 289 | }); 290 | 291 | expect([0, 1, 2, 3]).to.deep.equal(b); 292 | }); 293 | 294 | it('Copies existing arrays', 295 | function() { 296 | var a = [1, 8, 8, 8, 10, 10]; 297 | var b = collections.arrays.copy(a); 298 | expect(collections.arrays.equals(a, b)).equals(true); 299 | expect(a === b).equals(false); 300 | }); 301 | 302 | it('Swaps elements', 303 | function() { 304 | var a = [1, 8, 8, 8, 10, 10]; 305 | expect(collections.arrays.swap(a, 0, 5)).equals(true); 306 | expect(a[0]).equals(10); 307 | expect(a[5]).equals(1); 308 | expect(collections.arrays.swap(a, 0, 6)).equals(false); 309 | expect(collections.arrays.swap(a, 7, 2)).equals(false); 310 | expect(collections.arrays.swap(a, -1, 9)).equals(false); 311 | }); 312 | 313 | }); 314 | -------------------------------------------------------------------------------- /src/test/bagTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Bag', 7 | function() { 8 | 9 | var bag: collections.Bag; 10 | 11 | beforeEach(function() { 12 | bag = new collections.Bag(); 13 | }); 14 | 15 | var toStringF: any = function(f: any) { 16 | return f.description; 17 | }; 18 | 19 | it('Gives the right size', 20 | function() { 21 | bag.add('a'); 22 | bag.add('b'); 23 | bag.add('c'); 24 | expect(bag.size()).equals(3); 25 | bag.add('d'); 26 | expect(bag.size()).equals(4); 27 | bag.remove('d'); 28 | expect(bag.size()).equals(3); 29 | bag.clear(); 30 | 31 | bag.add('a'); 32 | bag.add('b'); 33 | bag.add('c'); 34 | expect(bag.size()).equals(3); 35 | bag.add('d'); 36 | expect(bag.size()).equals(4); 37 | bag.remove('d'); 38 | expect(bag.size()).equals(3); 39 | }); 40 | 41 | it('Gives the right size with duplicated elements', 42 | function() { 43 | bag.add('a'); 44 | bag.add('a'); 45 | bag.add('b'); 46 | bag.add('b'); 47 | bag.add('c'); 48 | expect(bag.size()).equals(5); 49 | bag.remove('b'); 50 | bag.remove('a'); 51 | expect(bag.size()).equals(3); 52 | bag.remove('a'); 53 | bag.remove('b'); 54 | bag.remove('c'); 55 | expect(bag.size()).equals(0); 56 | }); 57 | 58 | it('Contains existing elements', 59 | function() { 60 | bag.add('a'); 61 | bag.add('b'); 62 | bag.add('c'); 63 | bag.add('c'); 64 | bag.add('d'); 65 | 66 | expect(bag.contains('a')).equals(true); 67 | expect(bag.contains('b')).equals(true); 68 | expect(bag.contains('c')).equals(true); 69 | expect(bag.contains('d')).equals(true); 70 | expect(bag.contains('e')).equals(false); 71 | bag.remove('c'); 72 | expect(bag.contains('c')).equals(true); 73 | bag.remove('c'); 74 | expect(bag.contains('c')).equals(false); 75 | bag.clear(); 76 | bag.add(1); 77 | bag.add(2); 78 | expect(bag.contains(1)).equals(true); 79 | expect(bag.contains(2)).equals(true); 80 | expect(bag.contains(3)).equals(false); 81 | }); 82 | 83 | it('Contains existing elements with custom toString function', 84 | function() { 85 | bag = new collections.Bag(toStringF); 86 | var fn1: any = function() { }; 87 | fn1.description = 'fn1'; 88 | expect(bag.contains(fn1)).equals(false); 89 | bag.add(fn1); 90 | expect(bag.contains(fn1)).equals(true); 91 | var fn2: any = function() { }; 92 | fn2.description = 'fn2'; 93 | expect(bag.contains(fn2)).equals(false); 94 | bag.add(fn2); 95 | bag.add(fn2); 96 | expect(bag.contains(fn2)).equals(true); 97 | expect(bag.size()).equals(3); 98 | bag.remove(fn2); 99 | expect(bag.contains(fn2)).equals(true); 100 | bag.remove(fn2); 101 | expect(bag.contains(fn2)).equals(false); 102 | }); 103 | 104 | it('An empty bag is empty', 105 | function() { 106 | expect(bag.isEmpty()).equals(true); 107 | bag.add(1); 108 | bag.add(1); 109 | expect(bag.isEmpty()).equals(false); 110 | bag.remove(1); 111 | expect(bag.isEmpty()).equals(false); 112 | bag.remove(1); 113 | expect(bag.isEmpty()).equals(true); 114 | }); 115 | 116 | it('Adds', 117 | function() { 118 | expect(bag.add('a')).equals(true); 119 | expect(bag.add('b')).equals(true); 120 | expect(bag.contains('a')).equals(true); 121 | expect(bag.contains('b')).equals(true); 122 | expect(bag.add('b')).equals(true); 123 | expect(bag.contains('b')).equals(true); 124 | expect(bag.add(null)).equals(true); 125 | expect(bag.contains(null)).equals(true); 126 | expect(bag.add(null)).equals(true); 127 | expect(bag.contains(undefined)).equals(false); 128 | expect(bag.add(undefined)).equals(false); 129 | expect(bag.contains(undefined)).equals(false); 130 | }); 131 | it('Adds multiple copies', 132 | function() { 133 | expect(bag.add('a', 1)).equals(true); 134 | expect(bag.add('a')).equals(true); 135 | expect(bag.add('b', 3)).equals(true); 136 | expect(bag.contains('a')).equals(true); 137 | expect(bag.contains('b')).equals(true); 138 | expect(bag.add('b')).equals(true); 139 | expect(bag.count('a')).equals(2); 140 | expect(bag.count('b')).equals(4); 141 | bag.remove('a'); 142 | bag.remove('a'); 143 | expect(bag.count('a')).equals(0); 144 | }); 145 | 146 | it('Removes', 147 | function() { 148 | expect(bag.add('a')).equals(true); 149 | expect(bag.add('a')).equals(true); 150 | expect(bag.add('b')).equals(true); 151 | expect(bag.remove('a')).equals(true); 152 | expect(bag.remove('a')).equals(true); 153 | expect(bag.size()).equals(1); 154 | expect(bag.remove('b')).equals(true); 155 | expect(bag.size()).equals(0); 156 | }); 157 | 158 | it('Removes multiple copies', 159 | function() { 160 | expect(bag.add('a', 1)).equals(true); 161 | expect(bag.add('a')).equals(true); 162 | expect(bag.add('b', 3)).equals(true); 163 | expect(bag.remove('b', 2)).equals(true); 164 | expect(bag.count('b')).equals(1); 165 | expect(bag.remove('b', 1)).equals(true); 166 | expect(bag.count('b')).equals(0); 167 | expect(bag.remove('a', 2)).equals(true); 168 | expect(bag.count('a')).equals(0); 169 | expect(bag.add('c', 3)).equals(true); 170 | expect(bag.remove('c', 5)).equals(true); 171 | expect(bag.count('a')).equals(0); 172 | expect(bag.size()).equals(0); 173 | }); 174 | 175 | it('Clear removes all elements', 176 | function() { 177 | expect(bag.add('b', 3)).equals(true); 178 | bag.clear(); 179 | expect(bag.count('b')).equals(0); 180 | expect(bag.size()).equals(0); 181 | }); 182 | 183 | it('Converts to an array', 184 | function() { 185 | var arr = bag.toArray(); 186 | expect(arr.length).equals(0); 187 | expect(bag.add('b', 3)).equals(true); 188 | expect(bag.add('a', 2)).equals(true); 189 | expect(bag.add('c')).equals(true); 190 | arr = bag.toArray(); 191 | expect(collections.arrays.frequency(arr, 'b')).equals(3); 192 | expect(collections.arrays.frequency(arr, 'a')).equals(2); 193 | expect(collections.arrays.frequency(arr, 'c')).equals(1); 194 | }); 195 | 196 | it('Converts to a set', 197 | function() { 198 | var set = bag.toSet(); 199 | expect(set.size()).equals(0); 200 | expect(bag.add('b', 3)).equals(true); 201 | expect(bag.add('a', 2)).equals(true); 202 | expect(bag.add('c')).equals(true); 203 | set = bag.toSet(); 204 | expect(set.contains('b')).equals(true); 205 | expect(set.contains('a')).equals(true); 206 | expect(set.contains('c')).equals(true); 207 | }); 208 | 209 | it('For each gives all the elements', 210 | function() { 211 | bag.forEach(function(e) { 212 | expect(false).equals(true); 213 | }); 214 | var a = [1, 5, 5, 6]; 215 | bag.add(1); 216 | bag.add(5); 217 | bag.add(5); 218 | bag.add(6); 219 | bag.forEach(function(e) { 220 | expect(collections.arrays.contains(a, e)).equals(true); 221 | }); 222 | 223 | var count = 0; 224 | bag.forEach(function(e) { 225 | expect(collections.arrays.contains(a, e)).equals(true); 226 | if (e === 5) { 227 | count++; 228 | bag.remove(e); 229 | } 230 | }); 231 | expect(count).equals(2); 232 | expect(bag.contains(5)).equals(false); 233 | expect(bag.contains(1)).equals(true); 234 | expect(bag.contains(6)).equals(true); 235 | }); 236 | 237 | it('For each can be interrupted', 238 | function() { 239 | for (var i = 0; i < 5; i++) { 240 | bag.add(i); 241 | } 242 | var t = 0; 243 | bag.forEach(function(e) { 244 | t++; 245 | return false; 246 | }); 247 | expect(t).equals(1); 248 | }); 249 | }); 250 | -------------------------------------------------------------------------------- /src/test/bsTreeKVTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | 7 | /* 8 | This test suite focuses on the correctness of the type interface of BSTreeKV. 9 | The behavior of the class is asserted in bsTreeTest.ts 10 | */ 11 | describe('Binary Search Tree for Key-Value pairs', 12 | function() { 13 | 14 | type K = { 15 | key: number; 16 | }; 17 | 18 | function compare(a: K, b: K) { 19 | if (a.key > b.key) { 20 | return 1; 21 | } else if (a.key < b.key) { 22 | return -1; 23 | } else { // a.key === b.key 24 | return 0; 25 | } 26 | } 27 | 28 | type V = K & { 29 | data: string; 30 | }; 31 | 32 | var tree: collections.BSTreeKV; 33 | 34 | beforeEach(function() { 35 | tree = new collections.BSTreeKV(compare); 36 | }); 37 | var createTree1 = function() { 38 | tree.add({key: 1, data: 'first'}); 39 | tree.add({key: 2, data: 'second'}); 40 | tree.add({key: 3, data: 'third'}); 41 | }; 42 | var createTree2 = function() { 43 | tree.add({key: 6, data: 'f'}); 44 | tree.add({key: 2, data: 'b'}); 45 | tree.add({key: 1, data: 'a'}); 46 | tree.add({key: 4, data: 'd'}); 47 | tree.add({key: 3, data: 'c'}); 48 | tree.add({key: 5, data: 'e'}); 49 | tree.add({key: 7, data: 'g'}); 50 | tree.add({key: 9, data: 'i'}); 51 | tree.add({key: 8, data: 'h'}); 52 | }; 53 | 54 | it('Can remove with just the key or the value', function() { 55 | createTree1(); 56 | var obj = {key: 4, data: 'fourth'}; 57 | expect(tree.size()).equals(3); 58 | tree.add(obj); 59 | expect(tree.size()).equals(4); 60 | tree.remove(obj); 61 | tree.remove({key: 3}); 62 | expect(tree.size()).equals(2); 63 | }); 64 | 65 | it('Returns the full element with maximum value, not just its key', 66 | function() { 67 | createTree2(); 68 | expect(tree.maximum()).to.deep.equal({key: 9, data: 'i'}); 69 | }); 70 | 71 | it('Returns the full element with minimum value, not just its key', 72 | function() { 73 | createTree2(); 74 | expect(tree.minimum()).to.deep.equal({key: 1, data: 'a'}); 75 | }); 76 | 77 | it('Contains works with just the key or the full object', 78 | function() { 79 | createTree1(); 80 | 81 | var elExist = { key: 1, value: 'a' }; 82 | var elAbsent = { key: 5, value: 'e' }; 83 | 84 | expect(tree.contains({ key: 1 })).equals(true); 85 | expect(tree.contains(elExist)).equals(true); 86 | expect(tree.contains({ key: 5 })).equals(false); 87 | expect(tree.contains(elAbsent)).equals(false); 88 | }); 89 | 90 | it('Inorder traversal gives the full elements', 91 | function() { 92 | createTree2(); 93 | var array = [ 94 | {key: 1, data: 'a'}, 95 | {key: 2, data: 'b'}, 96 | {key: 3, data: 'c'}, 97 | {key: 4, data: 'd'}, 98 | {key: 5, data: 'e'}, 99 | {key: 6, data: 'f'}, 100 | {key: 7, data: 'g'}, 101 | {key: 8, data: 'h'}, 102 | {key: 9, data: 'i'} 103 | ]; 104 | var b: V[] = []; 105 | tree.inorderTraversal(function(element) { 106 | b.push(element); 107 | }); 108 | expect(array).to.deep.equal(b); 109 | }); 110 | 111 | 112 | it('Preorder traversal gives the full elements', 113 | function() { 114 | createTree2(); 115 | var array = [ 116 | {key: 6, data: 'f'}, 117 | {key: 2, data: 'b'}, 118 | {key: 1, data: 'a'}, 119 | {key: 4, data: 'd'}, 120 | {key: 3, data: 'c'}, 121 | {key: 5, data: 'e'}, 122 | {key: 7, data: 'g'}, 123 | {key: 9, data: 'i'}, 124 | {key: 8, data: 'h'} 125 | ]; 126 | var b: V[] = []; 127 | tree.preorderTraversal(function(element) { 128 | b.push(element); 129 | }); 130 | expect(array).to.deep.equal(b); 131 | }); 132 | 133 | it('Level traversal gives the full elements', 134 | function() { 135 | createTree2(); 136 | var array = [ 137 | {key: 6, data: 'f'}, 138 | {key: 2, data: 'b'}, 139 | {key: 7, data: 'g'}, 140 | {key: 1, data: 'a'}, 141 | {key: 4, data: 'd'}, 142 | {key: 9, data: 'i'}, 143 | {key: 3, data: 'c'}, 144 | {key: 5, data: 'e'}, 145 | {key: 8, data: 'h'} 146 | ]; 147 | var b: V[] = []; 148 | tree.levelTraversal(function(element) { 149 | b.push(element); 150 | }); 151 | expect(array).to.deep.equal(b); 152 | }); 153 | 154 | it('Postorter traversal gives the full elements', 155 | function() { 156 | createTree2(); 157 | var array = [ 158 | {key: 1, data: 'a'}, 159 | {key: 3, data: 'c'}, 160 | {key: 5, data: 'e'}, 161 | {key: 4, data: 'd'}, 162 | {key: 2, data: 'b'}, 163 | {key: 8, data: 'h'}, 164 | {key: 9, data: 'i'}, 165 | {key: 7, data: 'g'}, 166 | {key: 6, data: 'f'}, 167 | ]; 168 | var b: V[] = []; 169 | tree.postorderTraversal(function(element) { 170 | b.push(element); 171 | }); 172 | expect(array).to.deep.equal(b); 173 | }); 174 | 175 | it('For each gives the full elements', 176 | function() { 177 | createTree2(); 178 | var array = [ 179 | {key: 1, data: 'a'}, 180 | {key: 2, data: 'b'}, 181 | {key: 3, data: 'c'}, 182 | {key: 4, data: 'd'}, 183 | {key: 5, data: 'e'}, 184 | {key: 6, data: 'f'}, 185 | {key: 7, data: 'g'}, 186 | {key: 8, data: 'h'}, 187 | {key: 9, data: 'i'} 188 | ]; 189 | var b: V[] = []; 190 | tree.forEach(function(element) { 191 | b.push(element); 192 | }); 193 | expect(array).to.deep.equal(b); 194 | }); 195 | 196 | it('toArray gives the right ordering', 197 | function() { 198 | createTree2(); 199 | var array = [ 200 | {key: 1, data: 'a'}, 201 | {key: 2, data: 'b'}, 202 | {key: 3, data: 'c'}, 203 | {key: 4, data: 'd'}, 204 | {key: 5, data: 'e'}, 205 | {key: 6, data: 'f'}, 206 | {key: 7, data: 'g'}, 207 | {key: 8, data: 'h'}, 208 | {key: 9, data: 'i'} 209 | ]; 210 | var b = tree.toArray(); 211 | expect(array).to.deep.equal(b); 212 | }); 213 | 214 | }); 215 | -------------------------------------------------------------------------------- /src/test/bsTreeTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Binary Search Tree', 7 | function() { 8 | 9 | // The type of elements in the tested data structure. 10 | type T = string|number|null|undefined; 11 | 12 | var tree: collections.BSTree; 13 | 14 | beforeEach(function() { 15 | tree = new collections.BSTree(); 16 | }); 17 | var createTree1 = function() { 18 | tree.add('b'); 19 | tree.add('a'); 20 | tree.add('c'); 21 | }; 22 | var createTree2 = function() { 23 | tree.add('f'); 24 | tree.add('b'); 25 | tree.add('a'); 26 | tree.add('d'); 27 | tree.add('c'); 28 | tree.add('e'); 29 | tree.add('g'); 30 | tree.add('i'); 31 | tree.add('h'); 32 | }; 33 | 34 | it('Gives the right size', 35 | function() { 36 | createTree1(); 37 | expect(tree.size()).equals(3); 38 | tree.add('d'); 39 | expect(tree.size()).equals(4); 40 | tree.remove('d'); 41 | tree.remove('d'); 42 | expect(tree.size()).equals(3); 43 | tree.remove('b'); 44 | tree.remove('b'); 45 | expect(tree.size()).equals(2); 46 | tree.remove('c'); 47 | tree.remove('c'); 48 | expect(tree.size()).equals(1); 49 | tree.remove('a'); 50 | tree.remove('a'); 51 | expect(tree.size()).equals(0); 52 | 53 | tree.clear(); 54 | expect(tree.size()).equals(0); 55 | createTree1(); 56 | expect(tree.size()).equals(3); 57 | tree.add('d'); 58 | expect(tree.size()).equals(4); 59 | tree.remove('d'); 60 | expect(tree.size()).equals(3); 61 | tree.add('c'); 62 | expect(tree.size()).equals(3); 63 | }); 64 | 65 | it('Clears removes all elements', 66 | function() { 67 | createTree1(); 68 | tree.clear(); 69 | expect(tree.contains('a')).equals(false); 70 | }); 71 | 72 | it('Gives the right height', 73 | function() { 74 | createTree1(); 75 | expect(tree.height()).equals(1); 76 | }); 77 | 78 | it('Gives the right height 2', 79 | function() { 80 | createTree2(); 81 | expect(tree.height()).equals(3); 82 | }); 83 | 84 | it('Gives the right height on empty tree', 85 | function() { 86 | expect(tree.height()).equals(- 1); 87 | }); 88 | 89 | it('Gives the maximum element 1', 90 | function() { 91 | createTree1(); 92 | expect(tree.maximum()).equals('c'); 93 | }); 94 | 95 | it('Gives the maximum element 2', 96 | function() { 97 | createTree2(); 98 | expect(tree.maximum()).equals('i'); 99 | }); 100 | 101 | it('Gives the maximum element on empty tree', 102 | function() { 103 | expect(tree.maximum()).equals(undefined); 104 | }); 105 | 106 | it('Gives the minimum element 1', 107 | function() { 108 | createTree1(); 109 | expect(tree.minimum()).equals('a'); 110 | }); 111 | 112 | it('Gives the minimum element 2', 113 | function() { 114 | createTree2(); 115 | expect(tree.minimum()).equals('a'); 116 | }); 117 | 118 | it('Gives the minimum element on empty tree', 119 | function() { 120 | expect(tree.minimum()).equals(undefined); 121 | }); 122 | 123 | it('Contains existing elements', 124 | function() { 125 | createTree1(); 126 | 127 | expect(tree.contains('a')).equals(true); 128 | expect(tree.contains('b')).equals(true); 129 | expect(tree.contains('c')).equals(true); 130 | expect(tree.contains('e')).equals(false); 131 | tree.remove('a'); 132 | expect(tree.contains('a')).equals(false); 133 | expect(tree.contains('b')).equals(true); 134 | expect(tree.contains('c')).equals(true); 135 | 136 | tree.clear(); 137 | tree.add(3); 138 | tree.add(2); 139 | tree.add(4); 140 | tree.add(1); 141 | expect(tree.contains(1)).equals(true); 142 | expect(tree.contains(2)).equals(true); 143 | expect(tree.contains(3)).equals(true); 144 | expect(tree.contains(4)).equals(true); 145 | expect(tree.contains(5)).equals(false); 146 | }); 147 | 148 | it('An empty tree is empty', 149 | function() { 150 | expect(tree.isEmpty()).equals(true); 151 | tree.add(1); 152 | expect(tree.isEmpty()).equals(false); 153 | tree.remove(1); 154 | expect(tree.isEmpty()).equals(true); 155 | }); 156 | 157 | it('Adds', 158 | function() { 159 | expect(tree.add('b')).equals(true); 160 | expect(tree.add('a')).equals(true); 161 | expect(tree.contains('a')).equals(true); 162 | expect(tree.contains('b')).equals(true); 163 | expect(tree.add('b')).equals(false); 164 | expect(tree.contains('b')).equals(true); 165 | expect(tree.add(null)).equals(true); 166 | expect(tree.contains(null)).equals(true); 167 | expect(tree.add(null)).equals(false); 168 | expect(tree.contains(undefined)).equals(false); 169 | expect(tree.add(undefined)).equals(false); 170 | expect(tree.contains(undefined)).equals(false); 171 | }); 172 | 173 | it('Removes a leaf', 174 | function() { 175 | createTree2(); 176 | tree.remove('c'); 177 | var array = ['a', 'b', 'd', 'e', 'f', 'g', 'h', 'i']; 178 | var b: T[] = []; 179 | tree.inorderTraversal(function(element) { 180 | b.push(element); 181 | }); 182 | expect(array).to.deep.equal(b); 183 | }); 184 | 185 | it('Removes a node with one children', 186 | function() { 187 | createTree2(); 188 | tree.remove('i'); 189 | var array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; 190 | var b: T[] = []; 191 | tree.inorderTraversal(function(element) { 192 | b.push(element); 193 | }); 194 | expect(array).to.deep.equal(b); 195 | }); 196 | 197 | it('Removes a node with two children', 198 | function() { 199 | createTree2(); 200 | tree.remove('b'); 201 | var array = ['a', 'c', 'd', 'e', 'f', 'g', 'h', 'i']; 202 | var b: T[] = []; 203 | tree.inorderTraversal(function(element) { 204 | b.push(element); 205 | }); 206 | expect(array).to.deep.equal(b); 207 | }); 208 | 209 | it('Removes root', 210 | function() { 211 | createTree2(); 212 | tree.remove('f'); 213 | var array = ['a', 'b', 'c', 'd', 'e', 'g', 'h', 'i']; 214 | var b: T[] = []; 215 | tree.inorderTraversal(function(element) { 216 | b.push(element); 217 | }); 218 | expect(array).to.deep.equal(b); 219 | }); 220 | 221 | it('Inorder traversal gives the right ordering', 222 | function() { 223 | createTree2(); 224 | var array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']; 225 | var b: T[] = []; 226 | tree.inorderTraversal(function(element) { 227 | b.push(element); 228 | }); 229 | expect(array).to.deep.equal(b); 230 | }); 231 | 232 | it('Inorder traversal cen be interrupted', 233 | function() { 234 | createTree2(); 235 | var array = ['a', 'b', 'c', 'd']; 236 | var b: T[] = []; 237 | tree.inorderTraversal(function(element) { 238 | b.push(element); 239 | if (element === 'd') { 240 | return false; 241 | } 242 | }); 243 | expect(array).to.deep.equal(b); 244 | }); 245 | 246 | it('Preorder traversal gives the right ordering', 247 | function() { 248 | createTree2(); 249 | var array = ['f', 'b', 'a', 'd', 'c', 'e', 'g', 'i', 'h']; 250 | var b: T[] = []; 251 | tree.preorderTraversal(function(element) { 252 | b.push(element); 253 | }); 254 | expect(array).to.deep.equal(b); 255 | }); 256 | 257 | it('Preorder traversal can be interrupted', 258 | function() { 259 | createTree2(); 260 | var array = ['f', 'b', 'a']; 261 | var b: T[] = []; 262 | tree.preorderTraversal(function(element) { 263 | b.push(element); 264 | if (element === 'a') { 265 | return false; 266 | } 267 | }); 268 | expect(array).to.deep.equal(b); 269 | }); 270 | 271 | it('Level traversal gives the right ordering', 272 | function() { 273 | createTree2(); 274 | var array = ['f', 'b', 'g', 'a', 'd', 'i', 'c', 'e', 'h']; 275 | var b: T[] = []; 276 | tree.levelTraversal(function(element) { 277 | b.push(element); 278 | }); 279 | expect(array).to.deep.equal(b); 280 | }); 281 | 282 | it('Level traversal can be interrupted', 283 | function() { 284 | createTree2(); 285 | var array = ['f', 'b', 'g', 'a', 'd', 'i']; 286 | var b: T[] = []; 287 | tree.levelTraversal(function(element) { 288 | b.push(element); 289 | if (element === 'i') { 290 | return false; 291 | } 292 | }); 293 | expect(array).to.deep.equal(b); 294 | }); 295 | 296 | it('Postorter traversal gives the right ordering', 297 | function() { 298 | createTree2(); 299 | var array = ['a', 'c', 'e', 'd', 'b', 'h', 'i', 'g', 'f']; 300 | var b: T[] = []; 301 | tree.postorderTraversal(function(element) { 302 | b.push(element); 303 | }); 304 | expect(array).to.deep.equal(b); 305 | }); 306 | 307 | it('Postorter traversal can be interrupted', 308 | function() { 309 | createTree2(); 310 | var array = ['a', 'c', 'e', 'd', 'b']; 311 | var b: T[] = []; 312 | tree.postorderTraversal(function(element) { 313 | b.push(element); 314 | if (element === 'b') { 315 | return false; 316 | } 317 | }); 318 | expect(array).to.deep.equal(b); 319 | }); 320 | 321 | it('For each gives the right ordering', 322 | function() { 323 | createTree2(); 324 | var array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']; 325 | var b: T[] = []; 326 | tree.forEach(function(element) { 327 | b.push(element); 328 | }); 329 | expect(array).to.deep.equal(b); 330 | }); 331 | 332 | it('For each can be interrupted', 333 | function() { 334 | createTree2(); 335 | var array = ['a', 'b', 'c', 'd']; 336 | var b: T[] = []; 337 | tree.forEach(function(element) { 338 | b.push(element); 339 | if (element === 'd') { 340 | return false; 341 | } 342 | }); 343 | expect(array).to.deep.equal(b); 344 | }); 345 | 346 | it('toArray gives the right ordering', 347 | function() { 348 | createTree2(); 349 | var array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']; 350 | var b = tree.toArray(); 351 | expect(array).to.deep.equal(b); 352 | }); 353 | 354 | it('Empty tree returns an empty array', 355 | function() { 356 | expect(tree.toArray()).to.deep.equal([]); 357 | }); 358 | 359 | }); 360 | -------------------------------------------------------------------------------- /src/test/dictionaryTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Dictionary', 7 | function() { 8 | 9 | var dict: any = null; 10 | var elems = 100; 11 | var elemKeys: any = []; 12 | for (var i = 0; i < elems; i++) { 13 | elemKeys[i] = '' + i; 14 | } 15 | // Test with some potentially problematic keys 16 | elemKeys[2] = 'hasOwnProperty'; 17 | elemKeys[4] = '__proto__'; 18 | elemKeys[6] = ''; 19 | 20 | beforeEach(function() { 21 | dict = new collections.Dictionary(); 22 | }); 23 | 24 | it('Maps keys to values with string keys', 25 | function() { 26 | 27 | expect(dict.getValue('sd')).equals(undefined); 28 | 29 | // test with string keys 30 | for (var i = 0; i < elems; i++) { 31 | expect(dict.setValue(elemKeys[i], i + 1)).equals(undefined); 32 | } 33 | expect(dict.size()).equals(elems); 34 | 35 | for (var i = 0; i < elems; i++) { 36 | expect(dict.getValue(elemKeys[i])).equals(i + 1); 37 | } 38 | 39 | dict.setValue('a', 5); 40 | expect(dict.getValue('a')).equals(5); 41 | expect(dict.setValue('a', 21)).equals(5); 42 | expect(dict.size()).equals(elems + 1); 43 | expect(dict.getValue('a')).equals(21); 44 | 45 | 46 | }); 47 | 48 | it('Maps keys to values with number keys', 49 | function() { 50 | 51 | // test with number keys 52 | for (var i = 0; i < elems; i++) { 53 | expect(dict.setValue(i, i + 1)).equals(undefined); 54 | } 55 | 56 | for (var i = 0; i < elems; i++) { 57 | expect(dict.getValue(i)).equals(i + 1); 58 | } 59 | }); 60 | 61 | it('Maps keys to values with custom keys', 62 | function() { 63 | 64 | var ts = function(obj: any) { 65 | return obj.s; 66 | }; 67 | dict = new collections.Dictionary(ts); 68 | expect(dict.getValue('sd')).equals(undefined); 69 | 70 | for (var i = 0; i < elems; i++) { 71 | var o: any = {}; 72 | o.s = elemKeys[i]; 73 | expect(dict.setValue(o, i + 1)).equals(undefined); 74 | } 75 | 76 | for (var i = 0; i < elems; i++) { 77 | var d: any = {}; 78 | d.s = elemKeys[i]; 79 | expect(dict.getValue(d)).equals(i + 1); 80 | } 81 | }); 82 | 83 | it('Removes existing elements from the dictionary', 84 | function() { 85 | 86 | expect(dict.remove('1')).equals(undefined); 87 | for (var i = 0; i < elems; i++) { 88 | expect(dict.setValue(elemKeys[i], i + 1)).equals(undefined); 89 | } 90 | expect(dict.size()).equals(elems); 91 | 92 | for (var i = 0; i < elems; i++) { 93 | expect(dict.remove(elemKeys[i])).equals(i + 1); 94 | expect(dict.getValue(elemKeys[i])).equals(undefined); 95 | expect(dict.remove(elemKeys[i])).equals(undefined); 96 | } 97 | expect(dict.size()).equals(0); 98 | }); 99 | 100 | it('An empty dictionary is empty', 101 | function() { 102 | 103 | expect(dict.isEmpty()).equals(true); 104 | dict.setValue('1', 1); 105 | expect(dict.isEmpty()).equals(false); 106 | dict.remove('1'); 107 | expect(dict.isEmpty()).equals(true); 108 | }); 109 | 110 | it('Clear removes all elements', 111 | function() { 112 | dict.clear(); 113 | dict.setValue(1, 1); 114 | dict.clear(); 115 | expect(dict.isEmpty()).equals(true); 116 | expect(dict.getValue(1)).equals(undefined); 117 | }); 118 | 119 | it('Contains existing keys', 120 | function() { 121 | 122 | expect(dict.containsKey(0)).equals(false); 123 | for (var i = 0; i < 10; i++) { 124 | dict.setValue(elemKeys[i], i); 125 | expect(dict.containsKey(elemKeys[i])).equals(true); 126 | } 127 | for (var i = 0; i < 10; i++) { 128 | dict.remove(elemKeys[i]); 129 | expect(dict.containsKey(elemKeys[i])).equals(false); 130 | } 131 | }); 132 | 133 | it('Gives the right size', 134 | function() { 135 | 136 | expect(dict.size()).equals(0); 137 | for (var i = 0; i < 10; i++) { 138 | dict.setValue(elemKeys[i], i); 139 | expect(dict.size()).equals(i + 1); 140 | } 141 | 142 | }); 143 | 144 | it('Gives all the stored keys', 145 | function() { 146 | var k: any = []; 147 | for (var i = 0; i < elems; i++) { 148 | var keys = dict.keys(); 149 | k.sort(); 150 | keys.sort(); 151 | expect(collections.arrays.equals(k, keys)).equals(true); 152 | dict.setValue(elemKeys[i], i); 153 | k.push(elemKeys[i]); 154 | } 155 | }); 156 | 157 | it('Gives all the stored values', 158 | function() { 159 | var v: any = []; 160 | for (var i = 0; i < elems; i++) { 161 | var values = dict.values(); 162 | v.sort(); 163 | values.sort(); 164 | expect(collections.arrays.equals(v, values)).equals(true); 165 | dict.setValue(elemKeys[i], i); 166 | v.push(i); 167 | } 168 | }); 169 | 170 | it('For each gives all the pairs', 171 | function() { 172 | for (var i = 0; i < elems; i++) { 173 | dict.setValue(elemKeys[i], i); 174 | } 175 | var keys = dict.keys(); 176 | var values = dict.values(); 177 | dict.forEach(function(k: any, v: any) { 178 | expect(collections.arrays.remove(keys, k)).equals(true); 179 | expect(collections.arrays.remove(values, v)).equals(true); 180 | }); 181 | expect(keys.length).equals(0); 182 | expect(values.length).equals(0); 183 | }); 184 | 185 | 186 | it('For each can be interrupted', 187 | function() { 188 | for (var i = 0; i < elems; i++) { 189 | dict.setValue(elemKeys[i], i); 190 | } 191 | var t = 0; 192 | dict.forEach(function(k: any, v: any) { 193 | t++; 194 | return false; 195 | }); 196 | expect(t).equals(1); 197 | }); 198 | 199 | }); 200 | -------------------------------------------------------------------------------- /src/test/factoryDictionaryTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Factory Dictionary', 7 | function() { 8 | 9 | var dict: any = null; 10 | var defaultValue: Array = []; 11 | 12 | beforeEach(function() { 13 | dict = new collections.FactoryDictionary(() => []); 14 | }); 15 | 16 | it('Uses the default value only when necessary', 17 | function() { 18 | expect(dict.setDefault('a', defaultValue)).to.deep.equal(defaultValue); 19 | 20 | var key: string = 'b'; 21 | 22 | dict.setValue(key, []); 23 | expect(dict.setDefault(key, defaultValue)).to.not.equal(defaultValue); 24 | }); 25 | 26 | it('Automatically creates a key with a default value if it doesn\'t exist', 27 | function() { 28 | var key: string = 'a'; 29 | 30 | expect(dict.getValue(key)).to.deep.equal(defaultValue); 31 | expect(dict.containsKey(key)).equals(true); 32 | }); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /src/test/heapTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Heap', 7 | function() { 8 | 9 | var heap: any = null; 10 | 11 | beforeEach(function() { 12 | heap = new collections.Heap(); 13 | }); 14 | 15 | var createHeap1 = function() { 16 | heap.add(0); 17 | heap.add(1); 18 | heap.add(2); 19 | heap.add(3); 20 | }; 21 | 22 | var createHeap2 = function() { 23 | heap.add(1); 24 | heap.add(3); 25 | heap.add(0); 26 | heap.add(2); 27 | }; 28 | 29 | var createHeap3 = function() { 30 | heap.add('a'); 31 | heap.add('b'); 32 | heap.add('c'); 33 | heap.add('d'); 34 | }; 35 | 36 | var createHeap4 = function() { 37 | heap.add('b'); 38 | heap.add('d'); 39 | heap.add('a'); 40 | heap.add('c'); 41 | }; 42 | 43 | var createHeap5 = function() { 44 | heap.add({ val: 'b' }); 45 | heap.add({ val: 'd' }); 46 | heap.add({ val: 'a' }); 47 | heap.add({ val: 'c' }); 48 | }; 49 | 50 | function customCompare(a: any, b: any) { 51 | if (a.val < b.val) { 52 | return -1; 53 | } else if (a.val === b.val) { 54 | return 0; 55 | } else { 56 | return 1; 57 | } 58 | } 59 | 60 | it('Gives the right size 1', 61 | function() { 62 | createHeap1(); 63 | expect(heap.size()).equals(4); 64 | heap.removeRoot(); 65 | expect(heap.size()).equals(3); 66 | }); 67 | 68 | it('Gives the right size 2', 69 | function() { 70 | createHeap1(); 71 | heap.removeRoot(); 72 | heap.removeRoot(); 73 | heap.removeRoot(); 74 | heap.removeRoot(); 75 | expect(heap.size()).equals(0); 76 | }); 77 | 78 | it('Gives the right size with strings', 79 | function() { 80 | createHeap3(); 81 | heap.removeRoot(); 82 | heap.removeRoot(); 83 | heap.removeRoot(); 84 | heap.removeRoot(); 85 | expect(heap.size()).equals(0); 86 | }); 87 | 88 | it('Peeks the lowest element', 89 | function() { 90 | createHeap1(); 91 | expect(heap.peek()).equals(0); 92 | heap.clear(); 93 | expect(heap.peek()).equals(undefined); 94 | }); 95 | 96 | it('Peeks the lowest element 2', 97 | function() { 98 | createHeap2(); 99 | expect(heap.peek()).equals(0); 100 | }); 101 | 102 | it('Peeks the lowest element with strings', 103 | function() { 104 | createHeap3(); 105 | expect(heap.peek()).equals('a'); 106 | }); 107 | 108 | it('Peeks the lowest element with strings 2', 109 | function() { 110 | createHeap4(); 111 | expect(heap.peek()).equals('a'); 112 | }); 113 | 114 | it('Peeks the lowest element with custom objects', 115 | function() { 116 | heap = new collections.Heap(customCompare); 117 | createHeap5(); 118 | expect(heap.peek().val).equals('a'); 119 | }); 120 | 121 | it('Removes root', 122 | function() { 123 | createHeap1(); 124 | expect(heap.removeRoot()).equals(0); 125 | expect(heap.removeRoot()).equals(1); 126 | expect(heap.removeRoot()).equals(2); 127 | expect(heap.removeRoot()).equals(3); 128 | }); 129 | 130 | it('Removes root 2', 131 | function() { 132 | createHeap2(); 133 | heap.add(1); 134 | expect(heap.removeRoot()).equals(0); 135 | expect(heap.removeRoot()).equals(1); 136 | expect(heap.removeRoot()).equals(1); 137 | expect(heap.removeRoot()).equals(2); 138 | expect(heap.removeRoot()).equals(3); 139 | }); 140 | 141 | it('Removes root with custom objects', 142 | function() { 143 | heap = new collections.Heap(customCompare); 144 | createHeap5(); 145 | expect(heap.removeRoot().val).equals('a'); 146 | expect(heap.removeRoot().val).equals('b'); 147 | expect(heap.removeRoot().val).equals('c'); 148 | expect(heap.removeRoot().val).equals('d'); 149 | }); 150 | 151 | it('Adds and peeks', 152 | function() { 153 | heap.add(3); 154 | expect(heap.peek()).equals(3); 155 | heap.add(2); 156 | expect(heap.peek()).equals(2); 157 | heap.add(1); 158 | expect(heap.peek()).equals(1); 159 | heap.add(0); 160 | expect(heap.peek()).equals(0); 161 | }); 162 | 163 | it('Adds and peeks 2', 164 | function() { 165 | heap.add(1); 166 | expect(heap.peek()).equals(1); 167 | heap.add(3); 168 | expect(heap.peek()).equals(1); 169 | heap.add(0); 170 | expect(heap.peek()).equals(0); 171 | heap.add(2); 172 | expect(heap.peek()).equals(0); 173 | }); 174 | 175 | it('An empty heap is empty', 176 | function() { 177 | expect(heap.isEmpty()).equals(true); 178 | createHeap1(); 179 | for (var i = 0; i < heap.size(); i++) { 180 | expect(heap.isEmpty()).equals(false); 181 | heap.removeRoot(); 182 | } 183 | }); 184 | 185 | it('Clear removes all elements', 186 | function() { 187 | heap.clear(); 188 | createHeap1(); 189 | heap.clear(); 190 | expect(heap.isEmpty()).equals(true); 191 | expect(heap.peek()).equals(undefined); 192 | }); 193 | 194 | it('Contains inserted elements', 195 | function() { 196 | createHeap1(); 197 | for (var i = 0; i < 4; i++) { 198 | expect(heap.contains(i)).equals(true); 199 | } 200 | expect(heap.contains(i)).equals(false); 201 | }); 202 | 203 | it('For each gives the right elements', 204 | function() { 205 | 206 | heap.forEach(function(e: any) { 207 | expect(true).equals(false); // should not enter here 208 | }); 209 | createHeap1(); 210 | 211 | var elements: any = []; 212 | heap.forEach(function(e: any) { 213 | elements.push(e); 214 | }); 215 | 216 | expect(collections.arrays.contains(elements, 0)).equals(true); 217 | expect(collections.arrays.contains(elements, 1)).equals(true); 218 | expect(collections.arrays.contains(elements, 2)).equals(true); 219 | expect(collections.arrays.contains(elements, 3)).equals(true); 220 | expect(collections.arrays.contains(elements, 4)).equals(false); 221 | }); 222 | 223 | it('For each can be interrupted', 224 | function() { 225 | createHeap1(); 226 | var elements: any = []; 227 | heap.forEach(function(e: any) { 228 | elements.push(e); 229 | return false; 230 | }); 231 | expect(elements.length).equals(1); 232 | }); 233 | }); 234 | -------------------------------------------------------------------------------- /src/test/multiDictionaryTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Multi Dictionary', 7 | function() { 8 | 9 | var dict: any = null; 10 | var elems = 100; 11 | 12 | beforeEach(function() { 13 | dict = new collections.MultiDictionary(); 14 | }); 15 | 16 | it('Maps keys to values with string keys', 17 | function() { 18 | 19 | expect(dict.getValue('sd')).to.deep.equal([]); 20 | 21 | // test with string keys 22 | for (var i = 0; i < elems; i++) { 23 | expect(dict.setValue('' + i, i + 1)).equals(true); 24 | } 25 | expect(dict.size()).equals(elems); 26 | 27 | for (var i = 0; i < elems; i++) { 28 | expect(dict.getValue('' + i)).to.deep.equal([i + 1]); 29 | } 30 | 31 | dict.setValue('a', 5); 32 | expect(dict.getValue('a')).to.deep.equal([5]); 33 | expect(dict.setValue('a', 21)).equals(true); 34 | expect(dict.size()).equals(elems + 1); 35 | expect(dict.getValue('a')).to.deep.equal([5, 21]); 36 | 37 | }); 38 | 39 | it('Maps keys to values with number keys', 40 | function() { 41 | 42 | // test with number keys 43 | for (var i = 0; i < elems; i++) { 44 | expect(dict.setValue(i, i + 1)).equals(true); 45 | } 46 | 47 | for (var i = 0; i < elems; i++) { 48 | expect(dict.getValue(i)).to.deep.equal([i + 1]); 49 | } 50 | }); 51 | 52 | it('Maps keys to values with custom keys', 53 | function() { 54 | 55 | var ts = function(obj: any) { 56 | return obj.s; 57 | }; 58 | dict = new collections.MultiDictionary(ts); 59 | expect(dict.getValue('sd')).to.deep.equal([]); 60 | 61 | for (var i = 0; i < elems; i++) { 62 | var o: any = {}; 63 | o.s = '' + i; 64 | expect(dict.setValue(o, i + 1)).equals(true); 65 | } 66 | 67 | for (var i = 0; i < elems; i++) { 68 | var d: any = {}; 69 | d.s = '' + i; 70 | expect(dict.getValue(d)).to.deep.equal([i + 1]); 71 | } 72 | }); 73 | 74 | it('Maps multiple values', 75 | function() { 76 | dict.setValue('a', 5); 77 | expect(dict.getValue('a')).to.deep.equal([5]); 78 | expect(dict.setValue('a', 21)).equals(true); 79 | expect(dict.size()).equals(1); 80 | expect(dict.getValue('a')).to.deep.equal([5, 21]); 81 | expect(dict.size()).equals(1); 82 | expect(dict.setValue('a', 31)).equals(true); 83 | expect(dict.size()).equals(1); 84 | expect(dict.getValue('a')).to.deep.equal([5, 21, 31]); 85 | expect(dict.size()).equals(1); 86 | 87 | }); 88 | 89 | it('Removes existing elements from the dictionary', 90 | function() { 91 | 92 | expect(dict.remove('1')).equals(false); 93 | for (var i = 0; i < elems; i++) { 94 | expect(dict.setValue('' + i, i + 1)).equals(true); 95 | } 96 | expect(dict.size()).equals(elems); 97 | 98 | for (var i = 0; i < elems; i++) { 99 | expect(dict.remove('' + i)).equals(true); 100 | expect(dict.getValue('' + i)).to.deep.equal([]); 101 | expect(dict.remove('' + i)).equals(false); 102 | } 103 | expect(dict.size()).equals(0); 104 | }); 105 | 106 | it('Removes all values from a key', 107 | function() { 108 | dict.setValue('a', 1); 109 | dict.remove('a'); 110 | expect(dict.containsKey('a')).equals(false); 111 | expect(dict.getValue('a')).to.deep.equal([]); 112 | dict.setValue('a', 2); 113 | dict.setValue('a', 3); 114 | dict.remove('a'); 115 | expect(dict.containsKey('a')).equals(false); 116 | expect(dict.getValue('a')).to.deep.equal([]); 117 | }); 118 | 119 | it('Removes a single value from a key', 120 | function() { 121 | dict.setValue('a', 1); 122 | dict.remove('a', 1); 123 | expect(dict.containsKey('a')).equals(false); 124 | expect(dict.getValue('a')).to.deep.equal([]); 125 | dict.setValue('a', 2); 126 | dict.setValue('a', 3); 127 | dict.remove('a', 3); 128 | expect(dict.containsKey('a')).equals(true); 129 | expect(dict.getValue('a')).to.deep.equal([2]); 130 | dict.remove('a', 2); 131 | expect(dict.containsKey('a')).equals(false); 132 | expect(dict.getValue('a')).to.deep.equal([]); 133 | }); 134 | 135 | it('An empty dictionary is empty', 136 | function() { 137 | 138 | expect(dict.isEmpty()).equals(true); 139 | dict.setValue('1', 1); 140 | expect(dict.isEmpty()).equals(false); 141 | dict.remove('1'); 142 | expect(dict.isEmpty()).equals(true); 143 | }); 144 | 145 | it('Clear removes all elements', 146 | function() { 147 | dict.clear(); 148 | dict.setValue(1, 1); 149 | dict.clear(); 150 | expect(dict.isEmpty()).equals(true); 151 | expect(dict.getValue(1)).to.deep.equal([]); 152 | }); 153 | 154 | it('Contains existing keys', 155 | function() { 156 | 157 | expect(dict.containsKey(0)).equals(false); 158 | for (var i = 0; i < 10; i++) { 159 | dict.setValue(i, i); 160 | expect(dict.containsKey(i)).equals(true); 161 | } 162 | for (var i = 0; i < 10; i++) { 163 | dict.remove(i); 164 | expect(dict.containsKey(i)).equals(false); 165 | } 166 | }); 167 | 168 | it('Gives the right size', 169 | function() { 170 | 171 | expect(dict.size()).equals(0); 172 | for (var i = 0; i < 10; i++) { 173 | dict.setValue(i, i); 174 | expect(dict.size()).equals(i + 1); 175 | } 176 | }); 177 | 178 | it('Gives all the stored keys', 179 | function() { 180 | var k: any = []; 181 | for (var i = 0; i < elems; i++) { 182 | var keys = dict.keys(); 183 | k.sort(); 184 | keys.sort(); 185 | expect(k).to.deep.equal(keys); 186 | dict.setValue('' + i, i); 187 | k.push('' + i); 188 | } 189 | }); 190 | 191 | it('Gives all the stored values', 192 | function() { 193 | var v: any = []; 194 | for (var i = 0; i < elems; i++) { 195 | var values = dict.values(); 196 | v.sort(); 197 | values.sort(); 198 | expect(v).to.deep.equal(values); 199 | dict.setValue('' + i, i); 200 | v.push(i); 201 | } 202 | }); 203 | 204 | }); 205 | -------------------------------------------------------------------------------- /src/test/priorityQueueTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Priority Queue', 7 | function() { 8 | 9 | var queue: any = null; 10 | 11 | beforeEach(function() { 12 | queue = new collections.PriorityQueue(); 13 | }); 14 | 15 | var createPriorityQueue1 = function() { 16 | queue.enqueue(0); 17 | queue.enqueue(1); 18 | queue.enqueue(2); 19 | queue.enqueue(3); 20 | return queue; 21 | }; 22 | 23 | var createPriorityQueue2 = function() { 24 | queue.enqueue(1); 25 | queue.enqueue(3); 26 | queue.enqueue(0); 27 | queue.enqueue(2); 28 | return queue; 29 | }; 30 | 31 | it('Gives the right size', 32 | function() { 33 | createPriorityQueue1(); 34 | expect(queue.size()).equals(4); 35 | queue.dequeue(); 36 | expect(queue.size()).equals(3); 37 | }); 38 | 39 | it('Gives the right size 2', 40 | function() { 41 | createPriorityQueue2(); 42 | expect(queue.size()).equals(4); 43 | queue.dequeue(); 44 | expect(queue.size()).equals(3); 45 | }); 46 | 47 | it('Gives the right size 3', 48 | function() { 49 | createPriorityQueue1(); 50 | queue.dequeue(); 51 | queue.dequeue(); 52 | queue.dequeue(); 53 | queue.dequeue(); 54 | expect(queue.size()).equals(0); 55 | }); 56 | 57 | it('Contains inserted elements', 58 | function() { 59 | createPriorityQueue1(); 60 | for (var i = 0; i < 4; i++) { 61 | expect(queue.contains(i)).equals(true); 62 | } 63 | expect(queue.contains(5)).equals(false); 64 | }); 65 | 66 | it('An empty queue is empty', 67 | function() { 68 | createPriorityQueue1(); 69 | expect(queue.isEmpty()).equals(false); 70 | queue.dequeue(); 71 | queue.dequeue(); 72 | queue.dequeue(); 73 | expect(queue.isEmpty()).equals(false); 74 | queue.dequeue(); 75 | expect(queue.isEmpty()).equals(true); 76 | }); 77 | 78 | it('Peeks the highest priority item', 79 | function() { 80 | createPriorityQueue1(); 81 | expect(queue.peek()).equals(3); 82 | }); 83 | 84 | it('Peeks the highest priority item 2', 85 | function() { 86 | createPriorityQueue2(); 87 | expect(queue.peek()).equals(3); 88 | }); 89 | 90 | it('Peeking an empty queue returns undefined', 91 | function() { 92 | createPriorityQueue1(); 93 | queue.clear(); 94 | expect(queue.peek()).equals(undefined); 95 | }); 96 | 97 | it('Dequeues the highest priority item', 98 | function() { 99 | createPriorityQueue1(); 100 | expect(queue.dequeue()).equals(3); 101 | expect(queue.dequeue()).equals(2); 102 | expect(queue.dequeue()).equals(1); 103 | expect(queue.dequeue()).equals(0); 104 | }); 105 | 106 | it('Dequeues the highest priority item 2', 107 | function() { 108 | createPriorityQueue2(); 109 | expect(queue.dequeue()).equals(3); 110 | expect(queue.dequeue()).equals(2); 111 | expect(queue.dequeue()).equals(1); 112 | expect(queue.dequeue()).equals(0); 113 | }); 114 | 115 | it('Peek and enqueue are consistent', 116 | function() { 117 | queue.enqueue(0); 118 | expect(queue.peek()).equals(0); 119 | queue.enqueue(1); 120 | expect(queue.peek()).equals(1); 121 | queue.enqueue(2); 122 | expect(queue.peek()).equals(2); 123 | queue.enqueue(3); 124 | expect(queue.peek()).equals(3); 125 | }); 126 | 127 | it('Peek and enqueue are consistent 2', 128 | function() { 129 | queue.enqueue(1); 130 | expect(queue.peek()).equals(1); 131 | queue.enqueue(3); 132 | expect(queue.peek()).equals(3); 133 | queue.enqueue(0); 134 | expect(queue.peek()).equals(3); 135 | queue.enqueue(2); 136 | expect(queue.peek()).equals(3); 137 | }); 138 | 139 | it('For each gives the right elements', 140 | function() { 141 | 142 | queue.forEach(function(e: any) { 143 | expect(true).equals(false); // should not enter here 144 | }); 145 | createPriorityQueue1(); 146 | 147 | var elements: any = []; 148 | queue.forEach(function(e: any) { 149 | elements.push(e); 150 | }); 151 | 152 | expect(collections.arrays.contains(elements, 0)).equals(true); 153 | expect(collections.arrays.contains(elements, 1)).equals(true); 154 | expect(collections.arrays.contains(elements, 2)).equals(true); 155 | expect(collections.arrays.contains(elements, 3)).equals(true); 156 | expect(collections.arrays.contains(elements, 4)).equals(false); 157 | }); 158 | 159 | it('For each can be interrupted', 160 | function() { 161 | createPriorityQueue1(); 162 | var elements: any = []; 163 | queue.forEach(function(e: any) { 164 | elements.push(e); 165 | return false; 166 | }); 167 | expect(elements.length).equals(1); 168 | }); 169 | }); 170 | -------------------------------------------------------------------------------- /src/test/queueTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Queue', 7 | function() { 8 | 9 | var queue: any = null; 10 | 11 | beforeEach(function() { 12 | queue = new collections.Queue(); 13 | }); 14 | 15 | function createQueue() { 16 | queue.enqueue('a'); 17 | queue.enqueue('b'); 18 | queue.enqueue('c'); 19 | } 20 | 21 | it('Gives the right size', 22 | function() { 23 | expect(queue.size()).equals(0); 24 | createQueue(); 25 | expect(queue.size()).equals(3); 26 | queue.enqueue('d'); 27 | expect(queue.size()).equals(4); 28 | queue.dequeue(); 29 | expect(queue.size()).equals(3); 30 | queue.clear(); 31 | expect(queue.size()).equals(0); 32 | 33 | }); 34 | 35 | it('Enqueues', 36 | function() { 37 | createQueue(); 38 | var head = queue.dequeue(); 39 | expect(head).equals('a'); 40 | queue.dequeue(); 41 | head = queue.dequeue(); 42 | expect(head).equals('c'); 43 | expect(queue.isEmpty()).equals(true); 44 | head = queue.dequeue(); 45 | expect(head).equals(undefined); 46 | }); 47 | 48 | it('Peeks', 49 | function() { 50 | createQueue(); 51 | var head = queue.peek(); 52 | expect(head).equals('a'); 53 | var head2 = queue.dequeue(); 54 | expect(head).equals(head2); 55 | head = queue.peek(); 56 | expect(head).equals('b'); 57 | queue.clear(); 58 | head = queue.peek(); 59 | expect(head).equals(undefined); 60 | }); 61 | 62 | it('For each gives the right ordering', 63 | function() { 64 | 65 | queue.forEach(function(e: any) { 66 | expect(true).equals(false); // should not enter here 67 | }); 68 | 69 | for (var i = 0; i < 10; i++) { 70 | queue.add(i); 71 | } 72 | 73 | var i = 0; 74 | queue.forEach(function(e: any) { 75 | expect(e).equals(i); 76 | i++; 77 | }); 78 | }); 79 | 80 | it('For each can be interrupted', 81 | function() { 82 | var array = [0, 1, 2, 3, 4]; 83 | var b: any = []; 84 | for (var i = 0; i < 5; i++) { 85 | queue.add(i); 86 | } 87 | queue.forEach(function(e: any) { 88 | b.push(e); 89 | if (e === 3) { 90 | return false; 91 | } 92 | }); 93 | 94 | expect([0, 1, 2, 3]).to.deep.equal(b); 95 | }); 96 | it('Contains previously added items', 97 | function() { 98 | createQueue(); 99 | expect(queue.contains('a')).equals(true); 100 | expect(queue.contains('z')).equals(false); 101 | expect(queue.contains(undefined)).equals(false); 102 | }); 103 | 104 | }); 105 | -------------------------------------------------------------------------------- /src/test/runTests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Jasmine Spec Runner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/test/setTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Set', 7 | function() { 8 | 9 | var set: any = null; 10 | 11 | it('Gives the right size', 12 | function() { 13 | set = new collections.Set(); 14 | set.add('a'); 15 | set.add('b'); 16 | set.add('c'); 17 | expect(set.size()).equals(3); 18 | set.add('d'); 19 | expect(set.size()).equals(4); 20 | set.remove('d'); 21 | expect(set.size()).equals(3); 22 | set.clear(); 23 | 24 | set.add('a'); 25 | set.add('b'); 26 | set.add('c'); 27 | expect(set.size()).equals(3); 28 | set.add('d'); 29 | expect(set.size()).equals(4); 30 | set.remove('d'); 31 | expect(set.size()).equals(3); 32 | set.add('c'); 33 | expect(set.size()).equals(3); 34 | }); 35 | 36 | it('Contains existing elements', 37 | function() { 38 | set = new collections.Set(); 39 | set.add('a'); 40 | set.add('b'); 41 | set.add('c'); 42 | set.add('d'); 43 | 44 | expect(set.contains('a')).equals(true); 45 | expect(set.contains('b')).equals(true); 46 | expect(set.contains('c')).equals(true); 47 | expect(set.contains('d')).equals(true); 48 | expect(set.contains('e')).equals(false); 49 | 50 | set.clear(); 51 | set.add(1); 52 | set.add(2); 53 | set.add(3); 54 | set.add(4); 55 | expect(set.contains(1)).equals(true); 56 | expect(set.contains(2)).equals(true); 57 | expect(set.contains(3)).equals(true); 58 | expect(set.contains(4)).equals(true); 59 | expect(set.contains(5)).equals(false); 60 | 61 | var toStringF = function(f: any) { 62 | return f.description; 63 | }; 64 | 65 | set = new collections.Set(toStringF); 66 | var fn1: any = function() { }; 67 | fn1.description = 'fn1'; 68 | expect(set.contains(fn1)).equals(false); 69 | set.add(fn1); 70 | expect(set.contains(fn1)).equals(true); 71 | var fn2: any = function() { }; 72 | fn2.description = 'fn2'; 73 | expect(set.contains(fn2)).equals(false); 74 | set.add(fn2); 75 | expect(set.contains(fn2)).equals(true); 76 | expect(set.size()).equals(2); 77 | }); 78 | 79 | it('An empty set is empty', 80 | function() { 81 | set = new collections.Set(); 82 | expect(set.isEmpty()).equals(true); 83 | set.add(1); 84 | expect(set.isEmpty()).equals(false); 85 | }); 86 | 87 | it('Intersection is commutative', 88 | function() { 89 | //Two empty sets 90 | set = new collections.Set(); 91 | var set2 = new collections.Set(); 92 | set.intersection(set2); 93 | expect(set.isEmpty()).equals(true); 94 | set2.intersection(set); 95 | expect(set2.isEmpty()).equals(true); 96 | 97 | // non empty with empty 98 | set = new collections.Set(); 99 | set2 = new collections.Set(); 100 | set.add(1); 101 | set.add(2); 102 | set.add(3); 103 | set.intersection(set2); 104 | expect(set.isEmpty()).equals(true); 105 | set2.intersection(set); 106 | expect(set2.isEmpty()).equals(true); 107 | 108 | // non empty sets with common elements 109 | set = new collections.Set(); 110 | set2 = new collections.Set(); 111 | set.add(1); 112 | set.add(2); 113 | set2.add(1); 114 | set2.add(2); 115 | set2.add(3); 116 | 117 | set.intersection(set2); 118 | var s1 = set.toArray().sort(); 119 | expect(s1).to.deep.equal([1, 2]); 120 | set = new collections.Set(); 121 | set.add(1); 122 | set.add(2); 123 | 124 | set2.intersection(set); 125 | var s2 = set2.toArray().sort(); 126 | expect(s2).to.deep.equal([1, 2]); 127 | 128 | // non empty sets with no common elements 129 | set = new collections.Set(); 130 | set2 = new collections.Set(); 131 | set.add(1); 132 | set.add(2); 133 | set2.add(3); 134 | set2.add(4); 135 | set2.add(5); 136 | 137 | set.intersection(set2); 138 | expect(set.isEmpty()).equals(true); 139 | set.add(1); 140 | set.add(2); 141 | set2.intersection(set); 142 | expect(set2.isEmpty()).equals(true); 143 | }); 144 | 145 | it('Union is commutative', 146 | function() { 147 | set = new collections.Set(); 148 | var set2 = new collections.Set(); 149 | set.add(1); 150 | set.add(2); 151 | set2.add(2); 152 | set2.add(4); 153 | set2.add(5); 154 | set.union(set2); 155 | var s1 = set.toArray().sort(); 156 | expect(s1).to.deep.equal([1, 2, 4, 5]); 157 | set.clear(); 158 | set.add(1); 159 | set.add(2); 160 | set2.union(set); 161 | var s2 = set2.toArray().sort(); 162 | expect(s2).to.deep.equal([1, 2, 4, 5]); 163 | }); 164 | 165 | it('Difference works as expected', 166 | function() { 167 | 168 | //Two empty sets 169 | set = new collections.Set(); 170 | var set2 = new collections.Set(); 171 | set.difference(set2); 172 | expect(set.isEmpty()).equals(true); 173 | 174 | //Non empty and empty set 175 | set = new collections.Set(); 176 | set2 = new collections.Set(); 177 | set.add(1); 178 | set.add(2); 179 | set.difference(set2); 180 | var s1 = set.toArray().sort(); 181 | expect(s1).to.deep.equal([1, 2]); 182 | 183 | //Non empty sets with common elements 184 | set = new collections.Set(); 185 | set2 = new collections.Set(); 186 | set.add(1); 187 | set.add(2); 188 | set.add(3); 189 | set.add(4); 190 | set2.add(2); 191 | set2.add(3); 192 | set.difference(set2); 193 | s1 = set.toArray().sort(); 194 | expect(s1).to.deep.equal([1, 4]); 195 | 196 | // Two equal sets 197 | set = new collections.Set(); 198 | set2 = new collections.Set(); 199 | set.add(1); 200 | set.add(2); 201 | set.add(3); 202 | set2.add(1); 203 | set2.add(3); 204 | set2.add(2); 205 | set.difference(set2); 206 | expect(set.isEmpty()).equals(true); 207 | 208 | //Non empty sets with no common elements 209 | set = new collections.Set(); 210 | set2 = new collections.Set(); 211 | set.add(1); 212 | set.add(2); 213 | set.add(3); 214 | set.add(4); 215 | set2.add(6); 216 | set2.add(9); 217 | set.difference(set2); 218 | s1 = set.toArray().sort(); 219 | expect(s1).to.deep.equal([1, 2, 3, 4]); 220 | }); 221 | 222 | it('isSubsetOf works as expected', 223 | function() { 224 | 225 | //Two empty sets 226 | set = new collections.Set(); 227 | var set2 = new collections.Set(); 228 | expect(set.isSubsetOf(set2)).equals(true); 229 | 230 | // Two equal sets 231 | set = new collections.Set(); 232 | set2 = new collections.Set(); 233 | set.add(1); 234 | set.add(2); 235 | set2.add(2); 236 | set2.add(1); 237 | expect(set.isSubsetOf(set2)).equals(true); 238 | expect(set2.isSubsetOf(set)).equals(true); 239 | 240 | //Non empty sets with common elements 241 | set = new collections.Set(); 242 | set2 = new collections.Set(); 243 | set.add(1); 244 | set.add(2); 245 | set.add(3); 246 | set.add(4); 247 | set2.add(2); 248 | set2.add(3); 249 | expect(set2.isSubsetOf(set)).equals(true); 250 | expect(set.isSubsetOf(set2)).equals(false); 251 | 252 | //Non empty sets with no common elements 253 | set = new collections.Set(); 254 | set2 = new collections.Set(); 255 | set.add(3); 256 | set2.add(4); 257 | expect(set.isSubsetOf(set2)).equals(false); 258 | expect(set2.isSubsetOf(set)).equals(false); 259 | }); 260 | 261 | it('Adds', 262 | function() { 263 | set = new collections.Set(); 264 | expect(set.add('a')).equals(true); 265 | expect(set.add('b')).equals(true); 266 | expect(set.contains('a')).equals(true); 267 | expect(set.contains('b')).equals(true); 268 | expect(set.add('b')).equals(false); 269 | expect(set.contains('b')).equals(true); 270 | expect(set.add(null)).equals(true); 271 | expect(set.contains(null)).equals(true); 272 | expect(set.add(null)).equals(false); 273 | expect(set.contains(undefined)).equals(false); 274 | expect(set.add(undefined)).equals(false); 275 | expect(set.contains(undefined)).equals(false); 276 | }); 277 | 278 | it('For each gives all the elements', 279 | function() { 280 | set = new collections.Set(); 281 | set.forEach(function(e: any) { 282 | expect(false).equals(true); 283 | }); 284 | for (var i = 0; i < 100; i++) { 285 | set.add(i); 286 | } 287 | var values = set.toArray(); 288 | expect(values.length).equals(100); 289 | set.forEach(function(e: any) { 290 | expect(collections.arrays.remove(values, e)).equals(true); 291 | }); 292 | expect(values.length).equals(0); 293 | }); 294 | 295 | it('For each can be interrupted', 296 | function() { 297 | set = new collections.Set(); 298 | for (var i = 0; i < 5; i++) { 299 | set.add(i); 300 | } 301 | var t = 0; 302 | set.forEach(function(e: any) { 303 | t++; 304 | return false; 305 | }); 306 | expect(t).equals(1); 307 | }); 308 | }); 309 | -------------------------------------------------------------------------------- /src/test/stackTest.ts: -------------------------------------------------------------------------------- 1 | import * as collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('Stack', 7 | function() { 8 | 9 | var stack: any = null; 10 | 11 | beforeEach(function() { 12 | stack = new collections.Stack(); 13 | }); 14 | 15 | it('Pops', 16 | function() { 17 | expect(stack.pop()).equals(undefined); 18 | stack.push(1); 19 | stack.push(2); 20 | stack.push(3); 21 | expect(stack.pop()).equals(3); 22 | expect(stack.pop()).equals(2); 23 | expect(stack.pop()).equals(1); 24 | expect(stack.pop()).equals(undefined); 25 | 26 | }); 27 | 28 | it('Pushes and pops', 29 | function() { 30 | stack.push(1); 31 | expect(stack.pop()).equals(1); 32 | stack.push(2); 33 | expect(stack.pop()).equals(2); 34 | stack.push(3); 35 | expect(stack.pop()).equals(3); 36 | expect(stack.pop()).equals(undefined); 37 | }); 38 | 39 | it('Peeks', 40 | function() { 41 | stack.push(1); 42 | stack.push(2); 43 | stack.push(3); 44 | expect(stack.peek()).equals(3); 45 | stack.pop(); 46 | expect(stack.peek()).equals(2); 47 | stack.pop(); 48 | expect(stack.peek()).equals(1); 49 | stack.pop(); 50 | expect(stack.peek()).equals(undefined); 51 | }); 52 | 53 | it('Pushes and peeks', 54 | function() { 55 | expect(stack.peek()).equals(undefined); 56 | stack.push(1); 57 | expect(stack.peek()).equals(1); 58 | stack.push(2); 59 | expect(stack.peek()).equals(2); 60 | stack.push(3); 61 | expect(stack.peek()).equals(3); 62 | }); 63 | 64 | it('Gives the right size', 65 | function() { 66 | expect(stack.size()).equals(0); 67 | stack.push(1); 68 | stack.push(2); 69 | stack.push(3); 70 | expect(stack.size()).equals(3); 71 | stack.peek(); 72 | expect(stack.size()).equals(3); 73 | stack.pop(); 74 | stack.pop(); 75 | stack.pop(); 76 | expect(stack.size()).equals(0); 77 | }); 78 | 79 | it('For each gives the right ordering', 80 | function() { 81 | 82 | stack.forEach(function(e: any) { 83 | expect(true).equals(false); // should not enter here 84 | }); 85 | 86 | for (var i = 0; i < 10; i++) { 87 | stack.add(i); 88 | } 89 | 90 | var i = 10 - 1; 91 | stack.forEach(function(e: any) { 92 | expect(e).equals(i); 93 | i--; 94 | }); 95 | }); 96 | 97 | it('For each can be interrupted', 98 | function() { 99 | var array = [0, 1, 2, 3, 4]; 100 | var b: any = []; 101 | for (var i = 0; i < 5; i++) { 102 | stack.add(i); 103 | } 104 | stack.forEach(function(e: any) { 105 | b.push(e); 106 | if (e === 4) { 107 | return false; 108 | } 109 | }); 110 | 111 | expect([4]).to.deep.equal(b); 112 | }); 113 | 114 | }); 115 | -------------------------------------------------------------------------------- /src/test/utilTest.ts: -------------------------------------------------------------------------------- 1 | import * as Collections from '../lib/index'; 2 | 3 | import * as assert from 'power-assert'; 4 | import {expect} from 'chai'; 5 | 6 | describe('util', 7 | function () { 8 | 9 | class Car { 10 | constructor(public company: string, public type: string, public year: number) { 11 | } 12 | toString() { 13 | // Short hand. Adds each own property 14 | return Collections.util.makeString(this); 15 | } 16 | } 17 | 18 | it('makeString function works', 19 | function () { 20 | let carStringified = new Car('BMW', 'A', 2016).toString(); 21 | 22 | assert.equal(carStringified, '{company:BMW,type:A,year:2016}'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "noImplicitAny": true, 7 | "removeComments": false, 8 | "sourceMap": true, 9 | "outDir": "./dist", 10 | "noLib": false, 11 | "preserveConstEnums": true, 12 | "strict": true 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "typings/browser", 17 | "typings/browser.d.ts", 18 | "examples" 19 | ] 20 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "curly": true, 5 | "eofline": true, 6 | "forin": true, 7 | "indent": [ 8 | true, 9 | "spaces" 10 | ], 11 | "label-position": true, 12 | "max-line-length": [ 13 | true, 14 | 140 15 | ], 16 | "no-arg": true, 17 | "no-bitwise": true, 18 | "no-console": [ 19 | true, 20 | "debug", 21 | "info", 22 | "time", 23 | "timeEnd", 24 | "trace" 25 | ], 26 | "no-construct": true, 27 | "no-debugger": true, 28 | "no-duplicate-variable": false, 29 | "no-empty": false, 30 | "no-eval": true, 31 | "no-string-literal": true, 32 | "no-switch-case-fall-through": true, 33 | "no-trailing-whitespace": true, 34 | "no-unused-expression": true, 35 | "no-unused-variable": false, 36 | "no-use-before-declare": true, 37 | "one-line": [ 38 | true, 39 | "check-open-brace", 40 | "check-catch", 41 | "check-else", 42 | "check-whitespace" 43 | ], 44 | "quotemark": [ 45 | true, 46 | "single" 47 | ], 48 | "radix": true, 49 | "semicolon": true, 50 | "triple-equals": [ 51 | true, 52 | "allow-null-check" 53 | ], 54 | "variable-name": false, 55 | "whitespace": [ 56 | true, 57 | "check-branch", 58 | "check-decl", 59 | "check-operator", 60 | "check-separator", 61 | "check-type" 62 | ], 63 | "jsdoc-format": true 64 | } 65 | } --------------------------------------------------------------------------------