├── .gitignore
├── .travis.yml
├── package.json
├── Gruntfile.js
├── README.md
├── doubly-linked-list.js
└── test
└── doubly-linked-list_test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .*redcar
2 | node_modules
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.10"
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "DoublyLinkedList",
3 | "author": {
4 | "name": "Andrew Jones",
5 | "email": "andrew@andrew-jones.com",
6 | "url": "http://andrew-jones.com"
7 | },
8 | "version": "0.0.1",
9 | "private": true,
10 | "dependencies": {},
11 | "devDependencies": {
12 | "grunt": "~0.4",
13 | "grunt-cli": "~0.1",
14 | "grunt-beautify": "git://github.com/terryweiss/grunt-beautify.git#FixForGrunt040",
15 | "grunt-contrib-nodeunit": "~0.1.2",
16 | "grunt-contrib-jshint": "~0.1.1"
17 | },
18 | "licenses": [
19 | {
20 | "type": "MIT",
21 | "url": "https://github.com/andrewrjones/grunt-ssh/blob/master/LICENSE-MIT"
22 | }
23 | ],
24 | "main": "doubly-linked-list.js",
25 | "scripts": {
26 | "test": "grunt"
27 | }
28 | }
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 | 'use strict';
3 |
4 | // Project configuration.
5 | grunt.initConfig({
6 | nodeunit: {
7 | files: ['test/**/*.js']
8 | },
9 | beautify: {
10 | files: '<%= jshint.files %>'
11 | },
12 | jshint: {
13 | options: {
14 | curly: true,
15 | eqeqeq: true,
16 | immed: true,
17 | latedef: true,
18 | newcap: true,
19 | noarg: true,
20 | sub: true,
21 | undef: true,
22 | boss: true,
23 | eqnull: true,
24 | node: true,
25 | globals: {
26 | exports: true
27 | }
28 | },
29 | files: ['Gruntfile.js', 'doubly-linked-list.js', 'test/**/*.js']
30 | }
31 | });
32 |
33 | grunt.loadNpmTasks('grunt-contrib-jshint');
34 | grunt.loadNpmTasks('grunt-beautify');
35 | grunt.loadNpmTasks('grunt-contrib-nodeunit');
36 |
37 | // Default task.
38 | grunt.registerTask('default', ['jshint', 'nodeunit']);
39 |
40 | grunt.registerTask('tidy', ['beautify']);
41 |
42 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | DoublyLinkedList
2 | ================
3 |
4 | This is a Javascipt implementation of a [doubly linked list](http://en.wikipedia.org/wiki/Doubly_linked_list).
5 |
6 | I needed a doubly linked list for a project I was doing, and thought I would post the code here in case it was useful for anyone else.
7 |
8 | Works in both the browser and node.js.
9 |
10 | Synopsis
11 | --------
12 |
13 | node.js.
14 |
15 | ```javascript
16 | var DLL = require('doubly-linked-list.js');
17 | var list = new DLL.DoublyLinkedList();
18 |
19 | list.append('data1');
20 | node = list.append('data2');
21 | list.append('data3');
22 |
23 | size = list.size; // 3
24 | node.prev.data; // data1
25 | node.next.data; // data3
26 |
27 | node = list.item(1);
28 | data = node.data; // data2
29 | prev = node.prev; // data1
30 | next = node.next; // data3
31 |
32 | node = list.head();
33 | data = node.data; // data1
34 |
35 | node = list.tail();
36 | data = node.data; // data3
37 |
38 | list.prepend('data4');
39 |
40 | size = list.size; // 4
41 |
42 | node = list.head();
43 | data = node.data; // data4
44 | prev = node.prev; // null
45 | next = node.next; // data1
46 |
47 | node.append('frog');
48 | appended = node.next; // frog
49 | next = appended.next; // data1
50 |
51 | next.remove();
52 | afterRemoved = appended.next; // data2
53 |
54 | afterRemoved.prepend('bird');
55 | prepended = appended.next; // bird
56 | ```
57 |
58 | Browser.
59 |
60 | ```html
61 |
62 |
66 | ```
67 |
68 | API
69 | ---
70 |
71 | ### new DoublyLinkedList()
72 |
73 | Creates a new DoublyLinkedList. Takes no arguments.
74 |
75 | ### append(data)
76 |
77 | Appends a node to the end of the list. Returns the node.
78 |
79 | See `item(index)` for notes on the structure of the node returned.
80 |
81 | ### prepend(data)
82 |
83 | Prepends a node to the end of the list. Returns the node.
84 |
85 | See `item(index)` for notes on the structure of the node returned.
86 |
87 | ### item(index)
88 |
89 | Returns the node at the specified index. The index starts at 0.
90 |
91 | *Note:* Nodes returned support various operations. See the section about node objects for a list of supported properties and methods.
92 |
93 | ### head()
94 |
95 | Returns the node at the head of the list. See `item(index)` for notes on the structure of the node returned.
96 |
97 | ### tail()
98 |
99 | Returns the node at the tail of the list. See `item(index)` for notes on the structure of the node returned.
100 |
101 | ### size()
102 |
103 | Returns the size of the list.
104 |
105 | ### Node Objects
106 |
107 | When a list/node operation returns a node, it is in fact an object with the following properties and methods:
108 |
109 | #### prev
110 |
111 | The node before the current one, or null if this node is the list head.
112 |
113 | #### next
114 |
115 | The node after the current one, or null if this node is the list tail.
116 |
117 | #### data
118 |
119 | The data stored in the node when it was created.
120 |
121 | #### remove()
122 |
123 | Removes the node from the list.
124 |
125 | #### append(data)
126 |
127 | Creates a new node with a data value of `data` and adds it to the list after the current node. Returns the new node.
128 |
129 | #### prepend(data)
130 |
131 | Creates a new node with a data value of `data` and adds it to the list before the current node. Returns the new node.
132 |
133 | Build status
134 | ------------
135 |
136 | [](http://travis-ci.org/andrewrjones/doubly-linked-list-js)
137 |
138 | More
139 | ----
140 |
141 | https://github.com/playcraft/gamecore.js/blob/master/src/linkedlist.js
142 |
--------------------------------------------------------------------------------
/doubly-linked-list.js:
--------------------------------------------------------------------------------
1 | (function (exports) {
2 | "use strict";
3 | /*
4 | * Constructor. Takes no arguments.
5 | */
6 |
7 | function DoublyLinkedList() {
8 | // pointer to first item
9 | this._head = null;
10 | // pointer to the last item
11 | this._tail = null;
12 | // length of list
13 | this._length = 0;
14 | }
15 |
16 | // Wraps data in a node object.
17 | DoublyLinkedList.prototype._createNewNode = function (data) {
18 | var list = this;
19 |
20 | var node = {
21 | data: data,
22 | next: null,
23 | prev: null,
24 |
25 | remove: function() {
26 | if (this.prev !== null) {
27 | this.prev.next = this.next;
28 | }
29 |
30 | if (this.next !== null) {
31 | this.next.prev = this.prev;
32 | }
33 |
34 | if (list._head === this) {
35 | list._head = this.next;
36 | }
37 |
38 | if (list._tail === this) {
39 | list._tail = this.prev;
40 | }
41 |
42 | list._length--;
43 | },
44 |
45 | prepend: function(data) {
46 | if (list._head === this) {
47 | return list.prepend(data);
48 | }
49 |
50 | var newNode = list._createNewNode(data);
51 | newNode.prev = this.prev;
52 | newNode.next = this;
53 | this.prev.next = newNode;
54 | this.prev = newNode;
55 |
56 | list._length++;
57 | return newNode;
58 | },
59 |
60 | append: function(data) {
61 | if (list._tail === this) {
62 | return list.append(data);
63 | }
64 |
65 | var newNode = list._createNewNode(data);
66 | newNode.prev = this;
67 | newNode.next = this.next;
68 | this.next.prev = newNode;
69 | this.next = newNode;
70 |
71 | list._length++;
72 | return newNode;
73 | }
74 | };
75 |
76 | return node;
77 | };
78 |
79 | /*
80 | * Appends a node to the end of the list.
81 | */
82 | DoublyLinkedList.prototype.append = function (data) {
83 | var node = this._createNewNode(data);
84 |
85 | if (this._length === 0) {
86 |
87 | // first node, so all pointers to this
88 | this._head = node;
89 | this._tail = node;
90 | } else {
91 |
92 | // put on the tail
93 | this._tail.next = node;
94 | node.prev = this._tail;
95 | this._tail = node;
96 | }
97 |
98 | // update count
99 | this._length++;
100 |
101 | return node;
102 | };
103 |
104 | /*
105 | * Prepends a node to the end of the list.
106 | */
107 | DoublyLinkedList.prototype.prepend = function (data) {
108 | var node = this._createNewNode(data);
109 |
110 | if (this._head === null) {
111 |
112 | // we are empty, so this is the first node
113 | // use the same logic as append
114 | return this.append(data);
115 | } else {
116 |
117 | // place before head
118 | this._head.prev = node;
119 | node.next = this._head;
120 | this._head = node;
121 | }
122 |
123 | // update count
124 | this._length++;
125 |
126 | return node;
127 | };
128 |
129 | /*
130 | * Returns the node at the specified index. The index starts at 0.
131 | */
132 | DoublyLinkedList.prototype.item = function (index) {
133 | if (index >= 0 && index < this._length) {
134 | var node = this._head;
135 | while (index--) {
136 | node = node.next;
137 | }
138 | return node;
139 | }
140 | };
141 |
142 | /*
143 | * Returns the node at the head of the list.
144 | */
145 | DoublyLinkedList.prototype.head = function () {
146 | return this._head;
147 | };
148 |
149 | /*
150 | * Returns the node at the tail of the list.
151 | */
152 | DoublyLinkedList.prototype.tail = function () {
153 | return this._tail;
154 | };
155 |
156 | /*
157 | * Returns the size of the list.
158 | */
159 | DoublyLinkedList.prototype.size = function () {
160 | return this._length;
161 | };
162 |
163 | /*
164 | * Removes the item at the index.
165 | */
166 | DoublyLinkedList.prototype.remove = function (index) {
167 | throw "Not implemented";
168 | };
169 |
170 | exports.DoublyLinkedList = DoublyLinkedList;
171 | })(typeof exports === 'undefined' ? this['DLL'] = {} : exports);
172 |
--------------------------------------------------------------------------------
/test/doubly-linked-list_test.js:
--------------------------------------------------------------------------------
1 | var DLL = require('../doubly-linked-list.js');
2 |
3 | module.exports = {
4 | 'DoublyLinkedList': function (test) {
5 | "use strict";
6 |
7 | test.expect(76);
8 |
9 | var list = new DLL.DoublyLinkedList(),
10 | node = null;
11 |
12 | test.ok(list);
13 | test.strictEqual(list.size(), 0);
14 |
15 | // append items to the list
16 | list.append('data1');
17 | test.strictEqual(list.size(), 1);
18 | node = list.append('data2');
19 | test.strictEqual(list.size(), 2);
20 | list.append('data3');
21 | test.strictEqual(list.size(), 3);
22 | // check pointers of middle node
23 | test.strictEqual(node.prev.data, 'data1');
24 | test.strictEqual(node.next.data, 'data3');
25 |
26 | // get tail item
27 | node = list.tail();
28 | test.ok(node);
29 | test.strictEqual(node.data, 'data3');
30 | test.strictEqual(node.prev.data, 'data2');
31 | test.strictEqual(node.next, null);
32 |
33 | // get at 2
34 | node = list.item(1);
35 | test.ok(node);
36 | test.strictEqual(node.data, 'data2');
37 | // check pointers
38 | test.strictEqual(node.prev.data, 'data1');
39 | test.strictEqual(node.next.data, 'data3');
40 |
41 | // prepend an item
42 | node = list.prepend('data4');
43 | test.strictEqual(node.prev, null);
44 | test.strictEqual(node.next.data, 'data1');
45 | test.strictEqual(list.size(), 4);
46 |
47 | // get new item
48 | node = list.head();
49 | test.ok(node);
50 | test.strictEqual(node.data, 'data4');
51 | test.strictEqual(node.prev, null);
52 | test.strictEqual(node.next.data, 'data1');
53 |
54 | // get at 2
55 | node = list.item(1);
56 | test.ok(node);
57 | test.strictEqual(node.data, 'data1');
58 | // check pointers
59 | test.strictEqual(node.prev.data, 'data4');
60 | test.strictEqual(node.next.data, 'data2');
61 |
62 | // Test prepend on an empty list
63 | var prependList = new DLL.DoublyLinkedList();
64 | prependList.prepend('cake');
65 | test.strictEqual(prependList.size(), 1);
66 | test.strictEqual(prependList.head().data, 'cake');
67 |
68 | // Test node removal
69 | var makeListOfSizeN = function(n) {
70 | list = new DLL.DoublyLinkedList();
71 |
72 | for (var i = 0; i < n; i++) {
73 | list.append("thing" + i);
74 | }
75 |
76 | return list;
77 | };
78 |
79 | var removalList = makeListOfSizeN(3);
80 | removalList.item(1).remove();
81 | test.strictEqual(removalList.size(), 2);
82 | test.strictEqual(removalList.head().data, 'thing0');
83 | test.strictEqual(removalList.head().prev, null);
84 | test.strictEqual(removalList.head().next.data, 'thing2');
85 | test.strictEqual(removalList.tail().data, 'thing2');
86 | test.strictEqual(removalList.tail().prev.data, 'thing0');
87 | test.strictEqual(removalList.tail().next, null);
88 |
89 | removalList = makeListOfSizeN(3);
90 | removalList.item(0).remove();
91 | test.strictEqual(removalList.size(), 2);
92 | test.strictEqual(removalList.head().data, 'thing1');
93 | test.strictEqual(removalList.head().prev, null);
94 | test.strictEqual(removalList.head().next.data, 'thing2');
95 | test.strictEqual(removalList.tail().data, 'thing2');
96 | test.strictEqual(removalList.tail().prev.data, 'thing1');
97 | test.strictEqual(removalList.tail().next, null);
98 |
99 | removalList = makeListOfSizeN(3);
100 | removalList.item(2).remove();
101 | test.strictEqual(removalList.size(), 2);
102 | test.strictEqual(removalList.head().data, 'thing0');
103 | test.strictEqual(removalList.head().prev, null);
104 | test.strictEqual(removalList.head().next.data, 'thing1');
105 | test.strictEqual(removalList.tail().data, 'thing1');
106 | test.strictEqual(removalList.tail().prev.data, 'thing0');
107 | test.strictEqual(removalList.tail().next, null);
108 |
109 | removalList = makeListOfSizeN(2);
110 | removalList.item(0).remove();
111 | test.strictEqual(removalList.size(), 1);
112 | test.strictEqual(removalList.head().data, 'thing1');
113 | test.strictEqual(removalList.tail().data, 'thing1');
114 | test.strictEqual(removalList.head().prev, null);
115 | test.strictEqual(removalList.head().next, null);
116 |
117 | removalList = makeListOfSizeN(2);
118 | removalList.item(1).remove();
119 | test.strictEqual(removalList.size(), 1);
120 | test.strictEqual(removalList.head().data, 'thing0');
121 | test.strictEqual(removalList.tail().data, 'thing0');
122 | test.strictEqual(removalList.head().prev, null);
123 | test.strictEqual(removalList.head().next, null);
124 |
125 | removalList = makeListOfSizeN(1);
126 | removalList.item(0).remove();
127 | test.strictEqual(removalList.size(), 0);
128 | test.strictEqual(removalList.head(), null);
129 | test.strictEqual(removalList.tail(), null);
130 |
131 | // Test prepending before a specific node
132 | prependList = makeListOfSizeN(2);
133 | prependList.tail().prepend('new');
134 | var newNode = prependList.item(1);
135 | test.strictEqual(prependList.size(), 3);
136 | test.strictEqual(newNode.data, 'new');
137 | test.strictEqual(newNode.prev.data, 'thing0');
138 | test.strictEqual(newNode.next.data, 'thing1');
139 |
140 | prependList = makeListOfSizeN(2);
141 | prependList.head().prepend('new');
142 | test.strictEqual(prependList.size(), 3);
143 | newNode = prependList.head();
144 | test.strictEqual(newNode.data, 'new');
145 | test.strictEqual(newNode.next.data, 'thing0');
146 |
147 | // Test appending after a specific node
148 | var appendList = makeListOfSizeN(2);
149 | appendList.head().append('new');
150 | newNode = appendList.item(1);
151 | test.strictEqual(appendList.size(), 3);
152 | test.strictEqual(newNode.data, 'new');
153 | test.strictEqual(newNode.prev.data, 'thing0');
154 | test.strictEqual(newNode.next.data, 'thing1');
155 |
156 | appendList = makeListOfSizeN(2);
157 | appendList.tail().append('new');
158 | test.strictEqual(appendList.size(), 3);
159 | newNode = appendList.tail();
160 | test.strictEqual(newNode.data, 'new');
161 | test.strictEqual(newNode.prev.data, 'thing1');
162 |
163 | test.done();
164 | }
165 | };
--------------------------------------------------------------------------------