diff --git a/spec/doubleLinkedList_test.js b/spec/doubleLinkedList_test.js new file mode 100644 index 0000000..84d0797 --- /dev/null +++ b/spec/doubleLinkedList_test.js @@ -0,0 +1,156 @@ +import chai, { expect } from 'chai' +import chaiChange from 'chai-change' +import DoubleLinkedList from '../src/doubleLinkedList' + +chai.use(chaiChange) + +describe('Double Linked List', () => { + 'use strict' + + it('exists', () => { + expect(DoubleLinkedList).to.be.a('function') + }) + + describe('insert()', () => { + it('Inserts a node (with the provided data) to the tail of the list', () => { + const doubleLinkedList = new DoubleLinkedList() + + expect(() => doubleLinkedList.insert('foo')) + .to.alter(() => doubleLinkedList.count, { from: 0, to: 1 }) + expect(doubleLinkedList.getHeadNode().data) + .to.equal('foo') + expect(() => doubleLinkedList.insert('boo')) + .to.alter(() => doubleLinkedList.count, { from: 1, to: 2 }) + expect(doubleLinkedList.getTailNode().data) + .to.equal('boo') + }) + }) + + describe('insertFirst()', () => { + it('Inserts a node (with the provided data) to the head of the list', () => { + const doubleLinkedList = new DoubleLinkedList() + + expect(() => doubleLinkedList.insertFirst('foo')) + .to.alter(() => doubleLinkedList.count, { from: 0, to: 1 }) + expect(() => doubleLinkedList.insertFirst('boo')) + .to.alter(() => doubleLinkedList.count, { from: 1, to: 2 }) + console.log(doubleLinkedList) + expect(doubleLinkedList.getHeadNode().data) + .to.equal('boo') + expect(doubleLinkedList.getTailNode().data) + .to.equal('foo') + }) + }) + describe('contains()', () => { + it('Determines whether or not the list contains the provided data', () => { + const doubleLinkedList = new DoubleLinkedList() + doubleLinkedList.insertFirst('foo') + doubleLinkedList.insertFirst('boo') + + expect(doubleLinkedList.contains('foo')) + .to.be.true + expect(doubleLinkedList.contains('tap')) + .to.equal(false) + }) + }) + + describe('find()', () => { + it('Determines whether or not the list contains the provided data', () => { + const doubleLinkedList = new DoubleLinkedList() + doubleLinkedList.insertFirst('foo') + doubleLinkedList.insertFirst('boo') + doubleLinkedList.insertFirst('shoo') + doubleLinkedList.insertFirst('test') + console.log(doubleLinkedList) + + expect(doubleLinkedList.find('shoo').data) + .to.equal('shoo') + expect(doubleLinkedList.find('tap')) + .to.equal(-1) + }) + }) + + describe('clear()', () => { + it('Clears the list of all nodes/data', () => { + const doubleLinkedList = new DoubleLinkedList() + doubleLinkedList.insertFirst('foo') + doubleLinkedList.insertFirst('boo') + doubleLinkedList.insert('shoo') + + expect(doubleLinkedList.clear()) + .to.be.undefined + }) + }) + + describe('removeFirst()', () => { + it('Removes the head node from the list', () => { + const doubleLinkedList = new DoubleLinkedList() + doubleLinkedList.insertFirst('foo') + doubleLinkedList.insertFirst('boo') + doubleLinkedList.insert('shoo') + + expect(doubleLinkedList.removeFirst()) + .to.be.undefined + expect(doubleLinkedList.getHeadNode().data) + .to.equal('foo') + expect(doubleLinkedList.removeFirst()) + .to.be.undefined + expect(doubleLinkedList.getHeadNode().data) + .to.equal('shoo') + }) + }) + + describe('remove()', () => { + it('Removes the tail node from the list', () => { + const doubleLinkedList = new DoubleLinkedList() + doubleLinkedList.insertFirst('foo') + doubleLinkedList.insertFirst('boo') + doubleLinkedList.insert('shoo') + + expect(doubleLinkedList.remove()) + .to.be.undefined + expect(doubleLinkedList.getTailNode().data) + .to.equal('foo') + expect(doubleLinkedList.remove()) + .to.be.undefined + expect(doubleLinkedList.getTailNode().data) + .to.equal('boo') + }) + }) + + describe('insertAfter()', () => { + it('Inserts a node (with data) after the first node containing "(item)"', () => { + const doubleLinkedList = new DoubleLinkedList() + doubleLinkedList.insertFirst('foo') + doubleLinkedList.insertFirst('boo') + doubleLinkedList.insert('shoo') + + expect(doubleLinkedList.insertAfter('shoo', 'moo')) + .to.be.undefined + expect(doubleLinkedList.getTailNode().data) + .to.equal('moo') + expect(doubleLinkedList.insertAfter('moo', 'doo')) + .to.be.undefined + expect(doubleLinkedList.getTailNode().data) + .to.equal('doo') + }) + }) + + describe.only('insertBefore()', () => { + it('Inserts a node (with data) before the first node containing (item)', () => { + const doubleLinkedList = new DoubleLinkedList() + doubleLinkedList.insertFirst('foo') + doubleLinkedList.insertFirst('boo') + doubleLinkedList.insert('shoo') + + expect(doubleLinkedList.insertBefore('shoo', 'moo')) + .to.be.undefined + expect(doubleLinkedList.getTailNode().data) + .to.equal('shoo') + expect(doubleLinkedList.insertBefore('shoo', 'doo')) + .to.be.undefined + expect(doubleLinkedList.getTailNode().data) + .to.equal('shoo') + }) + }) +}) diff --git a/spec/linkedList_test.js b/spec/linkedList_test.js index 0fdab73..48219ae 100644 --- a/spec/linkedList_test.js +++ b/spec/linkedList_test.js @@ -132,7 +132,7 @@ describe('Linked List', () => { }) }) - describe.only('insertBefore()', () => { + describe('insertBefore()', () => { it('Inserts a node (with data) before the first node containing (item)', () => { const linkedList = new LinkedList() linkedList.insertFirst('foo') diff --git a/src/doubleLinkedList.js b/src/doubleLinkedList.js new file mode 100644 index 0000000..0c809a1 --- /dev/null +++ b/src/doubleLinkedList.js @@ -0,0 +1,150 @@ +'use strict' + +class Node { + constructor(data) { + this.data = data + this.next = null + this.prev = null + } +} + +export default class DoubleLinkedList { + constructor() { + this.count = 0 + this.head = null + this.tail = null + } + + getHeadNode() { + return this.head + } + + getTailNode() { + return this.tail + } + + + find(item) { + let currentNode = this.head + while (currentNode) { + if (currentNode.data === item) { + return currentNode + } else { + currentNode = currentNode.next + } + } + return -1 + } + + clear() { + this.head = null + this.tail = null + this.count = 0 + } + + insert(item) { + const newNode = new Node(item) + + if (this.head === null) { + this.head = newNode + } else { + this.tail.next = newNode + newNode.prev = this.tail + } + this.tail = newNode + this.count++ + + return newNode + } + + insertFirst(item) { + const newNode = new Node(item) + + if (this.head) { + this.head.prev = newNode + newNode.next = this.head + } else { + this.tail = newNode + } + + this.head = newNode + this.count++ + + return newNode + + } + + insertBefore(item, data) { + const newNode = new Node(data) + newNode.next = this.find(item) + + if (newNode.next === this.head) { + this.head.prev = newNode + this.head = newNode + } else { + let currentNode = this.head + while (currentNode.next !== newNode.next) { + currentNode = currentNode.next + } + newNode.prev = currentNode + currentNode.next = newNode + } + this.count++ + + } + + insertAfter(item, data) { + const newNode = new Node(data) + + if (this.find(item) === this.tail { + newNode.prev = this.tail + this.tail = newNode + } + newNode.next = this.find(item).next + newNode.prev = this.find(item) + this.find(item).next = newNode + this.count++ + + } + + remove() { + if (this.count === 1) { + this.clear() + } else { + let currentNode = this.getHeadNode() + while (currentNode.next.next) { + currentNode = currentNode.next + } + currentNode.next = null + this.count-- + this.tail = currentNode + } + + } + + removeFirst() { + if (this.count === 1) { + this.clear() + } else { + this.head = this.getHeadNode().next + this.count-- + } + + } + + isEmpty() { + if(!this.head){ + return null + } else { + return this.data + } + } + + size() { + return this.count + } + + contains(item) { + return this.find(item) !== -1 + } +}