Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

chain-lightning

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

chain-lightning - npm Package Compare versions

Comparing version
0.2.1
to
0.3.0
+1220
lib/BinaryTree.js
/*
Chain Lightning
Copyright (c) 2018 Cédric Ronvel
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
"use strict" ;
/*
For balancing, it uses the AVL algorithm.
See: https://en.wikipedia.org/wiki/AVL_tree
Visualization: https://www.cs.usfca.edu/~galles/visualization/AVLtree.html
*/
function BinaryTree( options , ... elements ) {
var autoIndex = 0 ;
this.trunc = null ;
this.length = 0 ;
this.keyFn = options && options.keyFn ;
this.uniqueKeys = !! ( options && options.uniqueKeys ) ;
if ( ! this.keyFn ) {
this.keyFn = ( element , hint ) => hint !== undefined ? hint : autoIndex ++ ;
}
if ( elements.length ) {
this.insert( ... elements ) ;
}
}
module.exports = BinaryTree ;
BinaryTree.from = ( iterable , options ) => {
var element ,
tree = new BinaryTree( options ) ;
for ( element of iterable ) {
tree.insert( element ) ;
}
return tree ;
} ;
function Node( tree , key , element , parent = null ) {
this.tree = tree ;
this.key = key ;
this.element = element ;
this.parent = parent ;
this.left = null ;
this.right = null ;
this.depth = 1 ;
}
BinaryTree.Node = Node ;
// /!\ DEPRECATED? .truncate*() does not call it /!\
// Used when a node is deleted
BinaryTree.prototype.destroyNode = function( node ) {
node.tree = null ;
node.key = null ;
node.element = null ;
node.parent = null ;
node.left = null ;
node.right = null ;
node.depth = 0 ;
} ;
// Implementation dependant, this works for key as finite number, for things like key as string, those methods must be overloaded
BinaryTree.prototype.isOrdered = ( key1 , key2 ) => key1 <= key2 ;
BinaryTree.prototype.defaultKey = 0 ;
BinaryTree.prototype.increment = key => key + 1 ;
BinaryTree.prototype.decrement = key => key - 1 ;
BinaryTree.prototype.values = function *() {
var current = this.getHeadNode() ;
while ( current ) {
yield current.element ;
current = this.nextNode( current ) ;
}
} ;
BinaryTree.prototype[Symbol.iterator] = BinaryTree.prototype.values ;
BinaryTree.prototype.keys = function() {
var keys = new Array( this.length ) ,
index = 0 ,
current = this.getHeadNode() ;
while ( current ) {
keys[ index ++ ] = current.key ;
current = this.nextNode( current ) ;
}
return keys ;
} ;
BinaryTree.prototype.getHeadNode =
BinaryTree.prototype.getMinNode = function( node = this.trunc ) {
if ( ! node ) { return null ; }
while ( node.left ) { node = node.left ; }
return node ;
} ;
BinaryTree.prototype.getTailNode =
BinaryTree.prototype.getMaxNode = function( node = this.trunc ) {
if ( ! node ) { return null ; }
while ( node.right ) { node = node.right ; }
return node ;
} ;
BinaryTree.prototype.nextNode = function( node ) {
if ( node.right ) {
node = node.right ;
while ( node.left ) {
node = node.left ;
}
return node ;
}
while ( node.parent ) {
if ( node === node.parent.left ) {
return node.parent ;
}
node = node.parent ;
}
return null ;
} ;
BinaryTree.prototype.previousNode = function( node ) {
if ( node.left ) {
node = node.left ;
while ( node.right ) {
node = node.right ;
}
return node ;
}
while ( node.parent ) {
if ( node === node.parent.right ) {
return node.parent ;
}
node = node.parent ;
}
return null ;
} ;
BinaryTree.prototype.get = function( key ) {
var node = this.getNode( key ) ;
if ( ! node ) { return undefined ; }
return node.element ;
} ;
BinaryTree.prototype.getNode = function( key ) {
if ( ! this.trunc ) { return null ; }
var node = this.trunc ;
for ( ;; ) {
if ( node.key === key ) {
return node ;
}
if ( this.isOrdered( node.key , key ) ) {
if ( ! node.right ) { return null ; }
node = node.right ;
}
else {
if ( ! node.left ) { return null ; }
node = node.left ;
}
}
} ;
BinaryTree.prototype.set = function( key , element , noOverwrite ) {
if ( ! this.trunc ) {
this.trunc = new Node( this , key , element ) ;
this.length ++ ;
return ;
}
var node = this.trunc ;
for ( ;; ) {
if ( key === node.key ) {
// Overwrite this key
if ( noOverwrite ) {
throw new Error( "Duplicated key '" + key + "'" ) ;
}
if ( this.uniqueKeys ) {
node.element = element ;
return ;
}
// No uniqueKey? So insert it after
if ( ! node.right ) {
node.right = new Node( this , key , element , node ) ;
this.length ++ ;
this.rebalance( node , true ) ;
return ;
}
node = node.right ;
}
if ( this.isOrdered( node.key , key ) ) {
if ( ! node.right ) {
node.right = new Node( this , key , element , node ) ;
this.length ++ ;
this.rebalance( node , true ) ;
return ;
}
node = node.right ;
}
else {
if ( ! node.left ) {
node.left = new Node( this , key , element , node ) ;
this.length ++ ;
this.rebalance( node , true ) ;
return ;
}
node = node.left ;
}
}
} ;
BinaryTree.prototype.insert = function( ... elements ) {
elements.forEach( element => this.set( this.keyFn( element ) , element , true ) ) ;
} ;
// Divide and conquer algo, to avoid rotation when all elements are in the correct order
BinaryTree.prototype.insertOrdered = function( ... elements ) {
var start , end , middle , diff , listIndex ,
list = [ 0 , elements.length - 1 ] ;
for ( listIndex = 0 ; listIndex < list.length ; listIndex += 2 ) {
start = list[ listIndex ] ;
end = list[ listIndex + 1 ] ;
diff = end - start ;
switch ( diff ) {
case 0 :
//console.log( "===" , start ) ;
this.set( this.keyFn( elements[ start ] , start ) , elements[ start ] , true ) ;
break ;
case 1 :
//console.log( "start end" , start , end ) ;
this.set( this.keyFn( elements[ start ] , start ) , elements[ start ] , true ) ;
list.push( end , end ) ;
//this.set( this.keyFn( elements[ end ] , end ) , elements[ end ] , true ) ;
break ;
case 2 :
//console.log( "middle start end" , start + 1 , start , end ) ;
this.set( this.keyFn( elements[ start + 1 ] , start + 1 ) , elements[ start + 1 ] , true ) ;
list.push( start , start ) ;
//this.set( this.keyFn( elements[ start ] , start ) , elements[ start ] , true ) ;
list.push( end , end ) ;
//this.set( this.keyFn( elements[ end ] , end ) , elements[ end ] , true ) ;
break ;
default :
middle = Math.round( start + diff / 2 ) ;
//console.log( "split" , middle , start , end ) ;
this.set( this.keyFn( elements[ middle ] , middle ) , elements[ middle ] , true ) ;
list.push( start , middle - 1 ) ;
list.push( middle + 1 , end ) ;
break ;
}
}
} ;
BinaryTree.prototype.delete = function( key ) {
var node = this.getNode( key ) ;
if ( ! node ) { return false ; }
this.deleteNode( node ) ;
} ;
BinaryTree.prototype.deleteNode = function( node ) {
var parent , referer , refererProp , replacerNode ;
parent = node.parent ;
if ( parent ) {
referer = parent ;
refererProp = node === parent.left ? 'left' : 'right' ;
}
else {
referer = this ;
refererProp = 'trunc' ;
}
if ( node.left ) {
if ( node.right ) {
// Ok, both node exist... it's the complicated case,
// Find a replacerNode, copy its key and value in the current node,
// and delete the replacer instead of this one.
// Note that the replacer has only one child, since it's either the min or the max of the subtree.
// Choose the heavier child
if ( node.left.depth >= node.right.depth ) {
// We will use the left subtree, the replacer is the maximum of this sub-tree
replacerNode = this.getMaxNode( node.left ) ;
}
else {
// We will use the right subtree, the replacer is the minimum of this sub-tree
replacerNode = this.getMinNode( node.right ) ;
}
// Copy key/value...
node.key = replacerNode.key ;
node.element = replacerNode.element ;
// ... and just delete the replacer using .deleteNode()
this.deleteNode( replacerNode ) ;
}
else {
// Only a left child, attach its subtree to the parent
referer[ refererProp ] = node.left ;
node.left.parent = parent ;
this.length -- ;
this.rebalance( node , true ) ;
this.destroyNode( node ) ;
}
}
else if ( node.right ) {
// Only a right child, attach its subtree to the parent
referer[ refererProp ] = node.right ;
node.right.parent = parent ;
this.length -- ;
this.rebalance( node , true ) ;
this.destroyNode( node ) ;
}
else {
// No child, simply unlink it from the parent
referer[ refererProp ] = null ;
this.length -- ;
this.rebalance( node , true ) ;
this.destroyNode( node ) ;
}
} ;
BinaryTree.prototype.truncateBefore = function( key , including ) {
var lastTruncatedNode = this.getClosestNode( key , ! including , -1 ) ;
//var firstKeptNode = this.nextNode( lastTruncatedNode ) ;
if ( ! lastTruncatedNode ) { return 0 ; }
var count = 0 ,
truncatedSubTree = lastTruncatedNode ,
current = lastTruncatedNode ,
remainderNode = lastTruncatedNode.right ;
// length reduction
while ( current ) {
count ++ ;
current = this.previousNode( current ) ;
}
this.length -= count ;
//console.log( "lastTruncatedNode:" , lastTruncatedNode.key ) ;
//this.debug() ;
//console.log( "truncatedSubTree is now" , truncatedSubTree.key ) ;
while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.right ) {
truncatedSubTree = truncatedSubTree.parent ;
//console.log( "truncatedSubTree is now" , truncatedSubTree.key ) ;
}
for ( ;; ) {
if ( truncatedSubTree.parent ) {
truncatedSubTree.parent.left = remainderNode ;
if ( remainderNode ) {
remainderNode.parent = truncatedSubTree.parent ;
}
// Loop conditions
//console.log( ">>> rebalancing" , truncatedSubTree.parent.key ) ;
remainderNode = this.rebalance( truncatedSubTree.parent ) ;
truncatedSubTree = remainderNode ;
//console.log( "- truncatedSubTree is now" , truncatedSubTree.key ) ;
while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.left ) {
truncatedSubTree = truncatedSubTree.parent ;
//console.log( "- truncatedSubTree is now" , truncatedSubTree.key ) ;
//console.log( ">>> rebalancing" , truncatedSubTree.key ) ;
truncatedSubTree = this.rebalance( truncatedSubTree ) ;
//console.log( " truncatedSubTree is now" , truncatedSubTree.key ) ;
}
if ( ! truncatedSubTree.parent ) { break ; }
//console.log( "+ truncatedSubTree is now" , truncatedSubTree.key ) ;
while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.right ) {
truncatedSubTree = truncatedSubTree.parent ;
//console.log( "+ truncatedSubTree is now" , truncatedSubTree.key ) ;
//console.log( ">>> rebalancing" , truncatedSubTree.key ) ;
truncatedSubTree = this.rebalance( truncatedSubTree ) ;
//console.log( " truncatedSubTree is now" , truncatedSubTree.key ) ;
}
if ( truncatedSubTree === remainderNode ) { break ; }
}
else {
//console.log( "trunc!" ) ;
this.trunc = remainderNode ;
if ( remainderNode ) {
remainderNode.parent = null ;
//console.log( ">>> rebalancing" , remainderNode.key ) ;
this.rebalance( remainderNode ) ;
}
break ;
}
}
return count ;
} ;
BinaryTree.prototype.truncateAfter = function( key , including ) {
var firstTruncatedNode = this.getClosestNode( key , ! including , 1 ) ;
//var firstKeptNode = this.nextNode( firstTruncatedNode ) ;
if ( ! firstTruncatedNode ) { return 0 ; }
var count = 0 ,
truncatedSubTree = firstTruncatedNode ,
current = firstTruncatedNode ,
remainderNode = firstTruncatedNode.left ;
// length reduction
while ( current ) {
count ++ ;
current = this.nextNode( current ) ;
}
this.length -= count ;
//console.log( "firstTruncatedNode:" , firstTruncatedNode.key ) ;
//this.debug() ;
//console.log( "truncatedSubTree is now" , truncatedSubTree.key ) ;
while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.left ) {
truncatedSubTree = truncatedSubTree.parent ;
//console.log( "truncatedSubTree is now" , truncatedSubTree.key ) ;
}
for ( ;; ) {
if ( truncatedSubTree.parent ) {
truncatedSubTree.parent.right = remainderNode ;
if ( remainderNode ) {
remainderNode.parent = truncatedSubTree.parent ;
}
// Loop conditions
//console.log( ">>> rebalancing" , truncatedSubTree.parent.key ) ;
remainderNode = this.rebalance( truncatedSubTree.parent ) ;
truncatedSubTree = remainderNode ;
//console.log( "- truncatedSubTree is now" , truncatedSubTree.key ) ;
while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.right ) {
truncatedSubTree = truncatedSubTree.parent ;
//console.log( "- truncatedSubTree is now" , truncatedSubTree.key ) ;
//console.log( ">>> rebalancing" , truncatedSubTree.key ) ;
truncatedSubTree = this.rebalance( truncatedSubTree ) ;
//console.log( " truncatedSubTree is now" , truncatedSubTree.key ) ;
}
if ( ! truncatedSubTree.parent ) { break ; }
//console.log( "+ truncatedSubTree is now" , truncatedSubTree.key ) ;
while ( truncatedSubTree.parent && truncatedSubTree === truncatedSubTree.parent.left ) {
truncatedSubTree = truncatedSubTree.parent ;
//console.log( "+ truncatedSubTree is now" , truncatedSubTree.key ) ;
//console.log( ">>> rebalancing" , truncatedSubTree.key ) ;
truncatedSubTree = this.rebalance( truncatedSubTree ) ;
//console.log( " truncatedSubTree is now" , truncatedSubTree.key ) ;
}
if ( truncatedSubTree === remainderNode ) { break ; }
}
else {
//console.log( "trunc!" ) ;
this.trunc = remainderNode ;
if ( remainderNode ) {
remainderNode.parent = null ;
//console.log( ">>> rebalancing" , remainderNode.key ) ;
this.rebalance( remainderNode ) ;
}
break ;
}
}
return count ;
} ;
/*
Advanced Array-like features
*/
// Array.prototype.includes() uses this for value equality
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
function isIdenticalTo( x , y ) {
return x === y || ( Number.isNaN( x ) && Number.isNaN( y ) ) ;
}
BinaryTree.prototype.keyOf = function( element , fromKey ) {
var current = fromKey ? this.getNode( fromKey ) : this.getHeadNode() ;
while ( current ) {
if ( isIdenticalTo( current.element , element ) ) { return current.key ; }
current = this.nextNode( current ) ;
}
return ;
} ;
BinaryTree.prototype.lastKeyOf = function( element , fromKey ) {
var current = fromKey ? this.getNode( fromKey ) : this.getTailNode() ;
while ( current ) {
if ( isIdenticalTo( current.element , element ) ) { return current.key ; }
current = this.previousNode( current ) ;
}
return ;
} ;
BinaryTree.prototype.includes = function( element ) {
if ( ! this.trunc ) { return false ; }
return this._recursiveIncludes( element , this.trunc ) ;
} ;
BinaryTree.prototype._recursiveIncludes = function( element , current ) {
if ( isIdenticalTo( current.element , element ) ) { return true ; }
if ( current.left && this._recursiveIncludes( element , current.left ) ) { return true ; }
if ( current.right && this._recursiveIncludes( element , current.right ) ) { return true ; }
return false ;
} ;
BinaryTree.prototype.forEach = function( fn , thisArg ) {
var current = this.getHeadNode() ;
while ( current ) {
fn.call( thisArg , current.element , current.key , this ) ;
current = this.nextNode( current ) ;
}
} ;
BinaryTree.prototype.some = function( fn , thisArg ) {
var current = this.getHeadNode() ;
while ( current ) {
if ( fn.call( thisArg , current.element , current.key , this ) ) { return true ; }
current = this.nextNode( current ) ;
}
return false ;
} ;
BinaryTree.prototype.every = function( fn , thisArg ) {
var current = this.getHeadNode() ;
while ( current ) {
if ( ! fn.call( thisArg , current.element , current.key , this ) ) { return false ; }
current = this.nextNode( current ) ;
}
return true ;
} ;
BinaryTree.prototype.find = function( fn , thisArg ) {
var current = this.getHeadNode() ;
while ( current ) {
if ( fn.call( thisArg , current.element , current.key , this ) ) {
return current.element ;
}
current = this.nextNode( current ) ;
}
return ;
} ;
BinaryTree.prototype.findKey = function( fn , thisArg ) {
var current = this.getHeadNode() ;
while ( current ) {
if ( fn.call( thisArg , current.element , current.key , this ) ) {
return current.key ;
}
current = this.nextNode( current ) ;
}
return ;
} ;
// Map to an array
BinaryTree.prototype.arrayMap =
BinaryTree.prototype.map = function( fn , thisArg ) {
var array = new Array( this.length ) ,
index = 0 ,
current = this.getHeadNode() ;
while ( current ) {
array[ index ++ ] = fn.call( thisArg , current.element , current.key , this ) ;
current = this.nextNode( current ) ;
}
return array ;
} ;
BinaryTree.prototype.reduce = function( fn , accumulator ) {
var current = this.getHeadNode() ;
while ( current ) {
accumulator = fn( accumulator , current.element , current.key , this ) ;
current = this.nextNode( current ) ;
}
return accumulator ;
} ;
BinaryTree.prototype.reduceRight = function( fn , accumulator ) {
var current = this.getTailNode() ;
while ( current ) {
accumulator = fn( accumulator , current.element , current.key , this ) ;
current = this.previousNode( current ) ;
}
return accumulator ;
} ;
// Filter to an array
BinaryTree.prototype.arrayFilter =
BinaryTree.prototype.filter = function( fn , thisArg ) {
var array = [] ,
index = 0 ,
current = this.getHeadNode() ;
while ( current ) {
if ( fn.call( thisArg , current.element , current.key , this ) ) {
array[ index ++ ] = current.element ;
}
current = this.nextNode( current ) ;
}
return array ;
} ;
/*
Advanced custom features
*/
// Delete all occurences of a value
BinaryTree.prototype.deleteValue =
BinaryTree.prototype.removeValue = function( value ) {
this.inPlaceFilter( e => ! isIdenticalTo( e , value ) ) ;
} ;
BinaryTree.prototype.inPlaceFilter = function( fn , thisArg ) {
var current = this.getHeadNode() ,
next ;
while ( current ) {
if ( fn.call( thisArg , current.element , current.key , this ) ) {
current = this.nextNode( current ) ;
}
else {
next = this.nextNode( current ) ;
this.deleteNode( current ) ;
current = next ;
}
}
return this ;
} ;
/*
Internal, low-level
*/
// Propagate depth upward and balance the tree if necessary
BinaryTree.prototype.rebalance = function( node , upwardRecusive ) {
var balance = this.updateDepth( node ) ;
// After things like .truncate*(), it is possible that one node needs multiple rotations
while ( balance > 1 || balance < -1 ) {
if ( balance > 1 ) {
if ( this.nodeBalance( node.right ) < 0 ) {
//console.log( "right-left heavy" ) ;
//console.log( "right-left rotations needed, keys: " + node.right.key + ' ; ' + node.key ) ;
//console.log( "\nbefore:" ) ;
//this.debug() ;
// First rotate the child
this.rotateNodeRight( node.right ) ;
//console.log( "\nafter the child rotation:" ) ;
//this.debug() ;
node = this.rotateNodeLeft( node ) ;
//console.log( "\nafter both rotations:" ) ;
//this.debug() ;
//console.log( "Node sanity check, key: " + node.key ) ;
this.nodeSanityCheck( node , node.parent ) ;
}
else {
//console.log( "right heavy" ) ;
//console.log( "simple left rotation needed, key: " + node.key ) ;
//console.log( "\nbefore:" ) ;
//this.debug() ;
node = this.rotateNodeLeft( node ) ;
//console.log( "\nafter:" ) ;
//this.debug() ;
//console.log( "Node sanity check, key: " + node.key ) ;
this.nodeSanityCheck( node , node.parent ) ;
}
}
else if ( balance < -1 ) {
if ( this.nodeBalance( node.left ) > 0 ) {
//console.log( "left-right heavy" ) ;
//console.log( "left-right rotations needed, keys: " + node.left.key + ' ; ' + node.key ) ;
//console.log( "\nbefore:" ) ;
//this.debug() ;
// First rotate the child
this.rotateNodeLeft( node.left ) ;
//console.log( "\nafter the child rotation:" ) ;
//this.debug() ;
node = this.rotateNodeRight( node ) ;
//console.log( "\nafter both rotations:" ) ;
//this.debug() ;
//console.log( "Node sanity check, key: " + node.key ) ;
this.nodeSanityCheck( node , node.parent ) ;
}
else {
//console.log( "left heavy" ) ;
//console.log( "simple right rotation needed, key: " + node.key ) ;
//console.log( "\nbefore:" ) ;
//this.debug() ;
node = this.rotateNodeRight( node ) ;
//console.log( "\nafter:" ) ;
//this.debug() ;
//console.log( "Node sanity check, key: " + node.key ) ;
this.nodeSanityCheck( node , node.parent ) ;
}
}
balance = this.nodeBalance( node ) ;
}
if ( upwardRecusive && node.parent ) {
this.rebalance( node.parent , true ) ;
}
return node ;
} ;
// Update the depth of the node, return its balance
BinaryTree.prototype.updateDepth = function( node ) {
if ( node.left ) {
if ( node.right ) {
// There are 2 children
node.depth = 1 + Math.max( node.left.depth , node.right.depth ) ;
return node.right.depth - node.left.depth ;
}
// Only a left child
node.depth = 1 + node.left.depth ;
return -node.left.depth ;
}
if ( node.right ) {
// Only a right child
node.depth = 1 + node.right.depth ;
return node.right.depth ;
}
// No child
node.depth = 1 ;
return 0 ;
} ;
// Just return the node balance without recomputing depth
BinaryTree.prototype.nodeBalance = function( node ) {
if ( node.left ) {
if ( node.right ) {
// There are 2 children
return node.right.depth - node.left.depth ;
}
// Only a left child
return -node.left.depth ;
}
if ( node.right ) {
// Only a right child
return node.right.depth ;
}
// No child
return 0 ;
} ;
// Assume node.right existance
BinaryTree.prototype.rotateNodeLeft = function( node ) {
var parent = node.parent ;
var replacerNode = node.right ;
// Attach the left of the right child to the current node's right
node.right = replacerNode.left ;
if ( node.right ) { node.right.parent = node ; }
// Attach the current node as the replacer's left
replacerNode.left = node ;
node.parent = replacerNode ;
// Attach the replacer to the parent node
replacerNode.parent = parent ;
if ( parent ) {
if ( parent.left === node ) {
parent.left = replacerNode ;
}
else {
parent.right = replacerNode ;
}
}
else {
this.trunc = replacerNode ;
}
// Now update the depth in the correct order
this.updateDepth( node ) ;
this.updateDepth( replacerNode ) ;
return replacerNode ;
} ;
// Same algo than rotateNodeRight, just swap all left/right
BinaryTree.prototype.rotateNodeRight = function( node ) {
var parent = node.parent ;
var replacerNode = node.left ;
// Attach the right of the left child to the current node's left
node.left = replacerNode.right ;
if ( node.left ) { node.left.parent = node ; }
// Attach the current node as the replacer's right
replacerNode.right = node ;
node.parent = replacerNode ;
// Attach the replacer to the parent node
replacerNode.parent = parent ;
if ( parent ) {
if ( parent.right === node ) {
parent.right = replacerNode ;
}
else {
parent.left = replacerNode ;
}
}
else {
this.trunc = replacerNode ;
}
// Now update the depth in the correct order
this.updateDepth( node ) ;
this.updateDepth( replacerNode ) ;
return replacerNode ;
} ;
/*
excluding: exclude the current key
direction: how to choose a node for a non uniqueKeys tree or when excluding the key:
* unset: don't care
* 1: forward direction, choose the left-most node
* -1: backward direction, choose the right-most node
*/
BinaryTree.prototype.getClosestNode = function( key , excluding , direction ) {
if ( ! this.trunc ) { return null ; }
var lastNode = null ,
lastDirection = 0 ,
lastLeftAncestor = null ,
lastRightAncestor = null ,
node = this.trunc ;
for ( ;; ) {
if ( node.key === key ) {
if ( excluding ) {
if ( this.uniqueKeys ) {
if ( direction > 0 ) { return this.nextNode( node ) ; }
return this.previousNode( node ) ;
}
if ( direction > 0 ) {
do {
node = this.nextNode( node ) ;
} while ( node && node.key === key ) ;
return node ;
}
do {
node = this.previousNode( node ) ;
} while ( node && node.key === key ) ;
return node ;
}
else if ( this.uniqueKeys ) {
return node ;
}
else if ( direction > 0 ) {
if ( ! node.left || node.left.key !== key ) { return node ; }
lastRightAncestor = lastNode = node ;
lastDirection = -1 ;
node = node.left ;
}
else { // if ( direction < 0 ) {
if ( ! node.right || node.right.key !== key ) { return node ; }
lastLeftAncestor = lastNode = node ;
lastDirection = 1 ;
node = node.right ;
}
}
else if ( this.isOrdered( node.key , key ) ) {
if ( ! node.right ) {
if ( direction < 0 ) { return node ; }
if ( lastDirection < 0 ) { return lastNode ; }
return lastRightAncestor ;
}
lastLeftAncestor = lastNode = node ;
lastDirection = 1 ;
node = node.right ;
}
else {
if ( ! node.left ) {
if ( direction > 0 ) { return node ; }
if ( lastDirection > 0 ) { return lastNode ; }
return lastLeftAncestor ;
}
lastRightAncestor = lastNode = node ;
lastDirection = -1 ;
node = node.left ;
}
}
} ;
// DEPRECATED?
// Delete subtree without re-balancing
BinaryTree.prototype.deleteSubTree = function( node , isRecursiveCall ) {
if ( node.left ) { this.deleteSubTree( node.left , true ) ; }
if ( node.right ) { this.deleteSubTree( node.right , true ) ; }
if ( ! isRecursiveCall ) {
if ( node.parent ) {
if ( node.parent.left === node ) {
node.parent.left = null ;
}
else {
node.parent.right = null ;
}
}
else {
this.trunc = null ;
}
}
this.length -- ;
this.destroyNode( node ) ;
} ;
/*
Debug stuffs
*/
BinaryTree.prototype.debug = function( node = this.trunc , spaces = 0 , prefix = '━' ) {
if ( ! node ) {
console.log( '(empty)' ) ;
return ;
}
var str = ' '.repeat( spaces ) + prefix + node.key ;
//var nextSpaces = str.length ? str.length - 1 : 0 ;
var nextSpaces = spaces + 2 ;
//str += ' (' + node.depth + ' | ' + this.nodeBalance( node ) + ')' ;
str += ' (' + this.nodeBalance( node ) + ')' ;
if ( node.right ) {
this.debug( node.right , nextSpaces , '┏' ) ;
}
console.log( str ) ;
if ( node.left ) {
this.debug( node.left , nextSpaces , '┗' ) ;
}
} ;
const assert = require( 'assert' ).strict ;
BinaryTree.prototype.sanityCheck = function( onlyStructure ) {
var runtime = {
length: 0 ,
onlyStructure: !! onlyStructure
} ;
if ( this.trunc ) {
this.nodeSanityCheck( this.trunc , null , runtime ) ;
}
if ( ! runtime.onlyStructure ) {
assert.strictEqual( this.length , runtime.length , "Length mismatch" ) ;
}
} ;
BinaryTree.prototype.nodeSanityCheck = function( node , parentNode = null , runtime = null ) {
if ( runtime ) {
if ( ++ runtime.length > this.length && ! runtime.onlyStructure ) {
assert.fail( "Length mismatch: exceeding, key: " + node.key ) ;
}
}
assert.strictEqual( node.tree , this , "Current node doesn't belong to the current tree, key: " + node.key ) ;
assert.strictEqual( node.parent , parentNode , "Parent mismatch, key: " + node.key ) ;
if ( node.left ) {
if ( node.right ) {
// There are 2 children
if ( runtime && ! runtime.onlyStructure ) {
assert.strictEqual( node.depth , 1 + Math.max( node.left.depth , node.right.depth ) , "Depth mismatch (RL), key: " + node.key ) ;
}
this.nodeSanityCheck( node.left , node , runtime ) ;
this.nodeSanityCheck( node.right , node , runtime ) ;
}
else {
// Only a left child
if ( runtime && ! runtime.onlyStructure ) {
assert.strictEqual( node.depth , 1 + node.left.depth , "Depth mismatch (L), key: " + node.key ) ;
}
this.nodeSanityCheck( node.left , node , runtime ) ;
}
}
else if ( node.right ) {
// Only a right child
if ( runtime && ! runtime.onlyStructure ) {
assert.strictEqual( node.depth , 1 + node.right.depth , "Depth mismatch (R), key: " + node.key ) ;
}
this.nodeSanityCheck( node.right , node , runtime ) ;
}
else if ( runtime && ! runtime.onlyStructure ) {
// No child
assert.strictEqual( node.depth , 1 , "Depth mismatch (), key: " + node.key ) ;
}
if ( runtime && ! runtime.onlyStructure ) {
// Should be done after the recursive call, to avoid throwing the wrong error
var balance = this.nodeBalance( node ) ;
if ( Math.abs( balance ) > 1 ) {
assert.fail( "Unbalanced node (" + balance + "), key: " + node.key ) ;
}
}
} ;
/*
Chain Lightning
Copyright (c) 2018 Cédric Ronvel
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
"use strict" ;
// This is a “Doubly” Linked List
function LinkedList( ... elements ) {
this.head = null ;
this.tail = null ;
this.length = 0 ;
if ( elements.length ) {
this.push( ... elements ) ;
}
}
module.exports = LinkedList ;
function Node( list , previous , next , element ) {
this.list = list ;
this.previous = previous ;
this.next = next ;
this.element = element ;
}
LinkedList.Node = Node ;
// Array.prototype.includes() uses this for value equality
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
function isIdenticalTo( x , y ) {
return x === y || ( Number.isNaN( x ) && Number.isNaN( y ) ) ;
}
LinkedList.prototype.values = function *() {
var current = this.head ;
while ( current ) {
yield current.element ;
current = current.next ;
}
} ;
LinkedList.prototype[Symbol.iterator] = LinkedList.prototype.values ;
LinkedList.from = function( iterable ) {
var node , element ,
list = new LinkedList() ;
for ( element of iterable ) {
node = new Node( list , list.tail , null , element ) ;
if ( ! list.head ) {
// This is the first element
list.head = list.tail = node ;
}
else {
list.tail.next = node ;
list.tail = node ;
}
list.length ++ ;
}
return list ;
} ;
// Similar to .keys()
LinkedList.prototype.nodes = function() {
var nodes = new Array( this.length ) ,
index = 0 ,
current = this.head ;
while ( current ) {
nodes[ index ++ ] = current ;
current = current.next ;
}
return nodes ;
} ;
LinkedList.prototype.push =
LinkedList.prototype.append = function( ... elements ) {
var index , node ;
if ( ! elements.length ) { return ; }
this.length += elements.length ;
node = new Node( this , this.tail , null , elements[ 0 ] ) ;
if ( this.tail ) {
this.tail.next = node ;
this.tail = node ;
}
else {
this.head = this.tail = node ;
}
for ( index = 1 ; index < elements.length ; index ++ ) {
node = new Node( this , this.tail , null , elements[ index ] ) ;
this.tail.next = node ;
this.tail = node ;
}
} ;
LinkedList.prototype.unshift =
LinkedList.prototype.prepend = function( ... elements ) {
var index , node ;
if ( ! elements.length ) { return ; }
this.length += elements.length ;
index = elements.length - 1 ;
node = new Node( this , null , this.head , elements[ index ] ) ;
if ( this.head ) {
this.head.previous = node ;
this.head = node ;
}
else {
this.head = this.tail = node ;
}
while ( index -- ) {
node = new Node( this , null , this.head , elements[ index ] ) ;
this.head.previous = node ;
this.head = node ;
}
} ;
LinkedList.prototype.pop = function() {
if ( ! this.tail ) { return ; }
var node = this.tail ;
this.tail = node.previous ;
if ( this.tail ) {
this.tail.next = null ;
}
else {
// That was the last element
this.head = null ;
}
this.length -- ;
return node.element ;
} ;
LinkedList.prototype.shift = function() {
if ( ! this.head ) { return ; }
var node = this.head ;
this.head = node.next ;
if ( this.head ) {
this.head.previous = null ;
}
else {
// That was the last element
this.tail = null ;
}
this.length -- ;
return node.element ;
} ;
/*
Advanced Array-like features
*/
LinkedList.prototype.nodeOf = function( element , fromNode ) {
var current ;
if ( fromNode ) {
if ( fromNode.list !== this ) { return ; }
current = fromNode ;
}
else {
current = this.head ;
}
while ( current ) {
if ( isIdenticalTo( current.element , element ) ) { return current ; }
current = current.next ;
}
return null ;
} ;
LinkedList.prototype.lastNodeOf = function( element , fromNode ) {
var current ;
if ( fromNode ) {
if ( fromNode.list !== this ) { return ; }
current = fromNode ;
}
else {
current = this.tail ;
}
while ( current ) {
if ( isIdenticalTo( current.element , element ) ) { return current ; }
current = current.previous ;
}
return null ;
} ;
// Almost like .indexOf()
LinkedList.prototype.includes = function( element , fromNode ) {
var current ;
if ( fromNode ) {
if ( fromNode.list !== this ) { return false ; }
current = fromNode ;
}
else {
current = this.head ;
}
while ( current ) {
if ( isIdenticalTo( current.element , element ) ) { return true ; }
current = current.next ;
}
return false ;
} ;
LinkedList.prototype.forEach = function( fn , thisArg ) {
var current = this.head ;
while ( current ) {
fn.call( thisArg , current.element , current , this ) ;
current = current.next ;
}
} ;
LinkedList.prototype.some = function( fn , thisArg ) {
var current = this.head ;
while ( current ) {
if ( fn.call( thisArg , current.element , current , this ) ) { return true ; }
current = current.next ;
}
return false ;
} ;
LinkedList.prototype.every = function( fn , thisArg ) {
var current = this.head ;
while ( current ) {
if ( ! fn.call( thisArg , current.element , current , this ) ) { return false ; }
current = current.next ;
}
return true ;
} ;
LinkedList.prototype.find = function( fn , thisArg ) {
var current = this.head ;
while ( current ) {
if ( fn.call( thisArg , current.element , current , this ) ) {
return current.element ;
}
current = current.next ;
}
return ;
} ;
LinkedList.prototype.findNode = function( fn , thisArg ) {
var current = this.head ;
while ( current ) {
if ( fn.call( thisArg , current.element , current , this ) ) {
return current ;
}
current = current.next ;
}
return null ;
} ;
// Create a new LinkedList
LinkedList.prototype.map = function( fn , thisArg ) {
var newList = new LinkedList() ,
newElement ,
node ,
current = this.head ;
while ( current ) {
newElement = fn.call( thisArg , current.element , current , this ) ;
node = new Node( newList , newList.tail , null , newElement ) ;
if ( newList.tail ) {
newList.tail.next = node ;
newList.tail = node ;
}
else {
newList.head = newList.tail = node ;
}
current = current.next ;
}
newList.length = this.length ;
return newList ;
} ;
LinkedList.prototype.reduce = function( fn , accumulator ) {
var current = this.head ;
while ( current ) {
accumulator = fn( accumulator , current.element , current , this ) ;
current = current.next ;
}
return accumulator ;
} ;
LinkedList.prototype.reduceRight = function( fn , accumulator ) {
var current = this.tail ;
while ( current ) {
accumulator = fn( accumulator , current.element , current , this ) ;
current = current.previous ;
}
return accumulator ;
} ;
LinkedList.prototype.filter = function( fn , thisArg ) {
var newList = new LinkedList() ,
node ,
current = this.head ;
while ( current ) {
if ( fn.call( thisArg , current.element , current , this ) ) {
node = new Node( newList , newList.tail , null , current.element ) ;
if ( newList.tail ) {
newList.tail.next = node ;
newList.tail = node ;
}
else {
newList.head = newList.tail = node ;
}
newList.length ++ ;
}
current = current.next ;
}
return newList ;
} ;
LinkedList.prototype.reverse = function() {
var tmp ,
current = this.head ;
while ( current ) {
tmp = current.next ;
current.next = current.previous ;
current.previous = tmp ;
// Note: it's already inverted, so use previous instead of next
current = current.previous ;
}
tmp = this.head ;
this.head = this.tail ;
this.tail = tmp ;
return this ;
} ;
/*
Advanced custom features
*/
LinkedList.prototype.get = function( node ) {
if ( ! node || node.list !== this ) { return ; }
return node.element ;
} ;
LinkedList.prototype.set = function( node , element ) {
if ( ! node || node.list !== this ) { return ; }
node.element = element ;
} ;
LinkedList.prototype.deleteNode =
LinkedList.prototype.removeNode = function( node ) {
if ( ! node || node.list !== this ) { return false ; }
if ( node.previous ) { node.previous.next = node.next ; }
if ( node.next ) { node.next.previous = node.previous ; }
if ( this.head === node ) { this.head = node.next ; }
if ( this.tail === node ) { this.tail = node.previous ; }
node.list = node.previous = node.next = null ;
this.length -- ;
return true ;
} ;
// Delete all occurences of a value
LinkedList.prototype.delete =
LinkedList.prototype.remove = function( value ) {
this.inPlaceFilter( e => ! isIdenticalTo( e , value ) ) ;
} ;
LinkedList.prototype.moveAfter = function( node , afterNode ) {
if (
! node || node.list !== this || ! afterNode || afterNode.list !== this ||
node === afterNode || afterNode === node.previous
) {
return false ;
}
var beforeNode = afterNode.next ;
if ( this.head === node ) { this.head = node.next ; }
if ( this.tail === node ) { this.tail = node.previous ; }
else if ( this.tail === afterNode ) { this.tail = node ; }
if ( node.previous ) { node.previous.next = node.next ; }
if ( node.next ) { node.next.previous = node.previous ; }
node.previous = afterNode ;
afterNode.next = node ;
node.next = beforeNode ;
if ( beforeNode ) { beforeNode.previous = node ; }
return true ;
} ;
LinkedList.prototype.moveBefore = function( node , beforeNode ) {
if (
! node || node.list !== this || ! beforeNode || beforeNode.list !== this ||
node === beforeNode || beforeNode === node.next
) {
return false ;
}
var afterNode = beforeNode.previous ;
if ( this.head === node ) { this.head = node.next ; }
else if ( this.head === beforeNode ) { this.head = node ; }
if ( this.tail === node ) { this.tail = node.previous ; }
if ( node.previous ) { node.previous.next = node.next ; }
if ( node.next ) { node.next.previous = node.previous ; }
node.next = beforeNode ;
beforeNode.previous = node ;
node.previous = afterNode ;
if ( afterNode ) { afterNode.next = node ; }
return true ;
} ;
LinkedList.prototype.moveToTail = function( node ) { return this.moveAfter( node , this.tail ) ; } ;
LinkedList.prototype.moveToHead = function( node ) { return this.moveBefore( node , this.head ) ; } ;
LinkedList.prototype.insertAfter = function( afterNode , ... elements ) {
if ( afterNode.list !== this || ! elements.length ) { return ; }
var node , index ,
lastNode = afterNode ,
beforeNode = afterNode.next ;
for ( index = 0 ; index < elements.length ; index ++ ) {
node = new Node( this , lastNode , null , elements[ index ] ) ;
lastNode.next = node ;
lastNode = node ;
}
this.length += elements.length ;
if ( beforeNode ) {
lastNode.next = beforeNode ;
beforeNode.previous = lastNode ;
}
else {
this.tail = lastNode ;
}
} ;
LinkedList.prototype.insertBefore = function( beforeNode , ... elements ) {
if ( beforeNode.list !== this || ! elements.length ) { return ; }
var node ,
index = elements.length ,
lastNode = beforeNode ,
afterNode = beforeNode.previous ;
while ( index -- ) {
node = new Node( this , null , lastNode , elements[ index ] ) ;
lastNode.previous = node ;
lastNode = node ;
}
this.length += elements.length ;
if ( afterNode ) {
lastNode.previous = afterNode ;
afterNode.next = lastNode ;
}
else {
this.head = lastNode ;
}
} ;
LinkedList.prototype.inPlaceFilter = function( fn , thisArg ) {
var lastNode = null ,
nextNode ,
node = this.head ;
this.head = null ;
while ( node ) {
node.previous = lastNode ;
nextNode = node.next ; // backup that
if ( fn.call( thisArg , node.element , node , this ) ) {
if ( ! this.head ) { this.head = node ; }
lastNode = node ;
}
else {
if ( lastNode ) { lastNode.next = node.next ; }
this.length -- ;
node.list = node.previous = node.next = null ;
}
node = nextNode ;
}
this.tail = lastNode ;
return this ;
} ;
/*
Debug stuffs
*/
const assert = require( 'assert' ).strict ;
LinkedList.prototype.sanityCheck = function() {
var length = 0 ,
lastNode = null ,
node = this.head ;
while ( node && ++ length <= this.length ) {
assert.strictEqual( node.list , this , "Current node doesn't belong to the current list" ) ;
assert.strictEqual( node.previous , lastNode , "Current node's previous is the real previous node" ) ;
// Useless because we precisely come from that lastNode
//if ( lastNode ) { assert( lastNode.next , node ) ; }
lastNode = node ;
node = node.next ;
}
assert.strictEqual( this.length , length , "Length mismatch" ) ;
assert.strictEqual( this.tail , lastNode , "The last node is not the tail" ) ;
} ;
/*
Chain Lightning
Copyright (c) 2018 Cédric Ronvel
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
"use strict" ;
/* global describe, it, before, after */
const lib = require( '..' ) ;
const BinaryTree = lib.BinaryTree ;
/* Tests */
describe( "Binary Tree" , () => {
describe( "Basic features" , () => {
it( "constructor arguments should be added as elements" , () => {
var tree ;
tree = new BinaryTree() ;
expect( [ ... tree ] ).to.equal( [] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , 'jack' ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' ] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , 'jack' , 'jean' , 'steve' ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' ] ) ;
tree.sanityCheck() ;
} ) ;
it( "BinaryTree.from() should create a tree from any iterable" , () => {
var tree ;
tree = BinaryTree.from( new Set() ) ;
expect( [ ... tree ] ).to.equal( [] ) ;
tree.sanityCheck() ;
tree = BinaryTree.from( new Set( [ 'jack' ] ) ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' ] ) ;
tree.sanityCheck() ;
tree = BinaryTree.from( new Set( [ 'jack' , 'jean' , 'steve' ] ) ) ;
tree.sanityCheck() ;
expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
} ) ;
it( ".values(), .keys() and iterator" , () => {
var tree ;
tree = new BinaryTree() ;
tree.set( 3 , 'jack' ) ;
tree.set( 2 , 'jean' ) ;
tree.set( 5 , 'steve' ) ;
tree.set( 2.5 , 'john' ) ;
tree.set( 2.7 , 'robert' ) ;
tree.set( 2.8 , 'johnson' ) ;
tree.set( 2.75 , 'boris' ) ;
tree.set( 6 , 'bobby' ) ;
tree.set( 2.85 , 'carl' ) ;
tree.set( 2.72 , 'tom' ) ;
tree.set( 2.76 , 'roger' ) ;
tree.set( 2.77 , 'vlad' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ;
expect( tree.keys() ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
expect( [ ... tree.values() ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ;
} ) ;
it( "set and get elements" , () => {
var tree ;
tree = new BinaryTree() ;
// Try getting unexisting keys
expect( tree.get( 0 ) ).to.be( undefined ) ;
expect( tree.get( 9 ) ).to.be( undefined ) ;
tree.set( 3 , 'jack' ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' ] ) ;
tree.sanityCheck() ;
tree.set( 2 , 'jean' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'jack' ] ) ;
tree.sanityCheck() ;
tree.set( 5 , 'steve' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'jack' , 'steve' ] ) ;
tree.sanityCheck() ;
tree.set( 2.5 , 'john' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'jack' , 'steve' ] ) ;
tree.sanityCheck() ;
tree.set( 2.7 , 'robert' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'jack' , 'steve' ] ) ;
tree.sanityCheck() ;
// Force a left-right heavy
tree.set( 2.8 , 'johnson' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'johnson' , 'jack' , 'steve' ] ) ;
tree.sanityCheck() ;
tree.set( 2.75 , 'boris' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'boris' , 'johnson' , 'jack' , 'steve' ] ) ;
tree.sanityCheck() ;
tree.set( 6 , 'bobby' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'boris' , 'johnson' , 'jack' , 'steve' , 'bobby' ] ) ;
tree.sanityCheck() ;
tree.set( 2.85 , 'carl' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'boris' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ;
tree.sanityCheck() ;
// Force a right-left heavy
tree.set( 2.72 , 'tom' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ;
tree.sanityCheck() ;
//tree.debug() ;
expect( tree.get( 3 ) ).to.be( 'jack' ) ;
expect( tree.get( 2 ) ).to.be( 'jean' ) ;
expect( tree.get( 5 ) ).to.be( 'steve' ) ;
expect( tree.get( 2.5 ) ).to.be( 'john' ) ;
expect( tree.get( 2.7 ) ).to.be( 'robert' ) ;
expect( tree.get( 2.8 ) ).to.be( 'johnson' ) ;
expect( tree.get( 2.75 ) ).to.be( 'boris' ) ;
expect( tree.get( 6 ) ).to.be( 'bobby' ) ;
expect( tree.get( 2.85 ) ).to.be( 'carl' ) ;
expect( tree.get( 2.72 ) ).to.be( 'tom' ) ;
// Try getting unexisting keys
expect( tree.get( 0 ) ).to.be( undefined ) ;
expect( tree.get( 9 ) ).to.be( undefined ) ;
} ) ;
it( "duplicated keys and the 'uniqueKeys' option" , () => {
var tree ;
tree = new BinaryTree() ;
// Without uniqueKeys
tree.set( 2 , 'jean' ) ;
tree.set( 3 , 'jack' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'jack' ] ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 3 ] ) ;
tree.sanityCheck() ;
tree.set( 2 , 'bob' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'bob' , 'jack' ] ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2 , 3 ] ) ;
tree.sanityCheck() ;
// With uniqueKeys
tree = new BinaryTree( { uniqueKeys: true } ) ;
tree.set( 2 , 'jean' ) ;
tree.set( 3 , 'jack' ) ;
tree.set( 2 , 'bob' ) ;
expect( [ ... tree ] ).to.equal( [ 'bob' , 'jack' ] ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 3 ] ) ;
tree.sanityCheck() ;
} ) ;
it( ".insert()" , () => {
var tree ;
tree = new BinaryTree() ;
expect( tree ).to.have.length( 0 ) ;
tree.insert( 'bob' ) ;
tree.insert( 'bill' ) ;
tree.insert( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... tree ] ).to.equal( [ 'bob' , 'bill' , 'jack' , 'jean' , 'steve' ] ) ;
tree.sanityCheck() ;
tree = new BinaryTree() ;
tree.insert( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
tree.sanityCheck() ;
} ) ;
it( "delete by key" , () => {
var tree ;
tree = new BinaryTree() ;
tree.set( 3 , 'jack' ) ;
tree.delete( 3 ) ;
expect( [ ... tree ] ).to.equal( [] ) ;
tree.sanityCheck() ;
tree = new BinaryTree() ;
tree.set( 3 , 'jack' ) ;
tree.set( 2 , 'jean' ) ;
tree.delete( 3 ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' ] ) ;
tree.sanityCheck() ;
tree.delete( 2 ) ;
expect( [ ... tree ] ).to.equal( [] ) ;
tree.sanityCheck() ;
tree = new BinaryTree() ;
tree.set( 3 , 'jack' ) ;
tree.set( 2 , 'jean' ) ;
tree.set( 5 , 'steve' ) ;
tree.set( 2.5 , 'john' ) ;
tree.set( 2.7 , 'robert' ) ;
tree.set( 2.8 , 'johnson' ) ;
tree.set( 2.75 , 'boris' ) ;
tree.set( 6 , 'bobby' ) ;
tree.set( 2.85 , 'carl' ) ;
tree.set( 2.72 , 'tom' ) ;
tree.set( 2.76 , 'roger' ) ;
tree.set( 2.77 , 'vlad' ) ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'carl' , 'jack' , 'steve' , 'bobby' ] ) ;
tree.sanityCheck() ;
//console.log( '\n\nTree:' ) ; tree.debug() ;
// Delete a leaf and force a left-right heavy
tree.delete( 2.85 ) ;
tree.sanityCheck() ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'john' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'jack' , 'steve' , 'bobby' ] ) ;
expect( tree.get( 2.85 ) ).to.be( undefined ) ;
// Delete a node with only one child
tree.delete( 2.5 ) ;
tree.sanityCheck() ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'johnson' , 'jack' , 'steve' , 'bobby' ] ) ;
expect( tree.get( 2.5 ) ).to.be( undefined ) ;
// Delete a node with only two children that are not leaves
tree.delete( 2.8 ) ;
tree.sanityCheck() ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'robert' , 'tom' , 'boris' , 'roger' , 'vlad' , 'jack' , 'steve' , 'bobby' ] ) ;
expect( tree.get( 2.8 ) ).to.be( undefined ) ;
// Delete the trunc node, the tree should replace the trunc node, it also cause a right heavy
tree.delete( 2.75 ) ;
tree.sanityCheck() ;
expect( [ ... tree ] ).to.equal( [ 'jean' , 'robert' , 'tom' , 'roger' , 'vlad' , 'jack' , 'steve' , 'bobby' ] ) ;
expect( tree.get( 2.75 ) ).to.be( undefined ) ;
//tree.debug() ;
} ) ;
} ) ;
describe( "Advanced Array-like features" , () => {
it( ".keyOf()/.lastKeyOf()" , () => {
var tree ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
tree = new BinaryTree( { uniqueKeys: true } , e1 , e2 , e3 ) ;
expect( tree.keyOf( e2 ) ).to.be( 1 ) ;
expect( tree.keyOf( e4 ) ).to.be( undefined ) ;
tree.insert( e2 , e2 , e2 ) ;
tree.set( tree.keyOf( e2 ) , e4 ) ;
expect( [ ... tree ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bob' } ] ) ;
tree.set( tree.lastKeyOf( e2 ) , e4 ) ;
expect( [ ... tree ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bobby' } ] ) ;
} ) ;
it( ".includes()" , () => {
var tree ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
tree = new BinaryTree() ;
expect( tree.includes( e2 ) ).to.be.false() ;
tree = new BinaryTree( null , e1 ) ;
expect( tree.includes( e2 ) ).to.be.false() ;
tree = new BinaryTree( null , e1 , e3 ) ;
expect( tree.includes( e2 ) ).to.be.false() ;
tree = new BinaryTree( null , e2 ) ;
expect( tree.includes( e2 ) ).to.be.true() ;
tree = new BinaryTree( null , e2 , e2 ) ;
expect( tree.includes( e2 ) ).to.be.true() ;
tree = new BinaryTree( null , e1 , e2 , e3 ) ;
expect( tree.includes( e2 ) ).to.be.true() ;
tree = new BinaryTree( null , e1 , e3 , e2 ) ;
expect( tree.includes( e2 ) ).to.be.true() ;
tree = new BinaryTree( null , e2 , e1 , e3 ) ;
expect( tree.includes( e2 ) ).to.be.true() ;
} ) ;
it( ".forEach()" , () => {
var tree ,
accumulator = [] ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
tree = new BinaryTree() ;
tree.forEach( element => accumulator.push( element.v ) ) ;
expect( accumulator ).to.equal( [] ) ;
tree = new BinaryTree( null , e1 , e2 , e3 ) ;
tree.forEach( element => accumulator.push( element.v ) ) ;
expect( accumulator ).to.equal( [ 'jack' , 'bob' , 'steve' ] ) ;
} ) ;
it( ".some()/.every()" , () => {
var tree ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
tree = new BinaryTree() ;
expect( tree.some( element => element.v === 'bob' ) ).to.be.false() ;
expect( tree.every( element => element.v === 'bob' ) ).to.be.true() ;
tree = new BinaryTree( null , e1 ) ;
expect( tree.some( element => element.v === 'bob' ) ).to.be.false() ;
expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ;
tree = new BinaryTree( null , e2 ) ;
expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( tree.every( element => element.v === 'bob' ) ).to.be.true() ;
tree = new BinaryTree( null , e1 , e2 ) ;
expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ;
tree = new BinaryTree( null , e2 , e1 ) ;
expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ;
tree = new BinaryTree( null , e1 , e2 , e3 ) ;
expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ;
tree = new BinaryTree( null , e1 , e2 , e2 , e3 ) ;
expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( tree.every( element => element.v === 'bob' ) ).to.be.false() ;
tree = new BinaryTree( null , e2 , e2 , e2 ) ;
expect( tree.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( tree.every( element => element.v === 'bob' ) ).to.be.true() ;
} ) ;
it( ".find()" , () => {
var tree ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bob' } ;
tree = new BinaryTree( null , e1 , e2 , e3 ) ;
expect( tree.find( element => element.v === 'bob' ) ).to.be( e2 ) ;
expect( tree.find( element => element.v === 'bobby' ) ).to.be( undefined ) ;
tree.insert( e4 ) ;
expect( tree.find( element => element.v === 'bob' ) ).to.be( e2 ) ;
tree.delete( 1 ) ;
expect( tree.find( element => element.v === 'bob' ) ).to.be( e4 ) ;
} ) ;
it( ".findKey()" , () => {
var tree ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bob' } ;
tree = new BinaryTree( null , e1 , e2 , e3 ) ;
expect( tree.findKey( element => element.v === 'bob' ) ).to.be( 1 ) ;
expect( tree.findKey( element => element.v === 'bobby' ) ).to.be( undefined ) ;
expect( tree.findKey( element => element.v === 'bobby' ) ).to.be( undefined ) ;
tree.insert( e4 ) ;
expect( tree.findKey( element => element.v === 'bob' ) ).to.be( 1 ) ;
tree.delete( 1 ) ;
expect( tree.findKey( element => element.v === 'bob' ) ).to.be( 3 ) ;
} ) ;
it( ".arrayMap()" , () => {
var array ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
array = new BinaryTree( null ).arrayMap( element => element.v + element.v ) ;
expect( array ).to.equal( [] ) ;
array = new BinaryTree( null , e1 ).arrayMap( element => element.v + element.v ) ;
expect( array ).to.equal( [ 'jackjack' ] ) ;
array = new BinaryTree( null , e1 , e2 , e3 ).arrayMap( element => element.v + element.v ) ;
expect( array ).to.equal( [ 'jackjack' , 'bobbob' , 'stevesteve' ] ) ;
} ) ;
it( ".reduce()/.reduceRight" , () => {
var tree ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
tree = new BinaryTree() ;
expect( tree.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ;
expect( tree.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ;
tree = new BinaryTree( null , e1 ) ;
expect( tree.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ;
expect( tree.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ;
tree = new BinaryTree( null , e1 , e2 , e3 ) ;
expect( tree.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jackbobsteve' ) ;
expect( tree.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'stevebobjack' ) ;
} ) ;
it( ".arrayFilter()" , () => {
var array ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
array = new BinaryTree().filter( element => element.v.length >= 4 ) ;
expect( array ).to.equal( [] ) ;
array = new BinaryTree( null , e1 ).filter( element => element.v.length >= 4 ) ;
expect( array ).to.equal( [ e1 ] ) ;
array = new BinaryTree( null , e1 ).filter( element => element.v.length < 4 ) ;
expect( array ).to.equal( [] ) ;
array = new BinaryTree( null , e1 , e2 , e3 ).filter( element => element.v.length >= 4 ) ;
expect( array ).to.equal( [ e1 , e3 ] ) ;
array = new BinaryTree( null , e1 , e3 ).filter( element => element.v.length >= 4 ) ;
expect( array ).to.equal( [ e1 , e3 ] ) ;
array = new BinaryTree( null , e2 , e2 , e2 ).filter( element => element.v.length >= 4 ) ;
expect( array ).to.equal( [] ) ;
array = new BinaryTree( null , e1 , e2 , e3 ).filter( element => element.v.length < 4 ) ;
expect( array ).to.equal( [ e2 ] ) ;
} ) ;
} ) ;
describe( "Advanced custom features" , () => {
it( ".deleteValue()" , () => {
var tree ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
tree = new BinaryTree( null , e1 , e2 , e3 ) ;
tree.deleteValue( e2 ) ;
expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ;
tree.sanityCheck() ;
tree = new BinaryTree() ;
tree.deleteValue( e2 ) ;
expect( [ ... tree ] ).to.equal( [] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , e2 ) ;
tree.deleteValue( e2 ) ;
expect( [ ... tree ] ).to.equal( [] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , e2 , e1 , e3 ) ;
tree.deleteValue( e2 ) ;
expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , e1 , e3 , e2 ) ;
tree.deleteValue( e2 ) ;
expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ;
tree.sanityCheck() ;
// Remove all occurences
tree = new BinaryTree( null , e2 , e2 , e2 , e1 , e2 , e3 , e2 ) ;
tree.deleteValue( e2 ) ;
expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ;
tree.sanityCheck() ;
// NaN test
tree = new BinaryTree( null , NaN , NaN , NaN , e1 , NaN , e3 , NaN ) ;
tree.deleteValue( NaN ) ;
expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ;
tree.sanityCheck() ;
} ) ;
it( ".inPlaceFilter()" , () => {
var tree ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
tree = new BinaryTree( null , e2 , e2 , e2 ) ;
expect( tree.inPlaceFilter( () => true ) ).to.be( tree ) ;
tree = new BinaryTree().inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... tree ] ).to.equal( [] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , e1 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... tree ] ).to.equal( [ e1 ] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( true , e1 ).inPlaceFilter( element => element.v.length < 4 ) ;
expect( [ ... tree ] ).to.equal( [] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , e1 , e2 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , e1 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... tree ] ).to.equal( [ e1 , e3 ] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , e2 , e2 , e2 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... tree ] ).to.equal( [] ) ;
tree.sanityCheck() ;
tree = new BinaryTree( null , e1 , e2 , e3 ).inPlaceFilter( element => element.v.length < 4 ) ;
expect( [ ... tree ] ).to.equal( [ e2 ] ) ;
tree.sanityCheck() ;
} ) ;
it( ".truncateBefore()" , () => {
var tree ;
var reset = () => {
tree = new BinaryTree() ;
tree.set( 3 , 'jack' ) ;
tree.set( 2 , 'jean' ) ;
tree.set( 5 , 'steve' ) ;
tree.set( 2.5 , 'john' ) ;
tree.set( 2.7 , 'robert' ) ;
tree.set( 2.8 , 'johnson' ) ;
tree.set( 2.75 , 'boris' ) ;
tree.set( 6 , 'bobby' ) ;
tree.set( 2.85 , 'carl' ) ;
tree.set( 2.72 , 'tom' ) ;
tree.set( 2.76 , 'roger' ) ;
tree.set( 2.77 , 'vlad' ) ;
} ;
reset() ;
tree.truncateBefore( 1 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck() ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 1 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck() ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck() ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck() ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.72 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.72 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.73 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.73 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.8 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.8 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.85 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.85 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.9 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 2.9 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 4 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 4 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 5 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 5 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 6 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 6 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 7 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateBefore( 7 ) ;
expect( [ ... tree.keys() ] ).to.equal( [] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
} ) ;
it( ".truncateAfter()" , () => {
var tree ;
var reset = () => {
tree = new BinaryTree() ;
tree.set( 3 , 'jack' ) ;
tree.set( 2 , 'jean' ) ;
tree.set( 5 , 'steve' ) ;
tree.set( 2.5 , 'john' ) ;
tree.set( 2.7 , 'robert' ) ;
tree.set( 2.8 , 'johnson' ) ;
tree.set( 2.75 , 'boris' ) ;
tree.set( 6 , 'bobby' ) ;
tree.set( 2.85 , 'carl' ) ;
tree.set( 2.72 , 'tom' ) ;
tree.set( 2.76 , 'roger' ) ;
tree.set( 2.77 , 'vlad' ) ;
} ;
reset() ;
tree.truncateAfter( 1 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [] ) ;
tree.sanityCheck() ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 1 ) ;
expect( [ ... tree.keys() ] ).to.equal( [] ) ;
tree.sanityCheck() ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [] ) ;
tree.sanityCheck() ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 ] ) ;
tree.sanityCheck() ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.72 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.72 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.73 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.73 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.8 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.8 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.85 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.85 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.9 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 2.9 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 4 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 4 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 5 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 5 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 6 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 6 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 7 , true ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
reset() ;
tree.truncateAfter( 7 ) ;
expect( [ ... tree.keys() ] ).to.equal( [ 2 , 2.5 , 2.7 , 2.72 , 2.75 , 2.76 , 2.77 , 2.8 , 2.85 , 3 , 5 , 6 ] ) ;
tree.sanityCheck( true ) ;
//console.log( "Tree after truncate:" ) ;
//tree.debug() ;
} ) ;
} ) ;
describe( "Internal methods" , () => {
it( ".insertOrdered()" , () => {
var tree ;
tree = new BinaryTree() ;
tree.insertOrdered( 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' ] ) ;
tree.sanityCheck() ;
//tree.debug() ;
tree = new BinaryTree() ;
tree.insertOrdered( 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' ] ) ;
tree.sanityCheck() ;
//tree.debug() ;
tree = new BinaryTree() ;
tree.insertOrdered( 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' , 'stephen' ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' , 'stephen' ] ) ;
tree.sanityCheck() ;
//tree.debug() ;
tree = new BinaryTree() ;
tree.insertOrdered( 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' , 'stephen' , 'jörgl' , 'edmund' , 'karl' , 'roberto' , 'juan' , 'augustus' , 'edward' , 'leon' , 'sergio' ) ;
expect( [ ... tree ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bob' , 'joe' , 'russel' , 'marco' , 'jacky' , 'stephen' , 'jörgl' , 'edmund' , 'karl' , 'roberto' , 'juan' , 'augustus' , 'edward' , 'leon' , 'sergio' ] ) ;
tree.sanityCheck() ;
//tree.debug() ;
} ) ;
it( ".getClosestNode()" , () => {
var tree = new BinaryTree() ;
tree.set( 3 , 'jack' ) ;
tree.set( 2 , 'jean' ) ;
tree.set( 5 , 'steve' ) ;
tree.set( 2.5 , 'john' ) ;
tree.set( 2.7 , 'robert' ) ;
tree.set( 2.8 , 'johnson' ) ;
tree.set( 2.75 , 'boris' ) ;
tree.set( 6 , 'bobby' ) ;
tree.set( 2.85 , 'carl' ) ;
tree.set( 2.72 , 'tom' ) ;
tree.set( 2.76 , 'roger' ) ;
tree.set( 2.77 , 'vlad' ) ;
//tree.debug() ;
// Including existing
expect( tree.getClosestNode( 2 , false , -1 ).key ).to.be( 2 ) ;
expect( tree.getClosestNode( 2 , false , 1 ).key ).to.be( 2 ) ;
expect( tree.getClosestNode( 2.72 , false , -1 ).key ).to.be( 2.72 ) ;
expect( tree.getClosestNode( 2.72 , false , 1 ).key ).to.be( 2.72 ) ;
expect( tree.getClosestNode( 2.8 , false , -1 ).key ).to.be( 2.8 ) ;
expect( tree.getClosestNode( 2.8 , false , 1 ).key ).to.be( 2.8 ) ;
expect( tree.getClosestNode( 2.85 , false , -1 ).key ).to.be( 2.85 ) ;
expect( tree.getClosestNode( 2.85 , false , 1 ).key ).to.be( 2.85 ) ;
expect( tree.getClosestNode( 6 , false , -1 ).key ).to.be( 6 ) ;
expect( tree.getClosestNode( 6 , false , 1 ).key ).to.be( 6 ) ;
// Excluding existing
expect( tree.getClosestNode( 2 , true , -1 ) ).to.be( null ) ;
expect( tree.getClosestNode( 2 , true , 1 ).key ).to.be( 2.5 ) ;
expect( tree.getClosestNode( 2.5 , true , -1 ).key ).to.be( 2 ) ;
expect( tree.getClosestNode( 2.5 , true , 1 ).key ).to.be( 2.7 ) ;
expect( tree.getClosestNode( 2.7 , true , -1 ).key ).to.be( 2.5 ) ;
expect( tree.getClosestNode( 2.7 , true , 1 ).key ).to.be( 2.72 ) ;
expect( tree.getClosestNode( 2.72 , true , -1 ).key ).to.be( 2.7 ) ;
expect( tree.getClosestNode( 2.72 , true , 1 ).key ).to.be( 2.75 ) ;
expect( tree.getClosestNode( 2.75 , true , -1 ).key ).to.be( 2.72 ) ;
expect( tree.getClosestNode( 2.75 , true , 1 ).key ).to.be( 2.76 ) ;
expect( tree.getClosestNode( 2.76 , true , -1 ).key ).to.be( 2.75 ) ;
expect( tree.getClosestNode( 2.76 , true , 1 ).key ).to.be( 2.77 ) ;
expect( tree.getClosestNode( 2.77 , true , -1 ).key ).to.be( 2.76 ) ;
expect( tree.getClosestNode( 2.77 , true , 1 ).key ).to.be( 2.8 ) ;
expect( tree.getClosestNode( 2.8 , true , -1 ).key ).to.be( 2.77 ) ;
expect( tree.getClosestNode( 2.8 , true , 1 ).key ).to.be( 2.85 ) ;
expect( tree.getClosestNode( 2.85 , true , -1 ).key ).to.be( 2.8 ) ;
expect( tree.getClosestNode( 2.85 , true , 1 ).key ).to.be( 3 ) ;
expect( tree.getClosestNode( 3 , true , -1 ).key ).to.be( 2.85 ) ;
expect( tree.getClosestNode( 3 , true , 1 ).key ).to.be( 5 ) ;
expect( tree.getClosestNode( 6 , true , -1 ).key ).to.be( 5 ) ;
expect( tree.getClosestNode( 6 , true , 1 ) ).to.be( null ) ;
// Including unexisting
expect( tree.getClosestNode( 1 , false , -1 ) ).to.be( null ) ;
expect( tree.getClosestNode( 1 , false , 1 ).key ).to.be( 2 ) ;
expect( tree.getClosestNode( 2.1 , false , -1 ).key ).to.be( 2 ) ;
expect( tree.getClosestNode( 2.1 , false , 1 ).key ).to.be( 2.5 ) ;
expect( tree.getClosestNode( 2.6 , false , -1 ).key ).to.be( 2.5 ) ;
expect( tree.getClosestNode( 2.6 , false , 1 ).key ).to.be( 2.7 ) ;
expect( tree.getClosestNode( 2.71 , false , -1 ).key ).to.be( 2.7 ) ;
expect( tree.getClosestNode( 2.71 , false , 1 ).key ).to.be( 2.72 ) ;
expect( tree.getClosestNode( 2.73 , false , -1 ).key ).to.be( 2.72 ) ;
expect( tree.getClosestNode( 2.73 , false , 1 ).key ).to.be( 2.75 ) ;
expect( tree.getClosestNode( 2.755 , false , -1 ).key ).to.be( 2.75 ) ;
expect( tree.getClosestNode( 2.755 , false , 1 ).key ).to.be( 2.76 ) ;
expect( tree.getClosestNode( 2.765 , false , -1 ).key ).to.be( 2.76 ) ;
expect( tree.getClosestNode( 2.765 , false , 1 ).key ).to.be( 2.77 ) ;
expect( tree.getClosestNode( 2.78 , false , -1 ).key ).to.be( 2.77 ) ;
expect( tree.getClosestNode( 2.78 , false , 1 ).key ).to.be( 2.8 ) ;
expect( tree.getClosestNode( 2.84 , false , -1 ).key ).to.be( 2.8 ) ;
expect( tree.getClosestNode( 2.84 , false , 1 ).key ).to.be( 2.85 ) ;
expect( tree.getClosestNode( 2.9 , false , -1 ).key ).to.be( 2.85 ) ;
expect( tree.getClosestNode( 2.9 , false , 1 ).key ).to.be( 3 ) ;
expect( tree.getClosestNode( 4 , false , -1 ).key ).to.be( 3 ) ;
expect( tree.getClosestNode( 4 , false , 1 ).key ).to.be( 5 ) ;
expect( tree.getClosestNode( 7 , false , -1 ).key ).to.be( 6 ) ;
expect( tree.getClosestNode( 7 , false , 1 ) ).to.be( null ) ;
// Excluding unexisting -- should be identical to 'including unexisting'
expect( tree.getClosestNode( 1 , true , -1 ) ).to.be( null ) ;
expect( tree.getClosestNode( 1 , true , 1 ).key ).to.be( 2 ) ;
expect( tree.getClosestNode( 2.1 , true , -1 ).key ).to.be( 2 ) ;
expect( tree.getClosestNode( 2.1 , true , 1 ).key ).to.be( 2.5 ) ;
expect( tree.getClosestNode( 2.6 , true , -1 ).key ).to.be( 2.5 ) ;
expect( tree.getClosestNode( 2.6 , true , 1 ).key ).to.be( 2.7 ) ;
expect( tree.getClosestNode( 2.71 , true , -1 ).key ).to.be( 2.7 ) ;
expect( tree.getClosestNode( 2.71 , true , 1 ).key ).to.be( 2.72 ) ;
expect( tree.getClosestNode( 2.73 , true , -1 ).key ).to.be( 2.72 ) ;
expect( tree.getClosestNode( 2.73 , true , 1 ).key ).to.be( 2.75 ) ;
expect( tree.getClosestNode( 2.755 , true , -1 ).key ).to.be( 2.75 ) ;
expect( tree.getClosestNode( 2.755 , true , 1 ).key ).to.be( 2.76 ) ;
expect( tree.getClosestNode( 2.765 , true , -1 ).key ).to.be( 2.76 ) ;
expect( tree.getClosestNode( 2.765 , true , 1 ).key ).to.be( 2.77 ) ;
expect( tree.getClosestNode( 2.78 , true , -1 ).key ).to.be( 2.77 ) ;
expect( tree.getClosestNode( 2.78 , true , 1 ).key ).to.be( 2.8 ) ;
expect( tree.getClosestNode( 2.84 , true , -1 ).key ).to.be( 2.8 ) ;
expect( tree.getClosestNode( 2.84 , true , 1 ).key ).to.be( 2.85 ) ;
expect( tree.getClosestNode( 2.9 , true , -1 ).key ).to.be( 2.85 ) ;
expect( tree.getClosestNode( 2.9 , true , 1 ).key ).to.be( 3 ) ;
expect( tree.getClosestNode( 4 , true , -1 ).key ).to.be( 3 ) ;
expect( tree.getClosestNode( 4 , true , 1 ).key ).to.be( 5 ) ;
expect( tree.getClosestNode( 7 , true , -1 ).key ).to.be( 6 ) ;
expect( tree.getClosestNode( 7 , true , 1 ) ).to.be( null ) ;
} ) ;
} ) ;
} ) ;
/*
Chain Lightning
Copyright (c) 2018 Cédric Ronvel
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
"use strict" ;
/* global describe, it, before, after */
const lib = require( '..' ) ;
const LinkedList = lib.LinkedList ;
/* Tests */
describe( "Linked List" , () => {
describe( "Basic features" , () => {
it( "constructor arguments should be added as elements" , () => {
var list ;
list = new LinkedList() ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( 'jack' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' ] ) ;
list.sanityCheck() ;
list = new LinkedList( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
list.sanityCheck() ;
} ) ;
it( "LinkedList.from() should create a list from any iterable" , () => {
var list ;
list = LinkedList.from( new Set() ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = LinkedList.from( new Set( [ 'jack' ] ) ) ;
expect( [ ... list ] ).to.equal( [ 'jack' ] ) ;
list.sanityCheck() ;
list = LinkedList.from( new Set( [ 'jack' , 'jean' , 'steve' ] ) ) ;
list.sanityCheck() ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
} ) ;
it( ".push()/.append()" , () => {
var list ;
list = new LinkedList() ;
expect( list ).to.have.length( 0 ) ;
list.push( 'bob' ) ;
list.append( 'bill' ) ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'bob' , 'bill' , 'jack' , 'jean' , 'steve' ] ) ;
list.sanityCheck() ;
list = new LinkedList() ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
list.sanityCheck() ;
} ) ;
it( ".unshift()/.prepend()" , () => {
var list ;
list = new LinkedList() ;
expect( list ).to.have.length( 0 ) ;
list.unshift( 'bob' ) ;
list.prepend( 'bill' ) ;
list.unshift( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bill' , 'bob' ] ) ;
list.sanityCheck() ;
list = new LinkedList() ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
list.sanityCheck() ;
} ) ;
it( ".pop()" , () => {
var list ;
list = new LinkedList() ;
expect( list.pop() ).to.be( undefined ) ;
expect( list ).to.have.length( 0 ) ;
list.sanityCheck() ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
list.sanityCheck() ;
expect( list.pop() ).to.be( 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' ] ) ;
list.sanityCheck() ;
expect( list.pop() ).to.be( 'jean' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' ] ) ;
list.sanityCheck() ;
expect( list.pop() ).to.be( 'jack' ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
expect( list.pop() ).to.be( undefined ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
} ) ;
it( ".shift()" , () => {
var list ;
list = new LinkedList() ;
expect( list.shift() ).to.be( undefined ) ;
expect( list ).to.have.length( 0 ) ;
list.sanityCheck() ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
list.sanityCheck() ;
expect( list.shift() ).to.be( 'jack' ) ;
expect( [ ... list ] ).to.equal( [ 'jean' , 'steve' ] ) ;
list.sanityCheck() ;
expect( list.shift() ).to.be( 'jean' ) ;
expect( [ ... list ] ).to.equal( [ 'steve' ] ) ;
list.sanityCheck() ;
expect( list.shift() ).to.be( 'steve' ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
expect( list.shift() ).to.be( undefined ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
} ) ;
} ) ;
describe( "Advanced Array-like features" , () => {
it( ".nodes()" , () => {
var nodes ;
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
nodes = list.nodes() ;
expect( nodes ).to.be.an( Array ) ;
expect( nodes ).to.have.length( 3 ) ;
expect( nodes.map( e => e.element ) ).to.equal( [ e1 , e2 , e3 ] ) ;
list = new LinkedList() ;
nodes = list.nodes() ;
expect( nodes ).to.be.an( Array ) ;
expect( nodes ).to.have.length( 0 ) ;
expect( nodes.map( e => e.element ) ).to.equal( [] ) ;
list = new LinkedList( e1 , e2 , e2 , e2 , e3 ) ;
nodes = list.nodes() ;
expect( nodes ).to.be.an( Array ) ;
expect( nodes ).to.have.length( 5 ) ;
expect( nodes.map( e => e.element ) ).to.equal( [ e1 , e2 , e2 , e2 , e3 ] ) ;
} ) ;
it( ".nodeOf()/.lastNodeOf()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.nodeOf( e2 ).element ).to.be( e2 ) ;
expect( list.nodeOf( e4 ) ).to.be( null ) ;
list.push( e2 , e2 , e2 ) ;
list.set( list.nodeOf( e2 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bob' } ] ) ;
list.set( list.lastNodeOf( e2 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bobby' } ] ) ;
} ) ;
it( ".includes()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new LinkedList() ;
expect( list.includes( e2 ) ).to.be.false() ;
list = new LinkedList( e1 ) ;
expect( list.includes( e2 ) ).to.be.false() ;
list = new LinkedList( e1 , e3 ) ;
expect( list.includes( e2 ) ).to.be.false() ;
list = new LinkedList( e2 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
list = new LinkedList( e2 , e2 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
list = new LinkedList( e1 , e3 , e2 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
list = new LinkedList( e2 , e1 , e3 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
} ) ;
it( ".forEach()" , () => {
var list ,
accumulator = [] ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new LinkedList() ;
list.forEach( element => accumulator.push( element.v ) ) ;
expect( accumulator ).to.equal( [] ) ;
list = new LinkedList( e1 , e2 , e3 ) ;
list.forEach( element => accumulator.push( element.v ) ) ;
expect( accumulator ).to.equal( [ 'jack' , 'bob' , 'steve' ] ) ;
} ) ;
it( ".some()/.every()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new LinkedList() ;
expect( list.some( element => element.v === 'bob' ) ).to.be.false() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.true() ;
list = new LinkedList( e1 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.false() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new LinkedList( e2 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.true() ;
list = new LinkedList( e1 , e2 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new LinkedList( e2 , e1 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new LinkedList( e1 , e2 , e2 , e3 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new LinkedList( e2 , e2 , e2 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.true() ;
} ) ;
it( ".find()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bob' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.find( element => element.v === 'bob' ) ).to.be( e2 ) ;
expect( list.find( element => element.v === 'bobby' ) ).to.be( undefined ) ;
list.push( e4 ) ;
expect( list.find( element => element.v === 'bob' ) ).to.be( e2 ) ;
list.unshift( e4 ) ;
expect( list.find( element => element.v === 'bob' ) ).to.be( e4 ) ;
} ) ;
it( ".findNode()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bob' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.get( list.findNode( element => element.v === 'bob' ) ) ).to.be( e2 ) ;
expect( list.findNode( element => element.v === 'bobby' ) ).to.be( null ) ;
expect( list.get( list.findNode( element => element.v === 'bobby' ) ) ).to.be( undefined ) ;
list.push( e4 ) ;
expect( list.get( list.findNode( element => element.v === 'bob' ) ) ).to.be( e2 ) ;
list.unshift( e4 ) ;
expect( list.get( list.findNode( element => element.v === 'bob' ) ) ).to.be( e4 ) ;
} ) ;
it( ".map()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new LinkedList().map( element => element.v + element.v ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 ).map( element => element.v + element.v ) ;
expect( [ ... list ] ).to.equal( [ 'jackjack' ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ).map( element => element.v + element.v ) ;
expect( [ ... list ] ).to.equal( [ 'jackjack' , 'bobbob' , 'stevesteve' ] ) ;
list.sanityCheck() ;
} ) ;
it( ".reduce()/.reduceRight" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new LinkedList() ;
expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ;
expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ;
list = new LinkedList( e1 ) ;
expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ;
expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jackbobsteve' ) ;
expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'stevebobjack' ) ;
} ) ;
it( ".filter()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new LinkedList( e2 , e2 , e2 ) ;
expect( list.filter( () => true ) ).not.to.be( list ) ;
list = new LinkedList().filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 ).filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 ).filter( element => element.v.length < 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ).filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e3 ).filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e2 , e2 , e2 ).filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ).filter( element => element.v.length < 4 ) ;
expect( [ ... list ] ).to.equal( [ e2 ] ) ;
list.sanityCheck() ;
} ) ;
it( ".reverse()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new LinkedList().reverse() ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 ).reverse() ;
expect( [ ... list ] ).to.equal( [ e1 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ).reverse() ;
expect( [ ... list ] ).to.equal( [ e3 , e2 , e1 ] ) ;
list.sanityCheck() ;
} ) ;
it( "missing .concat()" ) ;
it( "missing .copyWithin()" ) ;
it( "missing .slice()" ) ;
it( "missing .splice()" ) ;
it( "missing .sort()" ) ;
} ) ;
describe( "Advanced custom features" , () => {
it( ".removeNode()/.deleteNode()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
list.removeNode( list.nodeOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList() ;
list.removeNode( list.nodeOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e2 ) ;
list.removeNode( list.nodeOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e2 , e1 , e3 ) ;
list.removeNode( list.nodeOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e3 , e2 ) ;
list.removeNode( list.nodeOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
} ) ;
it( ".remove()/.delete()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList() ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e2 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e2 , e1 , e3 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e3 , e2 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
// Remove all occurences
list = new LinkedList( e2 , e2 , e2 , e1 , e2 , e3 , e2 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
// NaN test
list = new LinkedList( NaN , NaN , NaN , e1 , NaN , e3 , NaN ) ;
list.remove( NaN ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
} ) ;
it( ".moveAfter()/.moveToTail()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.nodeOf( e1 ) , list.nodeOf( e1 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.nodeOf( e1 ) , list.nodeOf( e2 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.nodeOf( e1 ) , list.nodeOf( e3 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e3 , e1 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveToTail( list.nodeOf( e1 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e3 , e1 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.nodeOf( e2 ) , list.nodeOf( e1 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.nodeOf( e2 ) , list.nodeOf( e2 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.nodeOf( e2 ) , list.nodeOf( e3 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveToTail( list.nodeOf( e2 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.nodeOf( e3 ) , list.nodeOf( e1 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.nodeOf( e3 ) , list.nodeOf( e2 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.nodeOf( e3 ) , list.nodeOf( e3 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveToTail( list.nodeOf( e3 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
} ) ;
it( ".moveBefore()/.moveToHead()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.nodeOf( e1 ) , list.nodeOf( e1 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveToHead( list.nodeOf( e1 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.nodeOf( e1 ) , list.nodeOf( e2 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.nodeOf( e1 ) , list.nodeOf( e3 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.nodeOf( e2 ) , list.nodeOf( e1 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveToHead( list.nodeOf( e2 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.nodeOf( e2 ) , list.nodeOf( e2 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.nodeOf( e2 ) , list.nodeOf( e3 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.nodeOf( e3 ) , list.nodeOf( e1 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e3 , e1 , e2 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveToHead( list.nodeOf( e3 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e3 , e1 , e2 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.nodeOf( e3 ) , list.nodeOf( e2 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.nodeOf( e3 ) , list.nodeOf( e3 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
} ) ;
it( ".insertAfter()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
list.insertAfter( list.nodeOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list.insertAfter( list.nodeOf( e2 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e4 , e3 ] ) ;
list.sanityCheck() ;
list.insertAfter( list.nodeOf( e1 ) , e4 , e4 , e4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e4 , e4 , e4 , e2 , e4 , e3 ] ) ;
list.sanityCheck() ;
list.insertAfter( list.nodeOf( e3 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e4 , e4 , e4 , e2 , e4 , e3 , e4 ] ) ;
list.sanityCheck() ;
} ) ;
it( ".insertBefore()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new LinkedList( e1 , e2 , e3 ) ;
list.insertBefore( list.nodeOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
list.sanityCheck() ;
list.insertBefore( list.nodeOf( e2 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e4 , e2 , e3 ] ) ;
list.sanityCheck() ;
list.insertBefore( list.nodeOf( e1 ) , e4 , e4 , e4 ) ;
expect( [ ... list ] ).to.equal( [ e4 , e4 , e4 , e1 , e4 , e2 , e3 ] ) ;
list.sanityCheck() ;
list.insertBefore( list.nodeOf( e3 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ e4 , e4 , e4 , e1 , e4 , e2 , e4 , e3 ] ) ;
list.sanityCheck() ;
} ) ;
it( ".inPlaceFilter()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new LinkedList( e2 , e2 , e2 ) ;
expect( list.inPlaceFilter( () => true ) ).to.be( list ) ;
list = new LinkedList().inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 ).inPlaceFilter( element => element.v.length < 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
list.sanityCheck() ;
list = new LinkedList( e2 , e2 , e2 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
list.sanityCheck() ;
list = new LinkedList( e1 , e2 , e3 ).inPlaceFilter( element => element.v.length < 4 ) ;
expect( [ ... list ] ).to.equal( [ e2 ] ) ;
list.sanityCheck() ;
} ) ;
} ) ;
} ) ;
+2
-617

@@ -29,619 +29,4 @@ /*

exports.LinkedList = require( './LinkedList.js' ) ;
exports.BinaryTree = require( './BinaryTree.js' ) ;
function List( ... elements ) {
this.head = null ;
this.tail = null ;
this.length = 0 ;
if ( elements.length ) {
this.push( ... elements ) ;
}
}
module.exports = List ;
function Slot( list , previous , next , element ) {
this.list = list ;
this.previous = previous ;
this.next = next ;
this.element = element ;
}
List.Slot = Slot ;
// Array.prototype.includes() uses this for value equality
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
function isIdenticalTo( x , y ) {
return x === y || ( Number.isNaN( x ) && Number.isNaN( y ) ) ;
}
List.prototype.values = function *() {
var current = this.head ;
while ( current ) {
yield current.element ;
current = current.next ;
}
} ;
List.prototype[Symbol.iterator] = List.prototype.values ;
List.from = function from( iterable ) {
var index , slot , element ,
list = new List() ;
for ( element of iterable ) {
slot = new Slot( list , list.tail , null , element ) ;
if ( ! list.head ) {
// This is the first element
list.head = list.tail = slot ;
}
else {
list.tail.next = slot ;
list.tail = slot ;
}
list.length ++ ;
}
return list ;
} ;
// Similar to .keys()
List.prototype.slots = function slots() {
var slots = new Array( this.length ) ,
index = 0 ,
current = this.head ;
while ( current ) {
slots[ index ++ ] = current ;
current = current.next ;
}
return slots ;
} ;
List.prototype.push =
List.prototype.append = function append( ... elements ) {
var index , slot ;
if ( ! elements.length ) { return ; }
this.length += elements.length ;
slot = new Slot( this , this.tail , null , elements[ 0 ] ) ;
if ( this.tail ) {
this.tail.next = slot ;
this.tail = slot ;
}
else {
this.head = this.tail = slot ;
}
for ( index = 1 ; index < elements.length ; index ++ ) {
slot = new Slot( this , this.tail , null , elements[ index ] ) ;
this.tail.next = slot ;
this.tail = slot ;
}
} ;
List.prototype.unshift =
List.prototype.prepend = function prepend( ... elements ) {
var index , slot ;
if ( ! elements.length ) { return ; }
this.length += elements.length ;
index = elements.length - 1 ;
slot = new Slot( this , null , this.head , elements[ index ] ) ;
if ( this.head ) {
this.head.previous = slot ;
this.head = slot ;
}
else {
this.head = this.tail = slot ;
}
while ( index -- ) {
slot = new Slot( this , null , this.head , elements[ index ] ) ;
this.head.previous = slot ;
this.head = slot ;
}
} ;
List.prototype.pop = function pop() {
if ( ! this.tail ) { return ; }
var slot = this.tail ;
this.tail = slot.previous ;
if ( this.tail ) {
this.tail.next = null ;
}
else {
// That was the last element
this.head = null ;
}
this.length -- ;
return slot.element ;
} ;
List.prototype.shift = function shift() {
if ( ! this.head ) { return ; }
var slot = this.head ;
this.head = slot.next ;
if ( this.head ) {
this.head.previous = null ;
}
else {
// That was the last element
this.tail = null ;
}
this.length -- ;
return slot.element ;
} ;
/*
Advanced Array-like features
*/
List.prototype.slotOf = function slotOf( element , fromSlot ) {
var current ;
if ( fromSlot ) {
if ( fromSlot.list !== this ) { return ; }
current = fromSlot ;
}
else {
current = this.head ;
}
while ( current ) {
if ( isIdenticalTo( current.element , element ) ) { return current ; }
current = current.next ;
}
return null ;
} ;
List.prototype.lastSlotOf = function lastSlotOf( element , fromSlot ) {
var current ;
if ( fromSlot ) {
if ( fromSlot.list !== this ) { return ; }
current = fromSlot ;
}
else {
current = this.tail ;
}
while ( current ) {
if ( isIdenticalTo( current.element , element ) ) { return current ; }
current = current.previous ;
}
return null ;
} ;
// Almost like .indexOf()
List.prototype.includes = function includes( element , fromSlot ) {
var current ;
if ( fromSlot ) {
if ( fromSlot.list !== this ) { return false ; }
current = fromSlot ;
}
else {
current = this.head ;
}
while ( current ) {
if ( isIdenticalTo( current.element , element ) ) { return true ; }
current = current.next ;
}
return false ;
} ;
List.prototype.forEach = function forEach( fn , thisArg ) {
var current = this.head ;
while ( current ) {
fn.call( thisArg , current.element , current , this ) ;
current = current.next ;
}
} ;
List.prototype.some = function some( fn , thisArg ) {
var current = this.head ;
while ( current ) {
if ( fn.call( thisArg , current.element , current , this ) ) { return true ; }
current = current.next ;
}
return false ;
} ;
List.prototype.every = function every( fn , thisArg ) {
var current = this.head ;
while ( current ) {
if ( ! fn.call( thisArg , current.element , current , this ) ) { return false ; }
current = current.next ;
}
return true ;
} ;
List.prototype.find = function find( fn , thisArg ) {
var current = this.head ;
while ( current ) {
if ( fn.call( thisArg , current.element , current , this ) ) {
return current.element ;
}
current = current.next ;
}
return ;
} ;
List.prototype.findSlot = function findSlot( fn , thisArg ) {
var current = this.head ;
while ( current ) {
if ( fn.call( thisArg , current.element , current , this ) ) {
return current ;
}
current = current.next ;
}
return null ;
} ;
List.prototype.map = function map( fn , thisArg ) {
var newList = new List() ,
newElement ,
slot ,
current = this.head ;
while ( current ) {
newElement = fn.call( thisArg , current.element , current , this ) ;
slot = new Slot( newList , newList.tail , null , newElement ) ;
if ( newList.tail ) {
newList.tail.next = slot ;
newList.tail = slot ;
}
else {
newList.head = newList.tail = slot ;
}
current = current.next ;
}
newList.length = this.length ;
return newList ;
} ;
List.prototype.reduce = function reduce( fn , accumulator ) {
var current = this.head ;
while ( current ) {
accumulator = fn( accumulator , current.element , current , this ) ;
current = current.next ;
}
return accumulator ;
} ;
List.prototype.reduceRight = function reduceRight( fn , accumulator ) {
var current = this.tail ;
while ( current ) {
accumulator = fn( accumulator , current.element , current , this ) ;
current = current.previous ;
}
return accumulator ;
} ;
List.prototype.filter = function filter( fn , thisArg ) {
var newList = new List() ,
slot ,
current = this.head ;
while ( current ) {
if ( fn.call( thisArg , current.element , current , this ) ) {
slot = new Slot( newList , newList.tail , null , current.element ) ;
if ( newList.tail ) {
newList.tail.next = slot ;
newList.tail = slot ;
}
else {
newList.head = newList.tail = slot ;
}
newList.length ++ ;
}
current = current.next ;
}
return newList ;
} ;
List.prototype.reverse = function reverse() {
var tmp ,
current = this.head ;
while ( current ) {
tmp = current.next ;
current.next = current.previous ;
current.previous = tmp ;
// Note: it's already inverted, so use previous instead of next
current = current.previous ;
}
tmp = this.head ;
this.head = this.tail ;
this.tail = tmp ;
return this ;
} ;
/*
Advanced custom features
*/
List.prototype.get = function get( slot ) {
if ( ! slot || slot.list !== this ) { return ; }
return slot.element ;
} ;
List.prototype.set = function set( slot , element ) {
if ( ! slot || slot.list !== this ) { return ; }
slot.element = element ;
} ;
List.prototype.deleteSlot =
List.prototype.removeSlot = function removeSlot( slot ) {
if ( ! slot || slot.list !== this ) { return false ; }
if ( slot.previous ) { slot.previous.next = slot.next ; }
if ( slot.next ) { slot.next.previous = slot.previous ; }
if ( this.head === slot ) { this.head = slot.next ; }
if ( this.tail === slot ) { this.tail = slot.previous ; }
slot.list = slot.previous = slot.next = null ;
this.length -- ;
return true ;
} ;
// Delete all occurences of a value
List.prototype.delete =
List.prototype.remove = function remove( value ) {
this.inPlaceFilter( e => ! isIdenticalTo( e , value ) ) ;
} ;
List.prototype.moveAfter = function moveAfter( slot , afterSlot ) {
if (
! slot || slot.list !== this || ! afterSlot || afterSlot.list !== this ||
slot === afterSlot || afterSlot === slot.previous
) {
return false ;
}
var beforeSlot = afterSlot.next ;
if ( this.head === slot ) { this.head = slot.next ; }
if ( this.tail === slot ) { this.tail = slot.previous ; }
else if ( this.tail === afterSlot ) { this.tail = slot ; }
if ( slot.previous ) { slot.previous.next = slot.next ; }
if ( slot.next ) { slot.next.previous = slot.previous ; }
slot.previous = afterSlot ;
afterSlot.next = slot ;
slot.next = beforeSlot ;
if ( beforeSlot ) { beforeSlot.previous = slot ; }
return true ;
} ;
List.prototype.moveBefore = function moveBefore( slot , beforeSlot ) {
if (
! slot || slot.list !== this || ! beforeSlot || beforeSlot.list !== this ||
slot === beforeSlot || beforeSlot === slot.next
) {
return false ;
}
var afterSlot = beforeSlot.previous ;
if ( this.head === slot ) { this.head = slot.next ; }
else if ( this.head === beforeSlot ) { this.head = slot ; }
if ( this.tail === slot ) { this.tail = slot.previous ; }
if ( slot.previous ) { slot.previous.next = slot.next ; }
if ( slot.next ) { slot.next.previous = slot.previous ; }
slot.next = beforeSlot ;
beforeSlot.previous = slot ;
slot.previous = afterSlot ;
if ( afterSlot ) { afterSlot.next = slot ; }
return true ;
} ;
List.prototype.moveToTail = function moveToTail( slot ) { return this.moveAfter( slot , this.tail ) ; } ;
List.prototype.moveToHead = function moveToHead( slot ) { return this.moveBefore( slot , this.head ) ; } ;
List.prototype.insertAfter = function insertAfter( afterSlot , ... elements ) {
if ( afterSlot.list !== this || ! elements.length ) { return ; }
var slot , index ,
lastSlot = afterSlot ,
beforeSlot = afterSlot.next ;
for ( index = 0 ; index < elements.length ; index ++ ) {
slot = new Slot( this , lastSlot , null , elements[ index ] ) ;
lastSlot.next = slot ;
lastSlot = slot ;
}
this.length += elements.length ;
if ( beforeSlot ) {
lastSlot.next = beforeSlot ;
beforeSlot.previous = lastSlot ;
}
else {
this.tail = lastSlot ;
}
} ;
List.prototype.insertBefore = function insertBefore( beforeSlot , ... elements ) {
if ( beforeSlot.list !== this || ! elements.length ) { return ; }
var slot ,
index = elements.length ,
lastSlot = beforeSlot ,
afterSlot = beforeSlot.previous ;
while ( index -- ) {
slot = new Slot( this , null , lastSlot , elements[ index ] ) ;
lastSlot.previous = slot ;
lastSlot = slot ;
}
this.length += elements.length ;
if ( afterSlot ) {
lastSlot.previous = afterSlot ;
afterSlot.next = lastSlot ;
}
else {
this.head = lastSlot ;
}
} ;
List.prototype.inPlaceFilter = function inPlaceFilter( fn , thisArg ) {
var lastSlot = null ,
nextSlot ,
slot = this.head ;
this.head = null ;
while ( slot ) {
slot.previous = lastSlot ;
nextSlot = slot.next ; // backup that
if ( fn.call( thisArg , slot.element , slot , this ) ) {
if ( ! this.head ) { this.head = slot ; }
lastSlot = slot ;
}
else {
if ( lastSlot ) { lastSlot.next = slot.next ; }
this.length -- ;
slot.list = slot.previous = slot.next = null ;
}
slot = nextSlot ;
}
this.tail = lastSlot ;
return this ;
} ;
{
"name": "chain-lightning",
"version": "0.2.1",
"version": "0.3.0",
"description": "Linked list",

@@ -5,0 +5,0 @@ "main": "lib/chainlightning.js",

/*
Chain Lightning
Copyright (c) 2018 Cédric Ronvel
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
"use strict" ;
/* global describe, it, before, after */
var List = require( '..' ) ;
function sanityCheck( list ) {
var length = 0 ,
lastSlot = null ,
slot = list.head ;
while ( slot ) {
expect( slot.list ).to.be( list ) ;
expect( slot.previous ).to.be( lastSlot ) ;
// Useless because we precisely come from that lastSlot
//if ( lastSlot ) { expect( lastSlot.next ).to.be( slot ) ; }
length ++ ;
lastSlot = slot ;
slot = slot.next ;
}
expect( list.tail ).to.be( lastSlot ) ;
expect( list.length ).to.be( length ) ;
}
/* Tests */
describe( "Basic features" , () => {
it( "constructor arguments should be added as elements" , () => {
var list ;
list = new List() ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( 'jack' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' ] ) ;
sanityCheck( list ) ;
list = new List( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
sanityCheck( list ) ;
} ) ;
it( "List.from() should create a list from any iterable" , () => {
var list ;
list = List.from( new Set() ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = List.from( new Set( [ 'jack' ] ) ) ;
expect( [ ... list ] ).to.equal( [ 'jack' ] ) ;
sanityCheck( list ) ;
list = List.from( new Set( [ 'jack' , 'jean' , 'steve' ] ) ) ;
sanityCheck( list ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
} ) ;
it( ".push()/.append()" , () => {
var list ;
list = new List() ;
expect( list ).to.have.length( 0 ) ;
list.push( 'bob' ) ;
list.append( 'bill' ) ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'bob' , 'bill' , 'jack' , 'jean' , 'steve' ] ) ;
sanityCheck( list ) ;
list = new List() ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".unshift()/.prepend()" , () => {
var list ;
list = new List() ;
expect( list ).to.have.length( 0 ) ;
list.unshift( 'bob' ) ;
list.prepend( 'bill' ) ;
list.unshift( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' , 'bill' , 'bob' ] ) ;
sanityCheck( list ) ;
list = new List() ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".pop()" , () => {
var list ;
list = new List() ;
expect( list.pop() ).to.be( undefined ) ;
expect( list ).to.have.length( 0 ) ;
sanityCheck( list ) ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
sanityCheck( list ) ;
expect( list.pop() ).to.be( 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' ] ) ;
sanityCheck( list ) ;
expect( list.pop() ).to.be( 'jean' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' ] ) ;
sanityCheck( list ) ;
expect( list.pop() ).to.be( 'jack' ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
expect( list.pop() ).to.be( undefined ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
} ) ;
it( ".shift()" , () => {
var list ;
list = new List() ;
expect( list.shift() ).to.be( undefined ) ;
expect( list ).to.have.length( 0 ) ;
sanityCheck( list ) ;
list.push( 'jack' , 'jean' , 'steve' ) ;
expect( [ ... list ] ).to.equal( [ 'jack' , 'jean' , 'steve' ] ) ;
sanityCheck( list ) ;
expect( list.shift() ).to.be( 'jack' ) ;
expect( [ ... list ] ).to.equal( [ 'jean' , 'steve' ] ) ;
sanityCheck( list ) ;
expect( list.shift() ).to.be( 'jean' ) ;
expect( [ ... list ] ).to.equal( [ 'steve' ] ) ;
sanityCheck( list ) ;
expect( list.shift() ).to.be( 'steve' ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
expect( list.shift() ).to.be( undefined ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
} ) ;
} ) ;
describe( "Advanced Array-like features" , () => {
it( ".slots()" , () => {
var slots ;
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new List( e1 , e2 , e3 ) ;
slots = list.slots() ;
expect( slots ).to.be.an( Array ) ;
expect( slots ).to.have.length( 3 ) ;
expect( slots.map( e => e.element ) ).to.equal( [ e1 , e2 , e3 ] ) ;
list = new List() ;
slots = list.slots() ;
expect( slots ).to.be.an( Array ) ;
expect( slots ).to.have.length( 0 ) ;
expect( slots.map( e => e.element ) ).to.equal( [] ) ;
list = new List( e1 , e2 , e2 , e2 , e3 ) ;
slots = list.slots() ;
expect( slots ).to.be.an( Array ) ;
expect( slots ).to.have.length( 5 ) ;
expect( slots.map( e => e.element ) ).to.equal( [ e1 , e2 , e2 , e2 , e3 ] ) ;
} ) ;
it( ".slotOf()/.lastSlotOf()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new List( e1 , e2 , e3 ) ;
expect( list.slotOf( e2 ).element ).to.be( e2 ) ;
expect( list.slotOf( e4 ) ).to.be( null ) ;
list.push( e2 , e2 , e2 ) ;
list.set( list.slotOf( e2 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bob' } ] ) ;
list.set( list.lastSlotOf( e2 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ { v: 'jack' } , { v: 'bobby' } , { v: 'steve' } , { v: 'bob' } , { v: 'bob' } , { v: 'bobby' } ] ) ;
} ) ;
it( ".includes()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new List() ;
expect( list.includes( e2 ) ).to.be.false() ;
list = new List( e1 ) ;
expect( list.includes( e2 ) ).to.be.false() ;
list = new List( e1 , e3 ) ;
expect( list.includes( e2 ) ).to.be.false() ;
list = new List( e2 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
list = new List( e2 , e2 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
list = new List( e1 , e2 , e3 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
list = new List( e1 , e3 , e2 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
list = new List( e2 , e1 , e3 ) ;
expect( list.includes( e2 ) ).to.be.true() ;
} ) ;
it( ".forEach()" , () => {
var list ,
accumulator = [] ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new List() ;
list.forEach( element => accumulator.push( element.v ) ) ;
expect( accumulator ).to.equal( [] ) ;
list = new List( e1 , e2 , e3 ) ;
list.forEach( element => accumulator.push( element.v ) ) ;
expect( accumulator ).to.equal( [ 'jack' , 'bob' , 'steve' ] ) ;
} ) ;
it( ".some()/.every()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new List() ;
expect( list.some( element => element.v === 'bob' ) ).to.be.false() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.true() ;
list = new List( e1 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.false() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new List( e2 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.true() ;
list = new List( e1 , e2 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new List( e2 , e1 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new List( e1 , e2 , e3 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new List( e1 , e2 , e2 , e3 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.false() ;
list = new List( e2 , e2 , e2 ) ;
expect( list.some( element => element.v === 'bob' ) ).to.be.true() ;
expect( list.every( element => element.v === 'bob' ) ).to.be.true() ;
} ) ;
it( ".find()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bob' } ;
list = new List( e1 , e2 , e3 ) ;
expect( list.find( element => element.v === 'bob' ) ).to.be( e2 ) ;
expect( list.find( element => element.v === 'bobby' ) ).to.be( undefined ) ;
list.push( e4 ) ;
expect( list.find( element => element.v === 'bob' ) ).to.be( e2 ) ;
list.unshift( e4 ) ;
expect( list.find( element => element.v === 'bob' ) ).to.be( e4 ) ;
} ) ;
it( ".findSlot()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bob' } ;
list = new List( e1 , e2 , e3 ) ;
expect( list.get( list.findSlot( element => element.v === 'bob' ) ) ).to.be( e2 ) ;
expect( list.findSlot( element => element.v === 'bobby' ) ).to.be( null ) ;
expect( list.get( list.findSlot( element => element.v === 'bobby' ) ) ).to.be( undefined ) ;
list.push( e4 ) ;
expect( list.get( list.findSlot( element => element.v === 'bob' ) ) ).to.be( e2 ) ;
list.unshift( e4 ) ;
expect( list.get( list.findSlot( element => element.v === 'bob' ) ) ).to.be( e4 ) ;
} ) ;
it( ".map()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new List().map( element => element.v + element.v ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e1 ).map( element => element.v + element.v ) ;
expect( [ ... list ] ).to.equal( [ 'jackjack' ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ).map( element => element.v + element.v ) ;
expect( [ ... list ] ).to.equal( [ 'jackjack' , 'bobbob' , 'stevesteve' ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".reduce()/.reduceRight" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new List() ;
expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ;
expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( '' ) ;
list = new List( e1 ) ;
expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ;
expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jack' ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.reduce( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'jackbobsteve' ) ;
expect( list.reduceRight( ( accumulator , element ) => accumulator + element.v , '' ) ).to.equal( 'stevebobjack' ) ;
} ) ;
it( ".filter()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new List( e2 , e2 , e2 ) ;
expect( list.filter( () => true ) ).not.to.be( list ) ;
list = new List().filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e1 ).filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 ] ) ;
sanityCheck( list ) ;
list = new List( e1 ).filter( element => element.v.length < 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ).filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e3 ).filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e2 , e2 , e2 ).filter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ).filter( element => element.v.length < 4 ) ;
expect( [ ... list ] ).to.equal( [ e2 ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".reverse()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new List().reverse() ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e1 ).reverse() ;
expect( [ ... list ] ).to.equal( [ e1 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ).reverse() ;
expect( [ ... list ] ).to.equal( [ e3 , e2 , e1 ] ) ;
sanityCheck( list ) ;
} ) ;
it( "missing .concat()" ) ;
it( "missing .copyWithin()" ) ;
it( "missing .slice()" ) ;
it( "missing .splice()" ) ;
it( "missing .sort()" ) ;
} ) ;
describe( "Advanced custom features" , () => {
it( ".removeSlot()/.deleteSlot()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new List( e1 , e2 , e3 ) ;
list.removeSlot( list.slotOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List() ;
list.removeSlot( list.slotOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e2 ) ;
list.removeSlot( list.slotOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e2 , e1 , e3 ) ;
list.removeSlot( list.slotOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e3 , e2 ) ;
list.removeSlot( list.slotOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".remove()/.delete()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new List( e1 , e2 , e3 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List() ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e2 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e2 , e1 , e3 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e3 , e2 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
// Remove all occurences
list = new List( e2 , e2 , e2 , e1 , e2 , e3 , e2 ) ;
list.remove( e2 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
// NaN test
list = new List( NaN , NaN , NaN , e1 , NaN , e3 , NaN ) ;
list.remove( NaN ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".moveAfter()/.moveToTail()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.slotOf( e1 ) , list.slotOf( e1 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.slotOf( e1 ) , list.slotOf( e2 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.slotOf( e1 ) , list.slotOf( e3 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e3 , e1 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveToTail( list.slotOf( e1 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e3 , e1 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.slotOf( e2 ) , list.slotOf( e1 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.slotOf( e2 ) , list.slotOf( e2 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.slotOf( e2 ) , list.slotOf( e3 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveToTail( list.slotOf( e2 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.slotOf( e3 ) , list.slotOf( e1 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.slotOf( e3 ) , list.slotOf( e2 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveAfter( list.slotOf( e3 ) , list.slotOf( e3 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveToTail( list.slotOf( e3 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".moveBefore()/.moveToHead()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.slotOf( e1 ) , list.slotOf( e1 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveToHead( list.slotOf( e1 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.slotOf( e1 ) , list.slotOf( e2 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.slotOf( e1 ) , list.slotOf( e3 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.slotOf( e2 ) , list.slotOf( e1 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveToHead( list.slotOf( e2 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e2 , e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.slotOf( e2 ) , list.slotOf( e2 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.slotOf( e2 ) , list.slotOf( e3 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.slotOf( e3 ) , list.slotOf( e1 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e3 , e1 , e2 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveToHead( list.slotOf( e3 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e3 , e1 , e2 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.slotOf( e3 ) , list.slotOf( e2 ) ) ).to.be.true() ;
expect( [ ... list ] ).to.equal( [ e1 , e3 , e2 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ) ;
expect( list.moveBefore( list.slotOf( e3 ) , list.slotOf( e3 ) ) ).to.be.false() ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".insertAfter()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new List( e1 , e2 , e3 ) ;
list.insertAfter( list.slotOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list.insertAfter( list.slotOf( e2 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e4 , e3 ] ) ;
sanityCheck( list ) ;
list.insertAfter( list.slotOf( e1 ) , e4 , e4 , e4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e4 , e4 , e4 , e2 , e4 , e3 ] ) ;
sanityCheck( list ) ;
list.insertAfter( list.slotOf( e3 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e4 , e4 , e4 , e2 , e4 , e3 , e4 ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".insertBefore()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ,
e4 = { v: 'bobby' } ;
list = new List( e1 , e2 , e3 ) ;
list.insertBefore( list.slotOf( e2 ) ) ;
expect( [ ... list ] ).to.equal( [ e1 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list.insertBefore( list.slotOf( e2 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e4 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list.insertBefore( list.slotOf( e1 ) , e4 , e4 , e4 ) ;
expect( [ ... list ] ).to.equal( [ e4 , e4 , e4 , e1 , e4 , e2 , e3 ] ) ;
sanityCheck( list ) ;
list.insertBefore( list.slotOf( e3 ) , e4 ) ;
expect( [ ... list ] ).to.equal( [ e4 , e4 , e4 , e1 , e4 , e2 , e4 , e3 ] ) ;
sanityCheck( list ) ;
} ) ;
it( ".inPlaceFilter()" , () => {
var list ,
e1 = { v: 'jack' } ,
e2 = { v: 'bob' } ,
e3 = { v: 'steve' } ;
list = new List( e2 , e2 , e2 ) ;
expect( list.inPlaceFilter( () => true ) ).to.be( list ) ;
list = new List().inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e1 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 ] ) ;
sanityCheck( list ) ;
list = new List( e1 ).inPlaceFilter( element => element.v.length < 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e1 , e3 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [ e1 , e3 ] ) ;
sanityCheck( list ) ;
list = new List( e2 , e2 , e2 ).inPlaceFilter( element => element.v.length >= 4 ) ;
expect( [ ... list ] ).to.equal( [] ) ;
sanityCheck( list ) ;
list = new List( e1 , e2 , e3 ).inPlaceFilter( element => element.v.length < 4 ) ;
expect( [ ... list ] ).to.equal( [ e2 ] ) ;
sanityCheck( list ) ;
} ) ;
} ) ;