Skip to content

Instantly share code, notes, and snippets.

@softwarespot
Last active October 31, 2015 08:24
Show Gist options
  • Save softwarespot/d94d7bd161632589cc72 to your computer and use it in GitHub Desktop.
Save softwarespot/d94d7bd161632589cc72 to your computer and use it in GitHub Desktop.
A linked list in JavaScript with ES2015
// Note: Save as linkedlist.js and run 'babel-node linkedlist.js' to display in the command window. This is written in ES2015
((global) => {
// Return strings of toString() found on the Object prototype
// Based on the implementation by lodash inc. is* function as well
const _objectStrings = {
BOOLEAN: '[object Boolean]',
};
// Store a reference of the toString function on the Object prototype chain
const _objectToString = global.Object.prototype.toString;
/**
* LinkedList interface
*/
function LinkedList() {
this.head = null;
}
/**
* LinkedList node interface
* @param {mixed} data Data to store in the node
*/
function LinkedListNode(data) {
this.data = data;
this.next = null;
}
/**
* Append functions to the LinkedList prototype chain
* @type {object}
*/
LinkedList.prototype = {
/**
* Insert either a primitive data e.g. number, string etc... or a LinkedListNode
*
* @param {LinkedListNode|mixed} data A primitive datatype e.g. number, string etc... or a LinkedListNode object reference
* @return {LinkedList} LinkedList object reference
*/
insert: (data) => {
// Create a new node if data is not an object reference of this.node
const node = _isNode(data) ? data : new LinkedListNode(data);
// If the head is already assigned with a node reference, then set the newly created node above
// to point to the head's node reference
if (this.head !== null) {
node.next = this.head;
}
// Set the head to point to the newly create node reference
this.head = node;
return this;
},
// Helper functions
/**
* Get the number of nodes in the linked list
*
* @return {number} Number of node object references in the linked list
*/
count: () => {
let length = 0;
let node = this.head;
while (node !== null) {
node = node.next;
length++;
}
return length;
},
/**
* Get a node object reference (cloned or not) at a particular index value.
* If less than zero or out of bounds, null will be returned instead
*
* @param {number} index Index value
* @param {boolean} clone True clone the node; otherwise, false
* @return {LinkedListNode|null} The linked list node; otherwise, null
*/
get: (index, clone) => {
// Invalid index value
if (index < 0) {
return null;
}
// Set to true by default if not a boolean type
if (!_isBoolean(clone)) {
clone = true;
}
let count = 0;
let node = this.head;
while (node !== null) {
// Return the node at the specified index
if (index === count) {
// Clone the node or return the node object reference
return clone ? new LinkedListNode(node.data) : node;
}
// Get the next node
node = node.next;
count++;
}
// Out of bounds index
return null;
},
/**
* Reverse a linked list
*
* @return {LinkedList} LinkedList object reference
*/
reverse: () => {
let node = this.head;
let next = null;
let previous = null;
while (node !== null) {
next = node.next;
node.next = previous;
previous = node;
node = next;
}
this.head = previous;
return this;
},
/**
* Display the linked list contents by passing the node data to the callback function
*
* @param {function} callback Callback function to pass the data property of the node to
* @return {LinkedList} LinkedList object reference
*/
print: (callback) => {
// Store the head node temporarily
let node = this.head;
while (node !== null) {
// Print to the console
callback(node.data);
// Get the next node
node = node.next;
}
return this;
},
};
// Methods (private)
/**
* Check if a variable is a boolean datatype
*
* @param {mixed} value Value to check
* @returns {boolean} True, the value is a boolean datatype; otherwise, false
*/
function _isBoolean(value) {
return value === true || value === false || (_isObjectLike(value) && _objectToString.call(value) === _objectStrings.BOOLEAN);
}
/**
* Check of a variable is linked list node
*
* @param {LinkedListNode} node Value to check
* @return {boolean} True, the value is a node; otherwise, false
*/
function _isNode(node) {
return node !== null && // If not null
_isObjectLike(node) && // An object datatype
node.hasOwnProperty('data') && // Has the data property
node.hasOwnProperty('next'); // Has the next property
}
/**
* Check if a variable is an object
*
* @param {mixed} value Value to check
* @returns {boolean} True, the value is an object; otherwise, false
*/
function _isObjectLike(value) {
return !!value && typeof value === 'object';
}
// Example
(() => {
// Create a linked list
const list = new LinkedList();
// Add primitive datatypes e.g. string, number to the linked list
list.insert('Some data 1');
list.insert('Some data 2');
list.insert('Some data 3');
// Add a new object reference of a linked list node
list.insert(new LinkedListNode('Some data 4'));
// Display the number of nodes in the linked list
global.console.log('LinkedList count: ', list.count());
// Get the linked list node at the index position 1. Note: The index count starts from zero
global.console.log('LinkedList index at 1: ', global.JSON.stringify(list.get(1, true)));
// Get the linked list node at 100th position (Note: It will return null as there are only 4 nodes in the linked list)
global.console.log('LinkedList index at 100: ', list.get(100));
// Display the contents of the linked list before reversal
global.console.log('LinkedList print: . . .');
list.print(global.console.log);
// Reverse the linked list
list.reverse();
// Display the contents of the linked list after reversal
global.console.log('LinkedList print: . . .');
list.print(global.console.log);
})();
})(window); // An IIFE for function scope i.e. creating a namespace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment