Skip to content

Commit

Permalink
Initial linked list implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
mixonic committed Aug 7, 2015
1 parent 39fe494 commit 3dd658e
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/js/utils/linked-item.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default class LinkedItem {
constructor() {
this.next = null;
this.prev = null;
}
}
73 changes: 73 additions & 0 deletions src/js/utils/linked-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
export default class LinkedList {
constructor() {
this.head = null;
this.tail = null;
}
prepend(item) {
this.insertBefore(item, this.head);
}
append(item) {
this.insertBefore(item, null);
}
insertAfter(item, prevItem) {
let nextItem = null;
if (prevItem) {
nextItem = prevItem.next;
}
this.insertBefore(item, nextItem);
}
insertBefore(item, nextItem) {
this.remove(item);
if (nextItem && nextItem.prev) {
// middle of the items
let prevItem = nextItem.prev;
item.next = nextItem;
nextItem.prev = item;
item.prev = prevItem;
prevItem.next = item;
} else if (nextItem) {
// first item
if (this.head === nextItem) {
item.next = nextItem;
nextItem.prev = item;
} else {
this.tail = item;
}
this.head = item;
} else {
// last item
if (this.tail) {
item.prev = this.tail;
this.tail.next = item;
}
if (!this.head) {
this.head = item;
}
this.tail = item;
}
}
remove(item) {
if (item.next && item.prev) {
// Middle of the list
item.next.prev = item.prev;
item.prev.next = item.next;
} else {
if (item === this.head) {
// Head of the list
if (item.next) {
item.next.prev = null;
}
this.head = item.next;
}
if (item === this.tail) {
// Tail of the list
if (item.prev) {
item.prev.next = null;
}
this.tail = item.prev;
}
}
item.prev = null;
item.next = null;
}
}
148 changes: 148 additions & 0 deletions tests/unit/utils/linked-list-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
const {module, test} = QUnit;

import LinkedList from 'content-kit-editor/utils/linked-list';
import LinkedItem from 'content-kit-editor/utils/linked-item';

module('Unit: Utils: LinkedList');

test('initial state', (assert) => {
let list = new LinkedList();
assert.equal(list.head, null, 'head is null');
assert.equal(list.tail, null ,'tail is null');
});

['append', 'prepend', 'insertBefore', 'insertAfter'].forEach(method => {
test(`#${method} initial item`, (assert) => {
let list = new LinkedList();
let item = new LinkedItem();
list[method](item);
assert.equal(list.head, item, 'head is item');
assert.equal(list.tail, item, 'tail is item');
assert.equal(item.next, null, 'item next is null');
assert.equal(item.prev, null, 'item prev is null');
});
});

test(`#append second item`, (assert) => {
let list = new LinkedList();
let itemOne = new LinkedItem();
let itemTwo = new LinkedItem();
list.append(itemOne);
list.append(itemTwo);
assert.equal(list.head, itemOne, 'head is itemOne');
assert.equal(list.tail, itemTwo, 'tail is itemTwo');
assert.equal(itemOne.prev, null, 'itemOne prev is null');
assert.equal(itemOne.next, itemTwo, 'itemOne next is itemTwo');
assert.equal(itemTwo.prev, itemOne, 'itemTwo prev is itemOne');
assert.equal(itemTwo.next, null, 'itemTwo next is null');
});

test(`#prepend first item`, (assert) => {
let list = new LinkedList();
let itemOne = new LinkedItem();
let itemTwo = new LinkedItem();
list.prepend(itemTwo);
list.prepend(itemOne);
assert.equal(list.head, itemOne, 'head is itemOne');
assert.equal(list.tail, itemTwo, 'tail is itemTwo');
assert.equal(itemOne.prev, null, 'itemOne prev is null');
assert.equal(itemOne.next, itemTwo, 'itemOne next is itemTwo');
assert.equal(itemTwo.prev, itemOne, 'itemTwo prev is itemOne');
assert.equal(itemTwo.next, null, 'itemTwo next is null');
});

test(`#insertBefore a middle item`, (assert) => {
let list = new LinkedList();
let itemOne = new LinkedItem();
let itemTwo = new LinkedItem();
let itemThree = new LinkedItem();
list.prepend(itemOne);
list.append(itemThree);
list.insertBefore(itemTwo, itemThree);
assert.equal(list.head, itemOne, 'head is itemOne');
assert.equal(list.tail, itemThree, 'tail is itemThree');
assert.equal(itemOne.prev, null, 'itemOne prev is null');
assert.equal(itemOne.next, itemTwo, 'itemOne next is itemTwo');
assert.equal(itemTwo.prev, itemOne, 'itemTwo prev is itemOne');
assert.equal(itemTwo.next, itemThree, 'itemTwo next is itemThree');
assert.equal(itemThree.prev, itemTwo, 'itemThree prev is itemTwo');
assert.equal(itemThree.next, null, 'itemThree next is null');
});

test(`#insertAfter a middle item`, (assert) => {
let list = new LinkedList();
let itemOne = new LinkedItem();
let itemTwo = new LinkedItem();
let itemThree = new LinkedItem();
list.prepend(itemOne);
list.append(itemThree);
list.insertAfter(itemTwo, itemOne);
assert.equal(list.head, itemOne, 'head is itemOne');
assert.equal(list.tail, itemThree, 'tail is itemThree');
assert.equal(itemOne.prev, null, 'itemOne prev is null');
assert.equal(itemOne.next, itemTwo, 'itemOne next is itemTwo');
assert.equal(itemTwo.prev, itemOne, 'itemTwo prev is itemOne');
assert.equal(itemTwo.next, itemThree, 'itemTwo next is itemThree');
assert.equal(itemThree.prev, itemTwo, 'itemThree prev is itemTwo');
assert.equal(itemThree.next, null, 'itemThree next is null');
});

test(`#remove an only item`, (assert) => {
let list = new LinkedList();
let item = new LinkedItem();
list.append(item);
list.remove(item);
assert.equal(list.head, null, 'head is null');
assert.equal(list.tail, null, 'tail is null');
assert.equal(item.prev, null, 'item prev is null');
assert.equal(item.next, null, 'item next is null');
});

test(`#remove a first item`, (assert) => {
let list = new LinkedList();
let itemOne = new LinkedItem();
let itemTwo = new LinkedItem();
list.append(itemOne);
list.append(itemTwo);
list.remove(itemOne);
assert.equal(list.head, itemTwo, 'head is itemTwo');
assert.equal(list.tail, itemTwo, 'tail is itemTwo');
assert.equal(itemOne.prev, null, 'itemOne prev is null');
assert.equal(itemOne.next, null, 'itemOne next is null');
assert.equal(itemTwo.prev, null, 'itemTwo prev is null');
assert.equal(itemTwo.next, null, 'itemTwo next is null');
});

test(`#remove a second item`, (assert) => {
let list = new LinkedList();
let itemOne = new LinkedItem();
let itemTwo = new LinkedItem();
list.append(itemOne);
list.append(itemTwo);
list.remove(itemTwo);
assert.equal(list.head, itemOne, 'head is itemOne');
assert.equal(list.tail, itemOne, 'tail is itemOne');
assert.equal(itemOne.prev, null, 'itemOne prev is null');
assert.equal(itemOne.next, null, 'itemOne next is null');
assert.equal(itemTwo.prev, null, 'itemTwo prev is null');
assert.equal(itemTwo.next, null, 'itemTwo next is null');
});

test(`#remove a middle item`, (assert) => {
let list = new LinkedList();
let itemOne = new LinkedItem();
let itemTwo = new LinkedItem();
let itemThree = new LinkedItem();
list.append(itemOne);
list.append(itemTwo);
list.append(itemThree);
list.remove(itemTwo);
assert.equal(list.head, itemOne, 'head is itemOne');
assert.equal(list.tail, itemThree, 'tail is itemThree');
assert.equal(itemOne.prev, null, 'itemOne prev is null');
assert.equal(itemOne.next, itemThree, 'itemOne next is itemThree');
assert.equal(itemTwo.prev, null, 'itemTwo prev is null');
assert.equal(itemTwo.next, null, 'itemTwo next is null');
assert.equal(itemThree.prev, itemOne, 'itemThree prev is itemOne');
assert.equal(itemThree.next, null, 'itemThree next is null');
});

0 comments on commit 3dd658e

Please sign in to comment.