export class LinkedList < T > { public length : any = 0 ; protected head : any ; protected tail : any ; protected current : any ; protected asArray : T [ ] = [ ] ; protected getNode ( position ) { if ( this . length === 0 || position < 0 || position >= this . length ) { throw new Error ( 'Position is out of the list' ) ; } let current = this . head ; for ( let index = 0 ; index < position ; index ++ ) { current = current . next ; } return current ; } protected createInternalArrayRepresentation ( ) { const outArray = [ ] ; let current = this . head ; while ( current ) { outArray . push ( current . value ) ; current = current . next ; } this . asArray = outArray ; } public get ( position ) { if ( this . length === 0 || position < 0 || position >= this . length ) { return void 0 ; } let current = this . head ; for ( let index = 0 ; index < position ; index ++ ) { current = current . next ; } return current . value ; } public add ( value , position = this . length ) { if ( position < 0 || position > this . length ) { throw new Error ( 'Position is out of the list' ) ; } const node = { value : value as any , next : undefined as any , previous : undefined as any } ; if ( this . length === 0 ) { this . head = node ; this . tail = node ; this . current = node ; } else { if ( position === 0 ) { node . next = this . head ; this . head . previous = node ; this . head = node ; } else if ( position === this . length ) { this . tail . next = node ; node . previous = this . tail ; this . tail = node ; } else { const currentPreviousNode = this . getNode ( position - 1 ) ; const currentNextNode = currentPreviousNode . next ; currentPreviousNode . next = node ; currentNextNode . previous = node ; node . previous = currentPreviousNode ; node . next = currentNextNode ; } } this . length ++ ; this . createInternalArrayRepresentation ( ) ; } public remove ( position = 0 ) { if ( this . length === 0 || position < 0 || position >= this . length ) { throw new Error ( 'Position is out of the list' ) ; } if ( position === 0 ) { this . head = this . head . next ; if ( this . head ) { this . head . previous = undefined ; } else { this . tail = undefined ; } } else if ( position === this . length - 1 ) { this . tail = this . tail . previous ; this . tail . next = undefined ; } else { const removedNode = this . getNode ( position ) ; removedNode . next . previous = removedNode . previous ; removedNode . previous . next = removedNode . next ; } this . length -- ; this . createInternalArrayRepresentation ( ) ; } public set ( position , value ) { if ( this . length === 0 || position < 0 || position >= this . length ) { throw new Error ( 'Position is out of the list' ) ; } const node = this . getNode ( position ) ; node . value = value ; this . createInternalArrayRepresentation ( ) ; } public toArray ( ) { return this . asArray ; } public findAll ( fn ) { let current = this . head ; const result = [ ] ; for ( let index = 0 ; index < this . length ; index ++ ) { if ( fn ( current . value , index ) ) { result . push ( { index , value : current . value } ) ; } current = current . next ; } return result ; } public push ( ... args ) { args . forEach ( ( arg ) => { this . add ( arg ) ; } ) ; return this . length ; } public pop ( ) { if ( this . length === 0 ) { return undefined ; } const last = this . tail ; this . remove ( this . length - 1 ) ; return last . value ; } public unshift ( ... args ) { args . reverse ( ) ; args . forEach ( ( arg ) => { this . add ( arg , 0 ) ; } ) ; return this . length ; } public shift ( ) { if ( this . length === 0 ) { return undefined ; } const lastItem = this . head . value ; this . remove ( ) ; return lastItem ; } public forEach ( fn ) { let current = this . head ; for ( let index = 0 ; index < this . length ; index ++ ) { fn ( current . value , index ) ; current = current . next ; } } public indexOf ( value ) { let current = this . head ; let position = 0 ; for ( let index = 0 ; index < this . length ; index ++ ) { if ( current . value === value ) { position = index ; break ; } current = current . next ; } return position ; } public some ( fn ) { let current = this . head ; let result = false ; while ( current && ! result ) { if ( fn ( current . value ) ) { result = true ; break ; } current = current . next ; } return result ; } public every ( fn ) { let current = this . head ; let result = true ; while ( current && result ) { if ( ! fn ( current . value ) ) { result = false ; } current = current . next ; } return result ; } public toString ( ) { return '[Linked List]' ; } public find ( fn ) { let current = this . head ; let result ; for ( let index = 0 ; index < this . length ; index ++ ) { if ( fn ( current . value , index ) ) { result = current . value ; break ; } current = current . next ; } return result ; } public findIndex ( fn ) { let current = this . head ; let result ; for ( let index = 0 ; index < this . length ; index ++ ) { if ( fn ( current . value , index ) ) { result = index ; break ; } current = current . next ; } return result ; } }