ds-algo-study
Version:
Just experimenting with publishing a package
337 lines (263 loc) • 7.55 kB
JavaScript
/* Expose. */
module.exports = List
List.Item = ListItem
var ListPrototype = List.prototype
var ListItemPrototype = ListItem.prototype
var IterPrototype = Iter.prototype
/* istanbul ignore next */
var $iterator = typeof Symbol === 'undefined' ? undefined : Symbol.iterator
ListPrototype.tail = ListPrototype.head = null
List.of = of
List.from = from
ListPrototype.toArray = toArray
ListPrototype.prepend = prepend
ListPrototype.append = append
/* istanbul ignore else */
if ($iterator !== undefined) {
ListPrototype[$iterator] = iterator
}
ListItemPrototype.next = ListItemPrototype.prev = ListItemPrototype.list = null
ListItemPrototype.prepend = prependItem
ListItemPrototype.append = appendItem
ListItemPrototype.detach = detach
IterPrototype.next = next
/* Constants. */
var errorMessage =
'An argument without append, prepend, or detach methods was given to `List'
/* Creates a new List: A linked list is a bit like an Array,
* but knows nothing about how many items are in it, and
* knows only about its first (`head`) and last (`tail`)
* items. Each item (e.g. `head`, `tail`, &c.) knows which
* item comes before or after it (its more like the
* implementation of the DOM in JavaScript). */
function List(/* items... */) {
if (arguments.length !== 0) {
appendAll(this, arguments)
}
}
/* Creates a new list from the arguments (each a list item)
* passed in. */
function appendAll(list, items) {
var length
var index
var item
var iter
if (!items) {
return list
}
if ($iterator !== undefined && items[$iterator]) {
iter = items[$iterator]()
item = {}
while (!item.done) {
item = iter.next()
add(item.value)
}
} else {
length = items.length
index = -1
while (++index < length) {
add(items[index])
}
}
return list
function add(item) {
if (item !== null && item !== undefined) {
list.append(item)
}
}
}
/* Creates a new list from the arguments (each a list item)
* passed in. */
function of(/* items... */) {
return appendAll(new this(), arguments)
}
/* Creates a new list from the given array-like object
* (each a list item) passed in. */
function from(items) {
return appendAll(new this(), items)
}
/* Returns the list's items as an array. This does *not* detach the items. */
function toArray() {
var item = this.head
var result = []
while (item) {
result.push(item)
item = item.next
}
return result
}
/* Prepends the given item to the list: Item will be the
* new first item (`head`). */
function prepend(item) {
var self = this
var head = self.head
if (!item) {
return false
}
if (!item.append || !item.prepend || !item.detach) {
throw new Error(errorMessage + '#prepend`.')
}
if (head) {
return head.prepend(item)
}
item.detach()
item.list = self
self.head = item
return item
}
/* Appends the given item to the list: Item will be the new
* last item (`tail`) if the list had a first item, and its
* first item (`head`) otherwise. */
function append(item) {
if (!item) {
return false
}
if (!item.append || !item.prepend || !item.detach) {
throw new Error(errorMessage + '#append`.')
}
var self = this
var head = self.head
var tail = self.tail
/* If self has a last item, defer appending to the last items append
* method, and return the result. */
if (tail) {
return tail.append(item)
}
/* If self has a first item, defer appending to the first items append
* method, and return the result. */
if (head) {
return head.append(item)
}
/* ...otherwise, there is no `tail` or `head` item yet. */
item.detach()
item.list = self
self.head = item
return item
}
/* Creates an iterator from the list. */
function iterator() {
return new Iter(this.head)
}
/* Creates a new ListItem: A linked list item is a bit like
* DOM node: It knows only about its "parent" (`list`), the
* item before it (`prev`), and the item after it (`next`). */
function ListItem() {}
/* Detaches the item operated on from its parent list. */
function detach() {
var self = this
var list = self.list
var prev = self.prev
var next = self.next
if (!list) {
return self
}
/* If self is the last item in the parent list, link the
* lists last item to the previous item. */
if (list.tail === self) {
list.tail = prev
}
/* If self is the first item in the parent list, link
* the lists first item to the next item. */
if (list.head === self) {
list.head = next
}
/* If both the last and first items in the parent list
* are the same, remove the link to the last item. */
if (list.tail === list.head) {
list.tail = null
}
/* If a previous item exists, link its next item to selfs
* next item. */
if (prev) {
prev.next = next
}
/* If a next item exists, link its previous item to selfs
* previous item. */
if (next) {
next.prev = prev
}
/* Remove links from self to both the next and previous
* items, and to the parent list. */
self.prev = self.next = self.list = null
return self
}
/* Prepends the given item *before* the item operated on. */
function prependItem(item) {
if (!item || !item.append || !item.prepend || !item.detach) {
throw new Error(errorMessage + 'Item#prepend`.')
}
var self = this
var list = self.list
var prev = self.prev
/* If self is detached, return false. */
if (!list) {
return false
}
/* Detach the prependee. */
item.detach()
/* If self has a previous item... */
if (prev) {
item.prev = prev
prev.next = item
}
/* Connect the prependee. */
item.next = self
item.list = list
/* Set the previous item of self to the prependee. */
self.prev = item
/* If self is the first item in the parent list, link the
* lists first item to the prependee. */
if (self === list.head) {
list.head = item
}
/* If the the parent list has no last item, link the lists
* last item to self. */
if (!list.tail) {
list.tail = self
}
return item
}
/* Appends the given item *after* the item operated on. */
function appendItem(item) {
if (!item || !item.append || !item.prepend || !item.detach) {
throw new Error(errorMessage + 'Item#append`.')
}
var self = this
var list = self.list
var next = self.next
if (!list) {
return false
}
/* Detach the appendee. */
item.detach()
/* If self has a next item... */
if (next) {
item.next = next
next.prev = item
}
/* Connect the appendee. */
item.prev = self
item.list = list
/* Set the next item of self to the appendee. */
self.next = item
/* If the the parent list has no last item or if self is
* the parent lists last item, link the lists last item
* to the appendee. */
if (self === list.tail || !list.tail) {
list.tail = item
}
return item
}
/* Creates a new `Iter` for looping over the `LinkedList`. */
function Iter(item) {
this.item = item
}
/* Move the `Iter` to the next item. */
function next() {
var current = this.item
this.value = current
this.done = !current
this.item = current ? current.next : undefined
return this
}