• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • ✅ The POLL for Hive's Texturing Contest #33 is OPEN! Vote for the TOP 3 SKINS! 🔗Click here to cast your vote!

Definitive Doubly-Linked List

  • Like
Reactions: deepstrasz
A script that enables linking objects together maintaining the order of a sequence. Each node you add to the list will have a value, of any type.
Moved from here: Definitive Doubly-Linked List

Brief Example
Lua:
    local list = LinkedList.create() -- Creates a new LinkedListHead that can have nodes inserted to itself.
    local mynode = list:insert("my age is: ", true)
    --[[- Inserts *before* the given LinkedList object unless "after" is true.
      - If a value is passed, the system will attach it to the node as a generic "value"
      - Returns the inserted node that was added to the list.]]
    list:insert("69", true)
    list:insert(" :)", true)
    mynode:remove() --Removes the node from the list. Equivalent to list.remove(mynode) in this context.

   -- accessing
    -- list.head      the head (internal implementation) of the list. [node.head] has the same result.
    -- list.next      next item in the same list. [list.next] is always the first node of the list. Doesn't skip the head.
    -- list.prev      previous item in the same list. [list.prev] is always the last node of the list. Doesn't skip the head.

    print(list.next.value) --Results in 69
    print(list.n) -- number of nodes in the list.
    --loops, starting from the first node of the list, in forward direction
    for node in LinkedList.loop(list.next) do
         print(node.value)                             --should print "69" and then ":)"
    end
Lua:
--[[
    Doubly-Linked List v1.5.4.1 by Wrda, Eikonium and Bribe, with special thanks to Jampion
    ------------------------------------------------------------------------------
    A script that enables linking objects together with "previous" and "next"
    syntax.
    ------------------------------------------------------------------------------
  
LinkedList API:
    LinkedList.create() -> LinkedListHead
      - Creates a new LinkedListHead that can have nodes inserted to itself.
    list.head -> the head (internal implementation) of the list. [node.head] has the same result.
    list.next -> next item in the same list. [list.next] is always the first node of the list.
                 Doesn't skip the head.
    list.prev -> previous item in the same list. [list.prev] is always the last node of the list.
                 Doesn't skip the head.
  
    list:insert([value : any, after : boolean]) -> LinkedListNode
      - Inserts *before* the given LinkedList object unless "after" is true.
      - If a value is passed, the system will attach it to the node as a generic "value"
      - Returns the inserted node that was added to the list.
    for node in LinkedList.loop(start : LinkedList [, finish : LinkedList , backwards : boolean]) do [stuff] end
      - Shows how to iterate over all nodes in "list". Inclusive range.
      - An example of how to iterate while skipping the first and last nodes of the list:
        for node in list.next.next:loop(list.prev.prev) do print(node.value) end
      
API specific to LinkedListHead:
    list.remove(node)
      - Removes a node from list.
    list.n
      - The number of LinkedListNodes in the list.
    fromList:merge(intoList : LinkedList [, mergeBefore : boolean])
      - Removes all nodes of list "fromList" and adds them to the end of list "intoList" (or
        at the beginning, if "mergeBefore" is true).
        "fromList" needs to be the linked list head, but "into" can be anywhere in that list.
    
LinkedListNode API:
    node:remove()
      - Removes a node from whatever list it is a part of.
    node:getNext()
      - Gets the next node, ignoring the head.
    node:getPrev()
      - Gets the previous node, ignoring the head.
    node.value
      - Whatever value might have been assigned from list:insert([value])
]]
---@class LinkedList     : table
---@field head LinkedListHead
---@field next LinkedList
---@field prev LinkedList
---@class LinkedListNode : LinkedList
---@field value any
---@class LinkedListHead : LinkedList
---@field n integer
LinkedList = {}
LinkedList.__index = LinkedList
---Creates a new LinkedList head node.
---@return LinkedListHead new_list_head
function LinkedList.create()
    local head = {}
    setmetatable(head, LinkedList)
    head.next = head
    head.prev = head
    head.head = head
    head.n = 0
    return head
end
---Inserts *before* the given head/node, unless "backward" is true.
---@param value? any
---@param insertAfter? boolean
---@return LinkedListNode new_node
function LinkedList:insert(value, insertAfter)
    local node = {}   ---@type LinkedListNode
    setmetatable(node, LinkedList)
    local from = insertAfter and self.next or self
    from.prev.next = node
    node.prev = from.prev
    from.prev = node
    node.next = from
 
    node.value = value
    local head = from.head
    node.head = head
    head.n = head.n + 1
    return node
end
---Removes a node from whatever list it is a part of.
function LinkedList:remove()
    self.prev.next = self.next
    self.next.prev = self.prev
    self.head.n = self.head.n - 1
    self:destroy()
end
---Nullify the linked list's inheritance (as a matter of principle).
function LinkedList:destroy()
    setmetatable(self, nil)
end
---Gets the next node in the sequence of the list it is in, ignoring the head.
---@return LinkedListNode
function LinkedList:getNext()
    if self.next == self.head then
        return self.head.next
    end
    return self.next
end
---Gets the previous node in the sequence of the list it is in, ignoring the head.
---@return LinkedListNode
function LinkedList:getPrev()
    if self.prev == self.head then
        return self.head.prev
    end
    return self.prev
end
---Merges LinkedListHead "from" to a LinkedList "into"
---@param from LinkedListHead
---@param into LinkedList
---@param mergeBefore? boolean
function LinkedList.merge(from, into, mergeBefore)
    local head = into.head
    into = mergeBefore and into.next or into
 
    for node in from:loop() do node.head = head end
    head.n = head.n + from.n
    from.n = 0
 
    from.next.prev = into.prev
    into.prev.next = from.next
    into.prev = from.prev
    from.prev.next = into
    --reset the original list to a simple LinkedListHead
    from.next = from
    from.prev = from
end
---Loop a LinkedList from a starting node/head to a finish node/head in either direction.
---Inclusive range.
---@param start LinkedList
---@param finish? LinkedList
---@param backward? boolean
---@return function
function LinkedList.loop(start, finish, backward)
    local head = start.head
    if head.n == 0 then return end
    local direction = backward and "prev" or "next"
    local skip = start ~= head or start == finish
    if not finish or finish == head then
        return function()
            if skip then
                skip = nil
            else
                start = start[direction]
            end
            return start ~= head and start or nil
        end
    else
        return function()
            if start ~= finish or skip then
                if skip and skip ~= 1 then
                    skip = start == finish and 1
                else
                    start = start[direction]
                    if start == head then
                        start = start[direction]
                    end
                end
                return start
            end
        end
    end
end
--End of LinkedList library

Installation:
Copy the code from the spoiler and paste into a script in your map.

Credits/Contributors:
Wrda
Eikonium
Bribe
(With special thanks to Jampion)
Contents

Definitive Doubly-Linked List (Binary)

Reviews
Antares
Resource moved from the Code Archive, originally approved by Jampion: "A great linked list implementation. Looping over the list is very convenient and all functions are implemented efficiently. Approved." Approved
Top