API Docs for:
Show:

File: dependencies\LinkedList.js


//=====Node object=====

/**
 @class LinkedListNode
 @constructor
 */
function LinkedListNode(value, next){
    /**
     The variable held by this node.
     @property value
     */
    this.value = value;
    /**
     Points to the next node on the list.
     @property next
     */
    this.next = next;
}

//=====List object=====

/**
 Singly-linked list.
 @class LinkedList
 @constructor
 */
function LinkedList(){
    this._first = null;
    this._length = 0;
}

/**
 @method getFirst
 @return {LinkedListNode|null} First node on the list. If the list is empty, this value will be null.
 */
LinkedList.prototype.getFirst = function(){
    return this._first;
};

/**
 @method getLength
 @return {Number} Length of the list.
 */
LinkedList.prototype.getLength = function(){
    return this._length;
};

/**
 Retrieves a value stored in a node under the given index. If the index is out of bounds, an error will be thrown.
 @method get
 @param index {Number}
 @return Value of the node under the given index.
 */
LinkedList.prototype.get = function(index){
    if(index < 0 || index >= this._length){
        throw new Error('You\'re trying to retrieve a value from a list using an out of bounds index ('+ index +')!');
    }
    
    var node = this._first;
    
    for(var i=0; i<index; ++i){
        node = node.next;            
    }
    
    return node.value;
};

/**
 Returns the index of the first node holding the provided value.
 @method indexOf
 @param value
 @return {Number} Index of the first node holding the given value. If no node holds the value, the function will return -1.
 */
LinkedList.prototype.indexOf = function(value){
    for(var node=this._first, i=0; node; node=node.next, ++i){
        if(node.value === value){
            return i;
        }
    }
    return -1;
};

/**
 Inserts a given value under the given index, shifting following values by one. Throws an error if the index is out of bounds.
 @method insert
 @param index {Number} Index under which the value should be placed.
 @param value The value to be inserted
 */
LinkedList.prototype.insert = function(index, value){
    if(index < 0 || index > this._length){
        throw new Error('You\'re attempting to insert a value ('+ value +') into a list under an index ('+ index +') that\'s out of bounds!');
    }
    
    if(index === 0){
        this._first = new LinkedListNode(value, this._first);
        this._length++;
    }else{
        var node = this._first;
        
        for(var i=1; i<index; ++i){
            node = node.next;
        }
        
        node.next = new LinkedListNode(value, node.next);
        this._length++;
    }
};

/**
 Merges the list with another. Does not copy.
 @method merge
 @param list {LinkedList} List to be merged.
 @param [index] {Number} Index under which the list being merged should be placed. If no index is provided, the list is appened at the end.
 */
LinkedList.prototype.merge = function(list, index){    
    if(!list.getFirst()){
        return;
    }
    
    if(index < 0 || index > this._length){
        throw new Error('You\'re attempting to merge a list using an index ('+ index +') that\'s out of bounds!');
    }
    
    //If index wasn't provided, append the list to the end of this one.
    if(index === undefined){
        index = this._length;
    }
    
    var lastListNode;
    
    if(index === 0){
        lastListNode = list.getFirst();
        
        //Get the last node of the list being merged
        while(lastListNode.next){
            lastListNode = lastListNode.next;
        }
        
        lastListNode.next = this._first;
        this._first = list.getFirst();
        this._length += list.getLength();
        
    }else{
        var node = this._first;
        lastListNode = list.getFirst();
        
        //Get the last node of the list being merged
        while(lastListNode.next){
            lastListNode = lastListNode.next;
        }
        
        //Get the node after which the list is going to be inserted
        for(var i=1; i<index; ++i){
            node = node.next;
        }
        
        lastListNode.next = node.next;
        node.next = list.getFirst();
        this._length += list.getLength();
    }
};

/**
 Removes the node under the index from the list. If the provided index is out of bounds or the list is empty, an error with be thrown.
 @method removeIndex
 @param index {Number} Index of the node to be removed.
 @return The value stored in the removed node.
 */
LinkedList.prototype.removeIndex = function(index){
    if(index < 0 || index >= this._length){
        throw new Error('You\'re attempting to remove an out of bounds index ('+ index +') from a list!');
    }
    
    var removedNode;
    
    if(index === 0){
        removedNode = this._first;        
        this._first = removedNode.next;
        this._length--;
        
        return removedNode.value;
    }else{
        var node = this._first;
        
        for(var i=1; i<index; ++i){
            node = node.next;
        }
        
        removedNode = node.next;
        node.next = removedNode.next;
        this._length--;
        
        return removedNode.value;
    }
};

/**
 Removes the first or all nodes holding the given value from the list.
 @method removeValue
 @param value {Number} Value to be removed.
 @param [removeAll] {Boolean} If this parameter is true, all values matching the first parameter will be removed. If it's false, only the first match will be removed.
 @return {Boolean} True if a node was removed, otherwise false
 */
LinkedList.prototype.removeValue = function(value, removeAll){
    if(!this._first){
        return false;
    }
    
    var removed = false,
        node = this._first;
    
    if(removeAll){
        while(node.next){
            if(node.next.value === value){
                node.next = node.next.next;
                this._length--;
                removed = true;
            }else{
                node = node.next;
            }
        }
        
        if(this._first.value === value){
            this._first = this._first.next;
            this._length--;
            removed = true;
        }
        
        return removed;
    }else{
        if(this._first.value === value){
            this._first = this._first.next;
            this._length--;
            return true;
        }
        
        while(node.next){
            if(node.next.value === value){
                node.next = node.next.next;
                this._length--;
                return true;
            }else{
                node = node.next;
            }
        }
        
        return false;
    }
};

/**
 Removes the first or all nodes for which the provided function returns true.
 @method removeIf
 @param condition {Function} Function used to evaluate whether a node should be removed. This function should accept a node value and return Boolean.
 @param [removeAll] {Boolean} If this parameter is true, all nodes for which the condition function will return true will be removed. If it's false, only the first matching node will be removed.
 @return {Boolean} True if a node was removed, otherwise false
 */
LinkedList.prototype.removeIf = function(condition, removeAll){
    if(!this._first){
        return false;
    }
    
    var removed = false,
        node = this._first;
    
    if(removeAll){
        while(node.next){
            if(condition(node.next.value)){
                node.next = node.next.next;
                this._length--;
                removed = true;
            }else{
                node = node.next;
            }
        }
        
        if(condition(this._first.value)){
            this._first = this._first.next;
            this._length--;
            removed = true;
        }
        
        return removed;
    }else{
        if(condition(this._first.value)){
            this._first = this._first.next;
            this._length--;
            return true;
        }
        
        while(node.next){
            if(condition(node.next.value)){
                node.next = node.next.next;
                this._length--;
                return true;
            }else{
                node = node.next;
            }
        }
        
        return false;
    }
};

/**
 Reverses the list.
 @method reverse
 */
LinkedList.prototype.reverse = function(){    
    for(var node=this._first, newListFirst=null, nextNode; node; node=nextNode){
        nextNode = node.next;
        node.next = newListFirst;
        newListFirst = node;
    }
    
    this._first = newListFirst;
};

/**
 Resets the list, removing all nodes.
 @method clear
 */
LinkedList.prototype.clear = function(){
    this._first = null;
    this._length = 0;
};

/**
 @method toArray
 @return {Array} An array holding all values stored in the list.
 */
LinkedList.prototype.toArray = function(){
    var array = [];
    
    for(var node=this._first; node; node=node.next){
        array.push(node.value);
    }
    
    return array;
};

/**
 Creates a reduced in size copy of the list. If the beginning index is out of bounds, an error will be thrown.
 @method subList
 @param beginIndex {Number} Index from which the copy process should start.
 @param [count] {Number} Number of nodes to be copied into the new list. If the count is out of bounds or not set, then the created list will contain all elements from the beginning index to the end of the list being copied. This value can't be negative.
 @return {LinkedList} The newly created subset.
 */
LinkedList.prototype.subList = function(beginIndex, count){
    if(count < 0){
        throw new Error('The provided count ('+ count +') can\'t be negative!');
    }
    
    if(beginIndex < 0 || beginIndex >= this._length){
        throw new Error('You\'re trying to create a sub list using an out of bound index ('+ beginIndex +')');
    }
    
    var list = new LinkedList(),
        node = this._first,
        i, newListNode;
        
    if(count === 0){
        return list;
    }
    
    for(i=0; i<beginIndex; ++i){
        node = node.next;
    }
    
    list.insert(0, node.value);
    node = node.next;
    
    if(count === undefined){
        for(newListNode=list.getFirst(); node; node=node.next, newListNode=newListNode.next){
            newListNode.next = new LinkedListNode(node.value, null);
            list._length++;
        }
    }else{
        for(newListNode=list.getFirst(), i=1; node && i<count; node=node.next, ++i, newListNode=newListNode.next){
            newListNode.next = new LinkedListNode(node.value, null);
            list._length++;
        }
    }
    
    return list;
};