UNPKG

doubly

Version:

Doubly linked list in TypeScript

995 lines (927 loc) 25.5 kB
import LinkedList from '..' import Node from '../Node' describe('LinkedList', () => { describe('constructor', () => { it('should create a linkedlist', () => { const list = new LinkedList() expect(list).toBeInstanceOf(LinkedList) }) it('should create a linkedlist w/ head', () => { const head = new Node(1) const list = new LinkedList({ head }) expect(list).toBeInstanceOf(LinkedList) expect(list.head).toBe(list.tail) expect(list.head).toBe(head) }) it('should create a linkedlist w/ head', () => { const sourceList = new LinkedList() sourceList.push(1) sourceList.push(2) sourceList.push(3) const head = sourceList.head const list = new LinkedList({ head }) expect(list).toBeInstanceOf(LinkedList) expect(list.head).toBe(sourceList.head) expect(list.tail).toBe(sourceList.tail) }) }) describe('at', () => { it('should get value at index (empty list)', () => { const list = new LinkedList() expect(list.at(2)).toMatchInlineSnapshot(`undefined`) }) it('should get value at index', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) expect(list.at(2)).toMatchInlineSnapshot(`11`) }) it('should get value at index (oob)', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) expect(list.at(100)).toMatchInlineSnapshot(`undefined`) }) }) describe('concat', () => { it('should concat lists', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) const list2 = new LinkedList() list2.push(4) list2.push(5) list2.push(6) const list3 = list.concat(list2) const values = [...list3] expect(values).toMatchInlineSnapshot(` Array [ 1, 2, 3, 4, 5, 6, ] `) expect(list3.size).toBe(values.length) }) }) describe('delete', () => { it('should error if index is negative', () => { const list = new LinkedList() expect(() => list.delete(-1)).toThrowErrorMatchingInlineSnapshot( `"\\"delete\\" negative index not supported"`, ) }) it('should delete at index (empty list)', () => { const list = new LinkedList() expect(list.delete(2)).toMatchInlineSnapshot(`false`) }) it('should delete at index (index exists, first value)', () => { const list = new LinkedList() list.push(100) list.push(200) expect(list.delete(0)).toMatchInlineSnapshot(`true`) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 200, ] `) expect(list.size).toBe(values.length) }) it('should delete at index (index exists, last value)', () => { const list = new LinkedList() list.push(100) list.push(200) expect(list.delete(1)).toMatchInlineSnapshot(`true`) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 100, ] `) expect(list.size).toBe(values.length) }) it('should delete at index (index exists, 1 of 5)', () => { const list = new LinkedList() list.push(100) list.push(200) list.push(300) list.push(400) list.push(500) expect(list.delete(1)).toMatchInlineSnapshot(`true`) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 100, 300, 400, 500, ] `) expect(list.size).toBe(values.length) }) it('should delete at index (index exists, 2 of 5)', () => { const list = new LinkedList() list.push(100) list.push(200) list.push(300) list.push(400) list.push(500) expect(list.delete(2)).toMatchInlineSnapshot(`true`) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 100, 200, 400, 500, ] `) expect(list.size).toBe(values.length) }) it('should delete at index (index exists, 3 of 5)', () => { const list = new LinkedList() list.push(100) list.push(200) list.push(300) list.push(400) list.push(500) expect(list.delete(3)).toMatchInlineSnapshot(`true`) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 100, 200, 300, 500, ] `) expect(list.size).toBe(values.length) }) }) describe('deleteNode', () => { it('should not deleteNode twice (head)', () => { const list = new LinkedList() list.push(1) const node = list.tail list.push(2) list.push(3) list.deleteNode(node) list.deleteNode(node) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 2, 3, ] `) expect(list.size).toBe(values.length) }) it('should not deleteNode twice (middle)', () => { const list = new LinkedList() list.push(1) list.push(2) const node = list.tail list.push(3) list.deleteNode(node) list.deleteNode(node) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 1, 3, ] `) expect(list.size).toBe(values.length) }) it('should not deleteNode twice (tail)', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) const node = list.tail list.deleteNode(node) list.deleteNode(node) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 1, 2, ] `) expect(list.size).toBe(values.length) }) }) describe('every', () => { it('should iterate through all items if all return truthy', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) const cb = jest.fn(() => true) const result = list.every(cb) expect(result).toBe(true) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) expect(cb).toHaveBeenNthCalledWith(3, list.head.next.next.value, 2, list) expect(cb).toHaveBeenCalledTimes(3) }) it('should stop if returns falsy', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) const cb = jest.fn((val, i) => { return i < 1 }) const result = list.every(cb) expect(result).toBe(false) expect(cb).toHaveBeenCalledTimes(2) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) }) }) describe('forEach', () => { it('should iterate through all items', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) const cb = jest.fn() list.forEach(cb) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) expect(cb).toHaveBeenNthCalledWith(3, list.head.next.next.value, 2, list) expect(cb).toHaveBeenCalledTimes(3) }) }) describe('forEachRight', () => { it('should iterate through all items', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) const cb = jest.fn() list.forEachRight(cb) expect(cb).toHaveBeenNthCalledWith(1, list.head.next.next.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) expect(cb).toHaveBeenNthCalledWith(3, list.head.value, 2, list) expect(cb).toHaveBeenCalledTimes(3) }) }) describe('filter', () => { it('should iterate through all items and create filtered list', () => { const list = new LinkedList() list.push(10) list.push(20) list.push(30) list.push(40) const cb = jest.fn((val, i) => { return i % 2 === 0 }) const list2 = list.filter(cb) const values = [...list2] expect(values).toMatchInlineSnapshot(` Array [ 10, 30, ] `) expect(list2.size).toBe(values.length) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) expect(cb).toHaveBeenNthCalledWith(3, list.head.next.next.value, 2, list) expect(cb).toHaveBeenNthCalledWith( 4, list.head.next.next.next.value, 3, list, ) expect(cb).toHaveBeenCalledTimes(4) }) }) describe('find', () => { it('should iterate through all items if no item found', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) const cb = jest.fn() list.find(cb) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) expect(cb).toHaveBeenNthCalledWith(3, list.head.next.next.value, 2, list) expect(cb).toHaveBeenCalledTimes(3) }) it('should return node if item found', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) const cb = jest.fn().mockReturnValueOnce(true) const result = list.find(cb) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(result).toBe(list.head.value) expect(cb).toHaveBeenCalledTimes(1) }) }) describe('findIndex', () => { it('should iterate through all items if no item found', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) const cb = jest.fn() const result = list.findIndex(cb) expect(result).toEqual(-1) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) expect(cb).toHaveBeenNthCalledWith(3, list.head.next.next.value, 2, list) expect(cb).toHaveBeenCalledTimes(3) }) it('should return index if item found', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) expect( list.findIndex((val) => val === list.head.value), ).toMatchInlineSnapshot(`0`) expect( list.findIndex((val) => val === list.head.next.value), ).toMatchInlineSnapshot(`1`) expect( list.findIndex((val) => val === list.head.next.next.value), ).toMatchInlineSnapshot(`2`) expect( list.findIndex((val) => val === list.tail.value), ).toMatchInlineSnapshot(`2`) }) }) describe('includes', () => { it('should return true if the list includes the value', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) expect(list.includes(9)).toBe(true) expect(list.includes(10)).toBe(true) expect(list.includes(11)).toBe(true) }) it('should return false if the list includes the value', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) expect(list.includes(90)).toBe(false) expect(list.includes(3)).toBe(false) }) }) describe('indexOf', () => { it('should return true if the list includes the value', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) expect(list.indexOf(list.head.value)).toMatchInlineSnapshot(`0`) expect(list.indexOf(list.head.next.value)).toMatchInlineSnapshot(`1`) expect(list.indexOf(list.head.next.next.value)).toMatchInlineSnapshot(`2`) expect(list.indexOf(list.tail.value)).toMatchInlineSnapshot(`2`) }) }) describe('map', () => { it('should iterate through all items and create mapped list', () => { const list = new LinkedList() list.push(10) list.push(20) list.push(30) list.push(40) const cb = jest.fn((val, i) => { return val * 2 }) const list2 = list.map(cb) const values = [...list2] expect(values).toMatchInlineSnapshot(` Array [ 20, 40, 60, 80, ] `) expect(list2.size).toBe(values.length) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) expect(cb).toHaveBeenNthCalledWith(3, list.head.next.next.value, 2, list) expect(cb).toHaveBeenNthCalledWith( 4, list.head.next.next.next.value, 3, list, ) expect(cb).toHaveBeenCalledTimes(4) }) }) describe('pop and push', () => { it('pop empty', () => { const list = new LinkedList() list.pop() expect(list.head).toMatchInlineSnapshot(`null`) expect(list.tail).toMatchInlineSnapshot(`null`) }) it('push pop', () => { const list = new LinkedList() list.push(1) const value = list.pop() expect(value).toBe(1) expect(list.head).toMatchInlineSnapshot(`null`) expect(list.tail).toMatchInlineSnapshot(`null`) }) it('push pop push', () => { const list = new LinkedList() list.push(1) list.pop() list.push(2) expect(list.head).toMatchInlineSnapshot(` Node { "next": null, "prev": null, "value": 2, } `) expect(list.tail).toMatchInlineSnapshot(` Node { "next": null, "prev": null, "value": 2, } `) }) it('push pop push push', () => { const list = new LinkedList() list.push(1) list.pop() list.push(2) list.push(3) expect(list.head).toMatchInlineSnapshot(` Node { "next": Node { "next": null, "prev": [Circular], "value": 3, }, "prev": null, "value": 2, } `) expect(list.tail).toMatchInlineSnapshot(` Node { "next": null, "prev": Node { "next": [Circular], "prev": null, "value": 2, }, "value": 3, } `) }) it('push push', () => { const list = new LinkedList() list.push(1) list.push(2) const list2 = new LinkedList() list2.push(3) list2.push(4) expect(list.head).toMatchInlineSnapshot(` Node { "next": Node { "next": null, "prev": [Circular], "value": 2, }, "prev": null, "value": 1, } `) expect(list.tail).toMatchInlineSnapshot(` Node { "next": null, "prev": Node { "next": [Circular], "prev": null, "value": 1, }, "value": 2, } `) expect(list2.head).toMatchInlineSnapshot(` Node { "next": Node { "next": null, "prev": [Circular], "value": 4, }, "prev": null, "value": 3, } `) expect(list2.tail).toMatchInlineSnapshot(` Node { "next": null, "prev": Node { "next": [Circular], "prev": null, "value": 3, }, "value": 4, } `) }) }) describe('unshift and shift', () => { it('shift empty', () => { const list = new LinkedList() list.shift() expect(list.head).toMatchInlineSnapshot(`null`) expect(list.tail).toMatchInlineSnapshot(`null`) }) it('unshift shift', () => { const list = new LinkedList<number>() list.unshift(1) const value = list.shift() expect(value).toBe(1) expect(list.head).toMatchInlineSnapshot(`null`) expect(list.tail).toMatchInlineSnapshot(`null`) const values = [...list] expect(values).toMatchInlineSnapshot(`Array []`) expect(list.size).toBe(values.length) }) it('unshift shift unshift', () => { const list = new LinkedList() list.unshift(1) list.shift() list.unshift(2) expect(list.head).toMatchInlineSnapshot(` Node { "next": null, "prev": null, "value": 2, } `) expect(list.tail).toMatchInlineSnapshot(` Node { "next": null, "prev": null, "value": 2, } `) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 2, ] `) expect(list.size).toBe(values.length) }) it('unshift shift unshift unshift', () => { const list = new LinkedList() list.unshift(1) list.shift() list.unshift(2) list.unshift(3) expect(list.head).toMatchInlineSnapshot(` Node { "next": Node { "next": null, "prev": [Circular], "value": 2, }, "prev": null, "value": 3, } `) expect(list.tail).toMatchInlineSnapshot(` Node { "next": null, "prev": Node { "next": [Circular], "prev": null, "value": 3, }, "value": 2, } `) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 3, 2, ] `) expect(list.size).toBe(values.length) }) it('unshift unshift', () => { const list = new LinkedList() list.unshift(1) list.unshift(2) expect(list.head).toMatchInlineSnapshot(` Node { "next": Node { "next": null, "prev": [Circular], "value": 1, }, "prev": null, "value": 2, } `) expect(list.tail).toMatchInlineSnapshot(` Node { "next": null, "prev": Node { "next": [Circular], "prev": null, "value": 2, }, "value": 1, } `) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 2, 1, ] `) expect(list.size).toBe(values.length) }) }) describe('reduce', () => { it('should reduce with initial value', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) const cb = jest.fn((memo, num) => memo + num.toString()) const result = list.reduce<string>(cb, '') expect(result).toBe('123') expect(cb).toHaveBeenNthCalledWith(1, '', list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, '1', list.head.next.value, 1, list) expect(cb).toHaveBeenNthCalledWith( 3, '12', list.head.next.next.value, 2, list, ) expect(cb).toHaveBeenCalledTimes(3) }) it('should reduce without initial value', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) const cb = jest.fn((memo, num) => memo + num.toString()) const result = list.reduce<string>(cb) expect(result).toBe('123') expect(cb).toHaveBeenCalledTimes(2) expect(cb).toHaveBeenNthCalledWith(1, 1, 2, 1, list) expect(cb).toHaveBeenNthCalledWith(2, '12', 3, 2, list) }) }) describe('some', () => { it('should iterate through all items if all return truthy', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) const cb = jest.fn(() => false) const result = list.some(cb) expect(result).toBe(false) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) expect(cb).toHaveBeenNthCalledWith(3, list.head.next.next.value, 2, list) expect(cb).toHaveBeenCalledTimes(3) }) it('should stop if returns falsy', () => { const list = new LinkedList() list.push(9) list.push(10) list.push(11) const cb = jest.fn((val, i) => { return i > 0 }) const result = list.some(cb) expect(result).toBe(true) expect(cb).toHaveBeenCalledTimes(2) expect(cb).toHaveBeenNthCalledWith(1, list.head.value, 0, list) expect(cb).toHaveBeenNthCalledWith(2, list.head.next.value, 1, list) }) }) describe('splice', () => { it('splice start', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) list.push(4) list.push(5) list.push(6) const removed = list.splice(0, 3, 100, 200, 300) expect([...removed]).toMatchInlineSnapshot(` Array [ 1, 2, 3, ] `) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 100, 200, 300, 4, 5, 6, ] `) expect(list.size).toBe(values.length) }) it('splice end', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) list.push(4) list.push(5) list.push(6) const removed = list.splice(5, 0, 100, 200, 300) const removedItems = [] for (let item of removed) removedItems.push(item) expect(removedItems).toMatchInlineSnapshot(`Array []`) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 1, 2, 3, 4, 5, 6, 100, 200, 300, ] `) expect(list.size).toBe(values.length) }) it('splice after end', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) list.push(4) list.push(5) list.push(6) const removed = list.splice(100, 3, 100, 200, 300) const removedItems = [] for (let item of removed) removedItems.push(item) expect(removedItems).toMatchInlineSnapshot(`Array []`) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 1, 2, 3, 4, 5, 6, 100, 200, 300, ] `) expect(list.size).toBe(values.length) }) it('should remove', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) list.push(4) list.push(5) list.push(6) const removed = list.splice(1, 2) expect([...removed]).toMatchInlineSnapshot(` Array [ 2, 3, ] `) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 1, 4, 5, 6, ] `) expect(list.size).toBe(values.length) }) it('should remove and push items', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) list.push(4) list.push(5) list.push(6) const removed = list.splice(1, 2, 100, 200, 300) expect([...removed]).toMatchInlineSnapshot(` Array [ 2, 3, ] `) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 1, 100, 200, 300, 4, 5, 6, ] `) expect(list.size).toBe(values.length) }) it('should push items', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) list.push(4) list.push(5) list.push(6) const removed = list.splice(2, 0, 100, 200, 300) expect([...removed]).toMatchInlineSnapshot(`Array []`) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 1, 2, 100, 200, 300, 3, 4, 5, 6, ] `) expect(list.size).toBe(values.length) }) it('should push items at 0', () => { const list = new LinkedList() list.push(1) list.push(2) list.push(3) list.push(4) list.push(5) list.push(6) const removed = list.splice(0, 2, 100, 200, 300) const removedItems = [] for (let item of removed) removedItems.push(item) expect(removedItems).toMatchInlineSnapshot(` Array [ 1, 2, ] `) const values = [...list] expect(values).toMatchInlineSnapshot(` Array [ 100, 200, 300, 3, 4, 5, 6, ] `) expect(list.size).toBe(values.length) }) }) })