diff --git a/packages/singly-linked-list/README.md b/packages/singly-linked-list/README.md index 4126beb..e448045 100644 --- a/packages/singly-linked-list/README.md +++ b/packages/singly-linked-list/README.md @@ -24,17 +24,21 @@ _I wholehearedly acknowledge that the basic data structure space is populated wi ## Basic Usage -Install with npm : +Install: ```bash -npm install singly-linked-list --save +npm install singly-linked-list +``` + +```bash +yarn add singly-linked-list ``` Basic usage example below. _Note: it does not cover all the available methods, rather just highlights the main functionality to get up and running with this data structure. For a description of all the methods, see the API section._ ```javascript -var LinkedList = require('singly-linked-list'); -var list = new LinkedList(); +import LinkedList from 'singly-linked-list'; +const list = new LinkedList(); list.isEmpty(); // --> true @@ -47,8 +51,8 @@ list.insert('data item 4'); // 'data item 1', ... ,'data item 4' // alternatively, the list can be initialized with an array -var initialData = ['data item 1', 'data item 2', 'data item 3', 'data item 4']; -var populatedList = new LinkedList(initialData); +const initialData = ['data item 1', 'data item 2', 'data item 3', 'data item 4']; +const populatedList = new LinkedList(initialData); // populatedList contains: // 'data item 1', ... ,'data item 4' @@ -83,100 +87,102 @@ list.isEmpty(); **Available methods for a singly-linked-list instance:** -- ### getHeadNode() +- `getHeadNode()` Returns the first node in the list -- ### getTailNode() +- `getTailNode()` Returns the last node in the list -- ### isEmpty() +- `isEmpty()` Determines if the list is empty or not. Returns true if is empty, false otherwise. -- ### getSize() +- `getSize()` Returns the size of the list, or number of nodes -- ### clear() +- `clear()` Clears the list of all nodes/data -- ### insert(data) +- `insert(data)` Inserts a node (with the provided `data`) to the end of the list -- ### insertFirst(data) +- `insertFirst(data)` Inserts a node (with the provided `data`) to the front of the list -- ### insertAt(index, data) +- `insertAt(index, data)` Inserts a node (with the provided `data`) at the `index` indicated. -- ### insertBefore(nodeData, dataToInsert) +- `insertBefore(nodeData, dataToInsert)` Inserts a node (with the `dataToInsert`) _before_ the first node containing `nodeData` -- ### insertAfter(nodeData, dataToInsert) +- `insertAfter(nodeData, dataToInsert)` Inserts a node (with the `dataToInsert`) _after_ the first node containing `nodeData` -- ### remove() +- `remove()` Removes the tail node from the list -- ### removeFirst() +- `removeFirst()` Removes the head node from the list -- ### removeAt(index) +- `removeAt(index)` Removes the node at the `index` provided -- ### removeNode(nodeData) +- `removeNode(nodeData)` Removes the first node that contains the `nodeData` provided -- ### indexOf(nodeData) +- `indexOf(nodeData)` Returns the index of the first node containing the provided `nodeData`. If a node cannot be found containing the provided data, null is returned. -- ### contains(nodeData) +- `contains(nodeData)` Determines whether or not the list contains the provided `nodeData` -- ### find(nodeData) +- `find(nodeData)` Returns the fist node containing the provided `nodeData`. If a node cannot be found containing the provided data, null is returned. -- ### findAt(index) +- `findAt(index)` Returns the node at the location provided by `index` -- ### forEach(fn) +- `forEach(fn)` Utility function to iterate over the list and call the `fn` provided on each node, or element, of the list -- ### toArray() +- `toArray()` Returns an array of all the data contained in the list -- ### printList() +- `printList()` + Prints to the console the data property of each node in the list **Available methods for an individual node instance:** -- ### getData() +- `getData()` Returns the data of the the node -- ### hasNext() +- `hasNext()` Returns whether or not the node has a pointer to the next node -- ### toString() +- `toString()` + Returns a string represenation of the node. If the data is an object, it returns the JSON.stringify version of the object. Otherwise, it simply returns the data --- diff --git a/packages/singly-linked-list/package.json b/packages/singly-linked-list/package.json index deceab6..c5ec86b 100644 --- a/packages/singly-linked-list/package.json +++ b/packages/singly-linked-list/package.json @@ -1,6 +1,6 @@ { "name": "singly-linked-list", - "version": "1.4.1", + "version": "2.0.0", "description": "Javascript implementation of singly linked list data structure", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/singly-linked-list/src/__tests__/list-node.spec.ts b/packages/singly-linked-list/src/__tests__/list-node.spec.ts index 6562383..611d006 100644 --- a/packages/singly-linked-list/src/__tests__/list-node.spec.ts +++ b/packages/singly-linked-list/src/__tests__/list-node.spec.ts @@ -1,14 +1,12 @@ import ListNode from '../lib/list-node'; describe('List Node', () => { - [null, undefined, '', 'test data', 42].forEach(data => { - it(`exists when instantiated with ${data}`, () => { - const node = new ListNode(data); - expect(node).toBeTruthy(); - }); + it.each([null, undefined, '', 'test data', 42])('exists when instantiated with %s', (data) => { + const node = new ListNode(data); + expect(node).toBeTruthy(); }); - [ + it.each( [ { data: 'test data', type: 'string' @@ -21,13 +19,11 @@ describe('List Node', () => { data: null, type: 'object' } - ].forEach(testInput => { - it(`returns the correct (${testInput.type}) data for ${testInput.data}`, () => { - let node = new ListNode(testInput.data); + ])('returns the correct $type data for $data', (input) => { + let node = new ListNode(input.data); let data = node.getData(); - expect(typeof data).toEqual(testInput.type); - expect(data).toEqual(testInput.data); - }); + expect(typeof data).toEqual(input.type); + expect(data).toEqual(input.data); }); it('returns the correct (object) data', () => { @@ -52,7 +48,7 @@ describe('List Node', () => { expect(firstNode.hasNext()).toBeFalsy(); }); - [ + it.each([ { data: { name: 'test item', number: 1 }, expected: '{"name":"test item","number":1}' @@ -65,10 +61,9 @@ describe('List Node', () => { data: 42, expected: '42' } - ].forEach(testInput => { - it(`returns a proper string representation for ${JSON.stringify(testInput.data)}`, () => { - const node = new ListNode(testInput.data); - expect(node.toString()).toEqual(testInput.expected); - }); + ])('returns a proper string for json data', (input) => { + const node = new ListNode(input.data); + expect(node.toString()).toEqual(input.expected); }); }); + diff --git a/packages/singly-linked-list/src/__tests__/list.spec.ts b/packages/singly-linked-list/src/__tests__/list.spec.ts index 3cee06b..833df91 100644 --- a/packages/singly-linked-list/src/__tests__/list.spec.ts +++ b/packages/singly-linked-list/src/__tests__/list.spec.ts @@ -1,9 +1,18 @@ import LinkedList from '../index'; +type TestObject = { + id?: number, + name?: string, + key?: string, + value?: string +}; + +type TestData = TestObject | string; + // Utility function to populate the list with dummy data. // The number of nodes added will be specified by the 'numNodes' // parameter. -const populateList = (aList: LinkedList, numNodes: number): void => { +const populateList = (aList: LinkedList, numNodes: number): void => { for (let i = 0; i < numNodes; i++) { aList.insert('test item ' + (i + 1)); } @@ -12,14 +21,14 @@ const populateList = (aList: LinkedList, numNodes: number): void => { // Utility function to populate the list with dummy data. // The number of nodes added will be specified by the 'numNodes' // parameter. -const populateArray = (arr: any[], numItems: number): void => { +const populateArray = (arr: string[], numItems: number): void => { for (let i = 0; i < numItems; i++) { arr.push('test item ' + (i + 1)); } }; describe('Linked List', () => { - let list: LinkedList; + let list: LinkedList; beforeEach(() => { list = new LinkedList(); @@ -318,7 +327,7 @@ describe('Linked List', () => { }); describe('pre-initialized list', () => { - let populatedList: LinkedList; + let populatedList: LinkedList; beforeEach(() => { const arr: any[] = []; diff --git a/packages/singly-linked-list/src/index.ts b/packages/singly-linked-list/src/index.ts index 36823a7..162c014 100644 --- a/packages/singly-linked-list/src/index.ts +++ b/packages/singly-linked-list/src/index.ts @@ -19,20 +19,20 @@ import ListNode from './lib/list-node'; * structure. * */ -class LinkedList { - head: ListNode; - tail: ListNode; +class LinkedList { + head: ListNode; + tail: ListNode; size: number; - iterator: ListIterator; + iterator: ListIterator; /** * Creates a LinkedList instance. Each instance has a head node, a tail node * and a size, which represents the number of nodes in the list. * * @constructor - * @param {array} arr The data to initialize with the list + * @param {T[]} arr The data to initialize with the list */ - constructor(arr?: any[]) { + constructor(arr?: T[]) { this.head = null; this.tail = null; this.size = 0; @@ -54,28 +54,28 @@ class LinkedList { /** * Creates a new Node object with 'data' assigned to the node's data property * - * @param {object|string|number} data The data to initialize with the node - * @returns {object} Node object intialized with 'data' + * @param {T} data The data to initialize with the node + * @returns {ListNode} Node object intialized with 'data' */ - createNewNode(data: any): ListNode { + createNewNode(data: T): ListNode { return new ListNode(data); } /** * Returns the first node in the list, commonly referred to as the 'head' node * - * @returns {object} the head node of the list + * @returns {ListNode} the head node of the list */ - getHeadNode(): ListNode { + getHeadNode(): ListNode { return this.head; } /** * Returns the last node in the list, commonly referred to as the 'tail' node * - * @returns {object} the tail node of the list + * @returns {ListNode} the tail node of the list */ - getTailNode(): ListNode { + getTailNode(): ListNode { return this.tail; } @@ -110,9 +110,9 @@ class LinkedList { * Utility function to iterate over the list and call the fn provided * on each node, or element, of the list * - * @param {object} cb The function to call on each node of the list + * @param {function} cb The function to call on each node of the list */ - forEach(cb: (node: ListNode) => void): void { + forEach(cb: (node: ListNode) => void): void { this.iterator.reset(); this.iterator.each(cb); } @@ -120,10 +120,10 @@ class LinkedList { /** * Returns an array of all the data contained in the list * - * @returns {array} the array of all the data from the list - */ - toArray(): ListNode[] { - const listArray: ListNode[] = []; + * @returns {T[]} the array of all the data from the list + */ + toArray(): T[] { + const listArray: T[] = []; this.forEach(node => { listArray.push(node.getData()); }); @@ -143,10 +143,10 @@ class LinkedList { /** * Inserts a node with the provided data to the end of the list * - * @param {object|string|number} data The data to initialize with the node + * @param {T} data The data to initialize with the node * @returns {boolean} true if insert operation was successful */ - insert(data: any): boolean { + insert(data: T): boolean { const newNode = this.createNewNode(data); if (this.isEmpty()) { this.head = this.tail = newNode; @@ -166,10 +166,10 @@ class LinkedList { /** * Inserts a node with the provided data to the front of the list * - * @param {object|string|number} data The data to initialize with the node + * @param {T} data The data to initialize with the node * @returns {boolean} true if insert operation was successful */ - insertFirst(data: any): boolean { + insertFirst(data: T): boolean { if (this.isEmpty()) { this.insert(data); } else { @@ -186,10 +186,10 @@ class LinkedList { * Inserts a node with the provided data at the index indicated. * * @param {number} index The index in the list to insert the new node - * @param {object|string|number} data The data to initialize with the node + * @param {T} data The data to initialize with the node * @returns {boolean} true if insert operation was successful */ - insertAt(index: number, data: any): boolean { + insertAt(index: number, data: T): boolean { let current = this.getHeadNode(), previous = null, position = 0; @@ -228,12 +228,12 @@ class LinkedList { /** * Inserts a node before the first node containing the provided data * - * @param {object|string|number} nodeData The data of the node to + * @param {T} nodeData The data of the node to * find to insert the new node before - * @param {object|string|number} dataToInsert The data to initialize with the node + * @param {T} dataToInsert The data to initialize with the node * @returns {boolean} true if insert operation was successful */ - insertBefore(nodeData: any, dataToInsert: any): boolean { + insertBefore(nodeData: T, dataToInsert: T): boolean { const index = this.indexOf(nodeData); return this.insertAt(index, dataToInsert); } @@ -241,12 +241,12 @@ class LinkedList { /** * Inserts a node after the first node containing the provided data * - * @param {object|string|number} nodeData The data of the node to + * @param {T} nodeData The data of the node to * find to insert the new node after - * @param {object|string|number} dataToInsert The data to initialize with the node + * @param {T} dataToInsert The data to initialize with the node * @returns {boolean} true if insert operation was successful */ - insertAfter(nodeData: any, dataToInsert: any): boolean { + insertAfter(nodeData: T, dataToInsert: T): boolean { const index = this.indexOf(nodeData); const size = this.getSize(); @@ -267,9 +267,9 @@ class LinkedList { * In order to remove the tail node a handle to the node before the * tail node is required, which requires a O(n) operation. * - * @returns the node that was removed + * @returns {ListNode} the node that was removed */ - remove(): ListNode { + remove(): ListNode { if (this.isEmpty()) { return null; } @@ -286,7 +286,7 @@ class LinkedList { // more than one node in the list } else { - let current; + let current: ListNode; // iterate over the list until we reach the second to last node, // the node whose next pointer points the the tail node while (this.iterator.hasNext()) { @@ -305,9 +305,9 @@ class LinkedList { /** * Removes the head node from the list * - * @returns the node that was removed + * @returns {ListNode} the node that was removed */ - removeFirst(): ListNode { + removeFirst(): ListNode { if (this.isEmpty()) { return null; } @@ -327,11 +327,11 @@ class LinkedList { * Removes the node at the index provided * * @param {number} index The index of the node to remove - * @returns the node that was removed + * @returns {ListNode} the node that was removed */ - removeAt(index: number): ListNode { - let current: ListNode = this.getHeadNode(), - previous: ListNode = null, + removeAt(index: number): ListNode { + let current: ListNode = this.getHeadNode(), + previous: ListNode = null, position = 0; // check for index out-of-bounds @@ -365,10 +365,10 @@ class LinkedList { /** * Removes the first node that contains the data provided * - * @param {object|string|number} data The data of the node to remove - * @returns the node that was removed + * @param {T} data The data of the node to remove + * @returns {ListNode} the node that was removed */ - removeNode(data: any): ListNode { + removeNode(data: T): ListNode { const index = this.indexOf(data); return this.removeAt(index); } @@ -377,12 +377,12 @@ class LinkedList { * Returns the index of the first node containing the provided data. If * a node cannot be found containing the provided data, -1 is returned. * - * @param {object|string|number} data The data of the node to find - * @returns the index of the node if found, -1 otherwise + * @param {T} data The data of the node to find + * @returns {number} the index of the node if found, -1 otherwise */ - indexOf(data: any): number { + indexOf(data: T): number { this.iterator.reset(); - let current: ListNode; + let current: ListNode; let index = 0; // iterate over the list (keeping track of the index value) until @@ -402,11 +402,11 @@ class LinkedList { /** * Determines whether or not the list contains the provided data * - * @param {object|string|number} data The data to check if the list + * @param {T} data The data to check if the list * contains - * @returns the true if the list contains data, false otherwise + * @returns {boolean} the true if the list contains data, false otherwise */ - contains(data: any): boolean { + contains(data: T): boolean { return this.indexOf(data) > -1; } @@ -414,13 +414,13 @@ class LinkedList { * Returns the fist node containing the provided data. If a node * cannot be found containing the provided data, null is returned. * - * @param {object|string|number} data The data of the node to find - * @returns the node if found, null otherwise + * @param {T} data The data of the node to find + * @returns {ListNode | null} the node if found, null otherwise */ - find(data: any): ListNode { + find(data: T): ListNode { this.iterator.reset(); - let current: ListNode; + let current: ListNode; // iterate over the list until we find the node containing the data // we are looking for while (this.iterator.hasNext()) { @@ -439,9 +439,9 @@ class LinkedList { * Returns the node at the location provided by index * * @param {number} index The index of the node to return - * @returns the node located at the index provided. + * @returns {ListNode} the node located at the index provided. */ - findAt(index: number): ListNode { + findAt(index: number): ListNode { // if idx is out of bounds or fn called on empty list, return -1 if (this.isEmpty() || index > this.getSize() - 1) { return null; diff --git a/packages/singly-linked-list/src/lib/list-iterator.ts b/packages/singly-linked-list/src/lib/list-iterator.ts index 94b24d0..12e2ad6 100644 --- a/packages/singly-linked-list/src/lib/list-iterator.ts +++ b/packages/singly-linked-list/src/lib/list-iterator.ts @@ -24,9 +24,9 @@ import ListNode from './list-node'; * of separation of concerns. * */ -class ListIterator { - list: LinkedList; - currentNode: ListNode; +class ListIterator { + list: LinkedList; + currentNode: ListNode; /** * Creates an iterator instance to iterate over the linked list provided. @@ -34,7 +34,7 @@ class ListIterator { * @constructor * @param {object} theList the linked list to iterate over */ - constructor(theList: LinkedList) { + constructor(theList: LinkedList) { this.list = theList; // a pointer the current node in the list that will be returned. @@ -42,7 +42,7 @@ class ListIterator { this.currentNode = null; } - next(): ListNode { + next(): ListNode { const current = this.currentNode; // a check to prevent error if randomly calling next() when // iterator is at the end of the list, meaining the currentNode @@ -80,7 +80,7 @@ class ListIterator { * * @returns the first node in the list */ - first(): ListNode { + first(): ListNode { this.reset(); return this.next(); } @@ -90,7 +90,7 @@ class ListIterator { * * @param {object} theList the linked list to iterate over */ - setList(theList: LinkedList): void { + setList(theList: LinkedList): void { this.list = theList; this.reset(); } @@ -102,9 +102,9 @@ class ListIterator { * @param {function} callback the callback function to be called with * each node of the list as an arg */ - each(callback: (node: ListNode) => void): void { + each(callback: (node: ListNode) => void): void { this.reset(); - let listNode: ListNode; + let listNode: ListNode; while (this.hasNext()) { listNode = this.next(); callback(listNode); diff --git a/packages/singly-linked-list/src/lib/list-node.ts b/packages/singly-linked-list/src/lib/list-node.ts index 975fb4a..fb1e2e3 100644 --- a/packages/singly-linked-list/src/lib/list-node.ts +++ b/packages/singly-linked-list/src/lib/list-node.ts @@ -11,18 +11,18 @@ * and a pointer the next node in the list. * */ -class ListNode { - data: any; - next: ListNode | null; +class ListNode { + data: T; + next: ListNode | null; /** * Creates a list node object with a data property and pointer * to the next node * * @constructor - * @param {any} data The data to initialize the node + * @param {T} data The data to initialize the node */ - constructor(data: any) { + constructor(data: T) { this.data = data; this.next = null; } @@ -39,9 +39,9 @@ class ListNode { /** * Gets the list node's data * - * @returns {any} the data of the node + * @returns {T} the data of the node */ - getData(): any { + getData(): T { return this.data; }