- Joined
- Nov 30, 2007
- Messages
- 1,202
This is a Array List container using a hashtable for storage to allow for a virtually endless amount of lists. It contains normal list interface, stack interface and a few utility methods such as sort, contains, containsAll, indexOf, setRange, addRange, swap, etc.
The principle behind this library is that the API should be as accomodating as possible, and not minimalistic, so if you have any ideas feel free to post suggested changes and argue your case for a new feature.
Sorting Units by unit id
Sorting players by score
How to:
It's also used here: Spell Menu System 1.2c
I've now included
This is a table version of the same library, I've decided to include it should any one prefer that. Note that my tests on the latest patch apperently doesn't inline methods properly so Table severly impeds the performance. And I'm not sure exactly why this is happening. Maybe it's a a problem on my end, and if that is the case this version is perfectly fine.
The principle behind this library is that the API should be as accomodating as possible, and not minimalistic, so if you have any ideas feel free to post suggested changes and argue your case for a new feature.
JASS:
library ArrayList requires optional HashRecyler
/*
Made by: Pinzu
Version: 1.2
This is a array list container utilizing a hashtable for storage, making it possible to allocate a virtually unlimited amount of lists.
It contains regular list interface, stack interface, and a few utility methods such as sort, addMatching, removeMatching, copy, swap, etc.
Credits:
Bannar for his ListT library which inspired this.
https://www.hiveworkshop.com/threads/containers-list-t.249011/
Bribe for his Table library which the HashRecyler is based on.
https://www.hiveworkshop.com/threads/snippet-new-table.188084/
Overfrost for suggesting improvements.
*/
// ******************************************************************************************************** \\
// API
// ******************************************************************************************************** \\
//! novjass
struct $NAME$ArrayList extends array
readonly static $TYPE$ pivot // Pivot element during sort or comparison
readonly static $TYPE$ other // Other element during sort
// Allocates the list.
static method create takes nothing returns thistype
// Deallocates the list.
method destroy takes nothing returns nothing
// Removes all elements from the list.
method clear takes nothing returns nothing
// Returns the number of elements in the list.
method operator length takes nothing returns integer
// Returns the element at the specified position.
method operator[] takes integer index returns $TYPE$
// Replaces the element at the specified position in the list with the specified element.
method operator[]= takes integer index, $TYPE$ element returns nothing
// Returns the value at the first position of the list
method first takes nothing returns $TYPE$
// Returns the value at the last position of the list
method last takes nothing returns $TYPE$
// Inserts the specified element at the specified position in the list.
method add takes integer index, $TYPE$ element returns nothing
// Removes the element at the specified position in the the list, returning the value.
method remove takes integer index returns $TYPE$
// Inserts the element to the last position of the list (stack interface).
method push takes $TYPE$ value returns thistype
// Removes the element at the last position of the list, returning the value (stack interface).
method pop takes $TYPE$ value returns $TYPE$
// Returns the index position of the first occurrence of the specified element in the list, or -1 if the list does not contain the element.
method indexOf takes $TYPE$ value returns integer
// Returns true if the index is storing any value (table interface).
method has takes integer index returns boolean
// Returns true if the list contains the specified element.
method contains takes $TYPE$ value returns boolean
// Returns true if the calling list contains all elements in the specified list.
method containsAll takes thistype list returns boolean
//Allocates a new list containing shallow copies of all elements held currently.
method clone takes nothing returns thistype
// Returns true if the list contains no elements.
method isEmpty takes nothing returns boolean
// Swaps position of 2 elements at the specified positions.
method swap takes integer indexA, integer indexB returns nothing
// Returns true if two lists are equal, same length and same structure.
method equals takes thistype list returns boolean
// Sorts the list after the provided condition (c). Note that the comparison must use the variables 'pivot' and 'other'.
// Furthermore, only '>' or '<' are valid operators for the sorting to work. Example:
private function SortUnitsByPlayer takes nothing returns boolean
return GetPlayerId(GetOwningPlayer(UnitArrayList.pivot)) < GetPlayerId(GetOwningPlayer(UnitArrayList.other))
endfunction
method sort takes code c returns nothing
// Reverses the order of all elements inside the list.
method reverse takes nothing returns nothing
// Changes the values of all assigned elements from the start position to the end of the range to the specified new value.
method setRange takes integer start, integer range, $TYPE$ value returns nothing
// Will perform a shallow copy of all elements from the specified source list to the calling list, with the given range. Starting
// from the specified source position, inserting to the specified destination position.
method addRange takes integer destPos, thistype src, integer srcPos, integer range returns thistype
//! endnovjass
// ******************************************************************************************************** \\
// Shared Resources
// ******************************************************************************************************** \\
globals
private constant integer SIZE_INDEX = -2 // Should be less than -1 to not risk collision with anything else
private trigger t = CreateTrigger()
private integer up
private integer down
endglobals
static if not LIBRARY_HashRecyler then
struct HashRecyler extends array
debug readonly static integer counter = 0
readonly static hashtable ht = InitHashtable()
static method operator[] takes integer k returns integer
return LoadInteger(ht, -1, k)
endmethod
static method operator []= takes integer k, integer tb returns nothing
call SaveInteger(ht, -1, k, tb)
endmethod
private static method onInit takes nothing returns nothing
set thistype[0] = 1
endmethod
static method alloc takes nothing returns integer
local integer k = thistype[0]
if (thistype[k] == 0) then
set thistype[0] = k + 1
else
set thistype[0] = thistype[k]
endif
debug set counter = counter + 1
return k
endmethod
static method free takes integer k returns nothing
set thistype[k] = thistype[0]
set thistype[0] = k
call FlushChildHashtable(ht, k)
debug set counter = counter - 1
endmethod
endstruct
endif
// ******************************************************************************************************** \\
// Add any textmacro below of the data types you wish to support.
//
// Notice: The last boolean is for a bug fix related to nulling hashtable handles, it's not a necessity.
// But it allows for nulling list storing handles like this:
//
// set units[0] = null or players[0] = null
//
// If you set it to false the above snippet will not work but set will be slightly more efficent.
//
// ******************************************************************************************************** \\
//! runtextmacro ARRAY_LIST_DEFINE("Int", "integer", "0", "Integer", "SavedInteger")
//! runtextmacro ARRAY_LIST_DEFINE("Real", "real", "0.", "Real", "SavedReal")
//! runtextmacro ARRAY_LIST_DEFINE("Bool", "boolean", "false", "Boolean", "SavedBoolean")
//! runtextmacro ARRAY_LIST_DEFINE("Str", "string", "null", "Str", "SavedString")
//! runtextmacro ARRAY_LIST_DEFINE("Unit", "unit", "null", "UnitHandle", "SavedHandle")
//! runtextmacro ARRAY_LIST_DEFINE("Player", "player", "null", "PlayerHandle", "SavedHandle")
// ******************************************************************************************************** \\
// Array List
// ******************************************************************************************************** \\
//! textmacro_once ARRAY_LIST_DEFINE takes NAME, TYPE, NONE, VAR, HANDLE
struct $NAME$ArrayList extends array
readonly static $TYPE$ pivot // Pivot element during sort or comparison
readonly static $TYPE$ other // Other element during sort
static method create takes nothing returns thistype
return HashRecyler.alloc()
endmethod
method destroy takes nothing returns nothing
call HashRecyler.free(this)
endmethod
private method operator length= takes integer index returns nothing
call SaveInteger(HashRecyler.ht , this, SIZE_INDEX, index)
endmethod
method operator length takes nothing returns integer
return LoadInteger(HashRecyler.ht , this, SIZE_INDEX)
endmethod
method clear takes nothing returns nothing
call FlushChildHashtable(HashRecyler.ht , this)
endmethod
method first takes nothing returns $TYPE$
return Load$VAR$(HashRecyler.ht , this, 0)
endmethod
method last takes nothing returns $TYPE$
return Load$VAR$(HashRecyler.ht , this, .length - 1)
endmethod
method operator[] takes integer index returns $TYPE$
return Load$VAR$(HashRecyler.ht , this, index)
endmethod
method operator[]= takes integer index, $TYPE$ element returns nothing
call Save$VAR$(HashRecyler.ht , this, index, element)
endmethod
method add takes integer index, $TYPE$ element returns nothing
local integer i = .length
if index < 0 or index > i then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").add:IndexOutOfBounds (" + I2S(index) + ")")
return
endif
set .length = i + 1
loop
exitwhen i == index
call Save$VAR$(HashRecyler.ht , this, i, Load$VAR$(HashRecyler.ht , this, i - 1))
set i = i - 1
endloop
call Save$VAR$(HashRecyler.ht , this, index, element)
endmethod
method remove takes integer index returns $TYPE$
local integer len = .length - 1
if index < 0 or index > len then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").remove:IndexOutOfBounds (" + I2S(index) + ")")
return $NONE$
endif
set .length = len
set other = Load$VAR$(HashRecyler.ht , this, index)
loop
exitwhen index == len
call Save$VAR$(HashRecyler.ht , this, index, Load$VAR$(HashRecyler.ht , this, index + 1))
set index = index + 1
endloop
call Remove$HANDLE$(HashRecyler.ht , this, len)
return other
endmethod
method push takes $TYPE$ value returns thistype
local integer len = .length
call Save$VAR$(HashRecyler.ht , this, len, value)
set .length = len + 1
return this
endmethod
method pop takes $TYPE$ value returns $TYPE$
local integer len = .length - 1
if len < 0 then
return $NONE$
endif
set .length = len
set other = Load$VAR$(HashRecyler.ht , this, len)
call Remove$HANDLE$(HashRecyler.ht , this, len)
return other
endmethod
method indexOf takes $TYPE$ value returns integer
local integer i = 0
local integer len = .length
loop
exitwhen i == len
if Load$VAR$(HashRecyler.ht , this, i) == value then
return i
endif
set i = i + 1
endloop
return -1
endmethod
method has takes integer index returns boolean
return index >= 0 and index < .length
endmethod
method contains takes $TYPE$ value returns boolean
return .indexOf(value) != -1
endmethod
method containsAll takes thistype list returns boolean
local integer i = 0
local integer n = list.length
local integer j
local integer m = .length
loop
exitwhen i == n
set other = Load$VAR$(HashRecyler.ht , list, i)
set j = 0
loop
if j == m then
return false
endif
exitwhen other == Load$VAR$(HashRecyler.ht , this, j)
set j = j + 1
endloop
set i = i + 1
endloop
return true
endmethod
method isEmpty takes nothing returns boolean
return .length == 0
endmethod
method swap takes integer indexA, integer indexB returns nothing
debug if indexA < 0 or indexA >= .length or indexB < 0 or indexB >= .length then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").swap:IndexOutOfBounds")
debug return
debug endif
set other = Load$VAR$(HashRecyler.ht , this, indexA)
call Save$VAR$(HashRecyler.ht , this, indexA, Load$VAR$(HashRecyler.ht , this, indexB))
call Save$VAR$(HashRecyler.ht , this, indexB, other)
endmethod
method clone takes nothing returns thistype
return create().addRange(0, this, 0, .length)
endmethod
method equals takes thistype list returns boolean
local integer i = .length
if i == list.length then
set i = i - 1
loop
exitwhen i < 0
if Load$VAR$(HashRecyler.ht , this, i) != Load$VAR$(HashRecyler.ht , list, i) then
return false
endif
set i = i - 1
endloop
return true
endif
return false
endmethod
private method setupTrigger takes code c returns nothing
call TriggerClearConditions(t)
call TriggerAddCondition(t, Condition(c))
endmethod
private method quicksort takes integer first, integer last returns nothing
if first < last then
set up = first
set down = last
set pivot = Load$VAR$(HashRecyler.ht , this, first)
loop
loop
set other = Load$VAR$(HashRecyler.ht , this, up)
exitwhen up >= last or TriggerEvaluate(t)
set up = up + 1
endloop
loop
set other = Load$VAR$(HashRecyler.ht , this, down)
exitwhen not TriggerEvaluate(t)
set down = down - 1
endloop
exitwhen up >= down
set other = Load$VAR$(HashRecyler.ht , this, up)
call Save$VAR$(HashRecyler.ht , this, up, Load$VAR$(HashRecyler.ht , this, down))
call Save$VAR$(HashRecyler.ht , this, down, other)
endloop
set other = Load$VAR$(HashRecyler.ht, this, first)
call Save$VAR$(HashRecyler.ht , this, first, Load$VAR$(HashRecyler.ht , this, down))
call Save$VAR$(HashRecyler.ht , this, down, other)
call .quicksort(first, down - 1)
call .quicksort(down + 1, last)
endif
endmethod
method sort takes code c returns nothing
debug if .length > 1400 then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "WARNING: $NAME$ArrayList.sort: Attempting to sort " + I2S(.length) + " elements.")
debug endif
call .setupTrigger(c)
call .quicksort(0, .length - 1)
endmethod
method reverseInner takes integer first, integer last returns nothing
if first < last then
set other = Load$VAR$(HashRecyler.ht , this, first)
call Save$VAR$(HashRecyler.ht , this, first, Load$VAR$(HashRecyler.ht , this, last))
call Save$VAR$(HashRecyler.ht , this, last, other)
call .reverseInner(first + 1, last - 1)
endif
endmethod
method reverse takes nothing returns nothing
//call reverseInner(0, .length - 1)
set up = 0
set down = .length - 1
loop
exitwhen down <= up
set other = Load$VAR$(HashRecyler.ht , this, up)
call Save$VAR$(HashRecyler.ht , this, up, Load$VAR$(HashRecyler.ht , this, down))
call Save$VAR$(HashRecyler.ht , this, down, other)
set up = up + 1
set down = down - 1
endloop
endmethod
method addRange takes integer destPos, thistype src, integer srcPos, integer range returns thistype
local integer srcSize = src.length
local integer len = .length
local integer i = destPos
if srcPos < 0 or srcPos == srcSize or destPos < 0 or destPos > len then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList.copy:IndexOutOfBounds")
return this
endif
if srcPos + range > srcSize then
set range = srcSize - srcPos
endif
loop
exitwhen i == len
call Save$VAR$(HashRecyler.ht , this, i + range, Load$VAR$(HashRecyler.ht , this, i)) // make room
set i = i + 1
endloop
set i = 0
loop
exitwhen i == range
call Save$VAR$(HashRecyler.ht , this, destPos + i, Load$VAR$(HashRecyler.ht , src, srcPos + i)) // fill room with copies
set i = i + 1
endloop
set .length = len + range
return this
endmethod
method setRange takes integer index, integer range, $TYPE$ value returns nothing
local integer len = .length
debug if index >= len then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").setRange:IndexOutOfBounds (" + I2S(index) + ")")
debug endif
set range = index + range
loop
exitwhen index == range or index >= len
call Save$VAR$(HashRecyler.ht , this, index, value)
set index = index + 1
endloop
endmethod
endstruct
//! endtextmacro
endlibrary
Sorting Units by unit id
Sorting players by score
How to:
JASS:
private function SortUnitsByPlayer takes nothing returns boolean
return GetPlayerId(GetOwningPlayer(UnitArrayList.pivot)) < GetPlayerId(GetOwningPlayer(UnitArrayList.other))
endfunction
local UnitArrayList list = UnitArrayList.create()
// ...
call list.sort(function SortUnitsByPlayer()) // limit is around 1400 - 2000 depending on complexity, the bigger problem is the lagg that might be inccured when sorting larger sets.
JASS:
scope Test initializer Init
globals
private integer start
private integer i
private integer j = 0
private integer MAX = 500
private IntArrayList printable
private IntArrayList largeList
private UnitArrayList units
endglobals
private function PrintJob takes nothing returns nothing
local string s = ""
local integer size = printable.size()
set start = i
set j = 0
loop
exitwhen i == size or j == 400
set s = s + I2S(printable[i]) + " "
set j = j + 1
set i = i + 1
endloop
call BJDebugMsg("Start: " + I2S(start) + "\n" + s)
if i < size then
call ExecuteFunc("Test__PrintJob")
endif
endfunction
private function PrintList takes IntArrayList list returns nothing
set i = 0
set printable = list
call ExecuteFunc("Test__PrintJob")
endfunction
private function ascendingOrder takes nothing returns boolean
return IntArrayList.pivot < IntArrayList.other
endfunction
private function descendingOrder takes nothing returns boolean
return IntArrayList.pivot > IntArrayList.other
endfunction
private function SortUnitsByPlayer takes nothing returns boolean
return GetPlayerId(GetOwningPlayer(UnitArrayList.pivot)) < GetPlayerId(GetOwningPlayer(UnitArrayList.other))
endfunction
globals
private integer array score
endglobals
private function SortPlayersByScore takes nothing returns boolean
return score[GetPlayerId(PlayerArrayList.pivot)] > score[GetPlayerId(PlayerArrayList.other)]
endfunction
private function PlayerList takes nothing returns nothing
local PlayerArrayList list = PlayerArrayList.create()
local integer i = 0
local integer length
local player p
loop
exitwhen i == 12
set score[i] = GetRandomInt(0, 100) // just a random score
call list.add(i, Player(i))
set i = i + 1
endloop
call list.sort(function SortPlayersByScore)
set i = 0
set length = list.size()
loop
exitwhen i == length
set p = list[i]
call BJDebugMsg(GetPlayerName(p) + " has a score of " + I2S(score[GetPlayerId(p)]))
set i = i + 1
endloop
endfunction
function FormatUnits takes nothing returns nothing
local integer i = 0
local integer length = units.size()
local unit u
loop
exitwhen i == length
set u = units[i]
call SetUnitX(u, i*10 - 3000)
call SetUnitY(u, 0)
set i = i + 1
endloop
endfunction
function SortUnits takes nothing returns nothing
call units.sort(function SortUnitsByPlayer)
call BJDebugMsg("Units sorted")
call ExecuteFunc("FormatUnits")
endfunction
private function UnitList takes nothing returns nothing
local integer i = 0
local integer length
set units = UnitArrayList.create()
loop
exitwhen i == 1800
call units.add(i, CreateUnit(Player(GetRandomInt(0, 24)), 'hpea', 0, 0, 0))
set i = i + 1
endloop
call BJDebugMsg("Created " + I2S(units.size()) + " number of units.")
call ExecuteFunc("SortUnits")
endfunction
private function RemoveMatchingFilter takes nothing returns boolean
return IntArrayList.pivot >= 30 and IntArrayList.pivot <= 60
endfunction
private function CreateList takes integer size returns IntArrayList
local integer k = 0
local IntArrayList list = IntArrayList.create()
loop
exitwhen k == size
call list.add(k, GetRandomInt(0, 200))
set k = k + 1
endloop
return list
endfunction
function SortLargeList takes nothing returns nothing
call largeList.sort(function ascendingOrder)
call PrintList(largeList)
endfunction
private function Main takes nothing returns nothing
/*
local integer i = 0
local IntArrayList list = IntArrayList.create()
local IntArrayList list2 = IntArrayList.create()
local IntArrayList tempList
loop
exitwhen i == 100
//call list.add(i, GetRandomInt(0, 100))
call list.add(i, i)
set i = i + 1
endloop
//call list.sort(function descendingOrder)
call PrintList(list)
// This will copy the first 50 elements from list to list2 insertion starting at 0
// The old list will however retain it's elements.
call IntArrayList.copy(list, 0, list2, 0, list.size())
call PrintList(list2)
call BJDebugMsg("size: " + I2S(list2.size()))
call BJDebugMsg("size: " + I2S(list.size()))
call list2.removeMatching(function RemoveMatchingFilter)
call PrintList(list2)
set tempList = IntArrayList.tempList
set i = 0
loop
exitwhen i == tempList.size()
call BJDebugMsg(I2S(i) + ": " + I2S(tempList[i]) + " was removed")
set i = i + 1
endloop
//call PlayerList()
*/
call UnitList()
//set largeList = CreateList(3200)
//call PrintList(largeList)
//call ExecuteFunc("SortLargeList")
endfunction
private function DeleteUnit takes nothing returns nothing
local integer rdm
if units.size() > 0 then
set rdm = GetRandomInt(0, units.size() - 1)
call RemoveUnit(units.remove(rdm))
call BJDebugMsg("Remaining: " + I2S(units.size()) + " -- " + I2S(rdm))
else
call PauseTimer(GetExpiredTimer())
call BJDebugMsg("Complete removal")
endif
endfunction
private function Init takes nothing returns nothing
call TimerStart(CreateTimer(), 0.2, false, function Main)
call TimerStart(CreateTimer(), 0.01, true, function DeleteUnit)
endfunction
endscope
It's also used here: Spell Menu System 1.2c
I've now included
has(index)
as a method which makes it technically possible to replace a table with this provided that the data is ordered.[/HIDDEN]
JASS:
method drop takes thistype evicted, code c returns nothing
local integer i = .length - 1
local integer j
local integer len
call .setupTrigger(c)
loop
exitwhen i < 0
set pivot = Load$VAR$(ht, this, i)
if TriggerEvaluate(t) then
if evicted != 0 then
call evicted.push(pivot)
endif
set j = i
set len = .length - 1
set .length = len
loop
exitwhen j == len
call Save$VAR$(ht, this, j, Load$VAR$(ht, this, j + 1))
set j = j + 1
endloop
call Remove$HANDLE$(ht, this, len)
endif
set i = i - 1
endloop
endmethod
method unique takes thistype evicted returns nothing
local thistype temp
local integer i
set temp = $NAME$ArrayList.create()
set i = .length - 1
loop
exitwhen i < 0
if temp.contains(this[i]) then
if evicted != 0 then
call evicted.push(this[i])
endif
call .remove(i)
else
call temp.push(this[i])
endif
set i = i - 1
endloop
call temp.destroy()
endmethod
private static method onAddMatching takes nothing returns nothing
call tempList.push(pivot)
endmethod
private static method onSetMatching takes nothing returns nothing
call Save$VAR$(ht, list, i, other)
endmethod
private static method onCountMatching takes nothing returns nothing
set up = up + 1
endmethod
private static method onRemoveMatching takes nothing returns nothing
local thistype this = list
if tempList != 0 then
call tempList.push(this.remove(i))
else
call this.remove(i)
endif
endmethod
method forEach takes integer start, integer end, integer change, code c, code a returns nothing
debug if change == 0 or (start > end and change > 0) or (start < end and change < 0) then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").forEach:InfiniteLoopDetected")
debug return
debug endif
call .setupTrigger(c, a)
set i = start
loop
exitwhen i == end
set pivot = Load$VAR$(ht, this, i)
if TriggerEvaluate(t) then
call TriggerExecute(t)
endif
set i = i + change
endloop
endmethod
method removeMatching takes thistype dest, code c returns thistype
set tempList = dest
call .forEach(.length - 1, -1, -1, c, function thistype.onRemoveMatching)
return dest
endmethod
method addMatching takes thistype srcs, code c returns nothing
set tempList = this
call srcs.forEach(0, srcs.length, 1, c, function thistype.onAddMatching)
endmethod
method setMatching takes $TYPE$ value, code c returns nothing
set other = value
call .forEach(0, .length, 1, c, function thistype.onSetMatching)
endmethod
method countMatching takes code c returns integer
set up = 0
call .forEach(0, .length, 1, c, function thistype.onCountMatching)
return up
endmethod
This is a table version of the same library, I've decided to include it should any one prefer that. Note that my tests on the latest patch apperently doesn't inline methods properly so Table severly impeds the performance. And I'm not sure exactly why this is happening. Maybe it's a a problem on my end, and if that is the case this version is perfectly fine.
JASS:
library ArrayList requires Table
/*
Made by: Pinzu
Version: 1.2
This is a array list container utilizing a hashtable for storage, making it possible to allocate a virtually unlimited amount of lists.
It contains regular list interface, stack interface, and a few utility methods such as sort, addMatching, removeMatching, copy, swap, etc.
Credits:
Bannar for his ListT library which inspired this.
https://www.hiveworkshop.com/threads/containers-list-t.249011/
Bribe for his Table library.
https://www.hiveworkshop.com/threads/snippet-new-table.188084/
Overfrost for suggesting improvements.
*/
// ******************************************************************************************************** \\
// API
// ******************************************************************************************************** \\
//! novjass
struct $NAME$ArrayList extends array
readonly static $TYPE$ pivot // Pivot element during sort or comparison
readonly static $TYPE$ other // Other element during sort
// Allocates the list.
static method create takes nothing returns thistype
// Deallocates the list.
method destroy takes nothing returns nothing
// Removes all elements from the list.
method clear takes nothing returns nothing
// Returns the number of elements in the list.
method operator length takes nothing returns integer
// Returns the element at the specified position.
method operator[] takes integer index returns $TYPE$
// Replaces the element at the specified position in the list with the specified element.
method operator[]= takes integer index, $TYPE$ element returns nothing
// Returns the value at the first position of the list
method first takes nothing returns $TYPE$
// Returns the value at the last position of the list
method last takes nothing returns $TYPE$
// Inserts the specified element at the specified position in the list.
method add takes integer index, $TYPE$ element returns nothing
// Removes the element at the specified position in the the list, returning the value.
method remove takes integer index returns $TYPE$
// Inserts the element to the last position of the list (stack interface).
method push takes $TYPE$ value returns thistype
// Removes the element at the last position of the list, returning the value (stack interface).
method pop takes $TYPE$ value returns $TYPE$
// Returns the index position of the first occurrence of the specified element in the list, or -1 if the list does not contain the element.
method indexOf takes $TYPE$ value returns integer
// Returns true if the index is storing any value (table interface).
method has takes integer index returns boolean
// Returns true if the list contains the specified element.
method contains takes $TYPE$ value returns boolean
// Returns true if the calling list contains all elements in the specified list.
method containsAll takes thistype list returns boolean
//Allocates a new list containing shallow copies of all elements held currently.
method clone takes nothing returns thistype
// Returns true if the list contains no elements.
method isEmpty takes nothing returns boolean
// Swaps position of 2 elements at the specified positions.
method swap takes integer indexA, integer indexB returns nothing
// Returns true if two lists are equal, same length and same structure.
method equals takes thistype list returns boolean
// Sorts the list after the provided condition (c). Note that the comparison must use the variables 'pivot' and 'other'.
// Furthermore, only '>' or '<' are valid operators for the sorting to work. Example:
private function SortUnitsByPlayer takes nothing returns boolean
return GetPlayerId(GetOwningPlayer(UnitArrayList.pivot)) < GetPlayerId(GetOwningPlayer(UnitArrayList.other))
endfunction
method sort takes code c returns nothing
// Reverses the order of all elements inside the list.
method reverse takes nothing returns nothing
// Changes the values of all assigned elements from the start position to the end of the range to the specified new value.
method setRange takes integer start, integer range, $TYPE$ value returns nothing
// Will perform a shallow copy of all elements from the specified source list to the calling list, with the given range. Starting
// from the specified source position, inserting to the specified destination position.
method addRange takes integer destPos, thistype src, integer srcPos, integer range returns thistype
//! endnovjass
// ******************************************************************************************************** \\
// Shared Resources
// ******************************************************************************************************** \\
globals
private constant integer SIZE_INDEX = -2 // Should be less than -1 to not risk collision with anything else
private trigger t = CreateTrigger()
private integer up
private integer down
endglobals
// ******************************************************************************************************** \\
// Add any textmacro below of the data types you wish to support
//
// ******************************************************************************************************** \\
//! runtextmacro ARRAY_LIST_DEFINE("Int", "integer", "0", "Integer", "SavedInteger")
//! runtextmacro ARRAY_LIST_DEFINE("Real", "real", "0.", "Real", "SavedReal")
//! runtextmacro ARRAY_LIST_DEFINE("Bool", "boolean", "false", "Boolean", "SavedBoolean")
//! runtextmacro ARRAY_LIST_DEFINE("Str", "string", "null", "Str", "SavedString")
//! runtextmacro ARRAY_LIST_DEFINE("Unit", "unit", "null", "UnitHandle", "SavedHandle")
//! runtextmacro ARRAY_LIST_DEFINE("Player", "player", "null", "PlayerHandle", "SavedHandle")
// ******************************************************************************************************** \\
// Array List
// ******************************************************************************************************** \\
//! textmacro_once ARRAY_LIST_DEFINE takes NAME, TYPE, NONE, VAR, HANDLE
struct $NAME$ArrayList extends array
readonly static $TYPE$ pivot // Pivot element during sort or comparison
readonly static $TYPE$ other // Other element during sort
static method create takes nothing returns thistype
return Table.create()
endmethod
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
private method operator length= takes integer index returns nothing
set Table(this).integer[SIZE_INDEX] = index
endmethod
method operator length takes nothing returns integer
return Table(this).integer[SIZE_INDEX]
endmethod
method clear takes nothing returns nothing
call Table(this).flush()
endmethod
method first takes nothing returns $TYPE$
return Table(this).$TYPE$[0]
endmethod
method last takes nothing returns $TYPE$
return Table(this).$TYPE$[.length - 1]
endmethod
method operator[] takes integer index returns $TYPE$
return Table(this).$TYPE$[index]
endmethod
method operator[]= takes integer index, $TYPE$ element returns nothing
set Table(this).$TYPE$[index] = element
endmethod
method add takes integer index, $TYPE$ element returns nothing
local integer i = .length
if index < 0 or index > i then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").add:IndexOutOfBounds (" + I2S(index) + ")")
return
endif
set .length = i + 1
loop
exitwhen i == index
set Table(this).$TYPE$[i] = Table(this).$TYPE$[i - 1]
set i = i - 1
endloop
set Table(this).$TYPE$[index] = element
endmethod
method remove takes integer index returns $TYPE$
local integer len = .length - 1
if index < 0 or index > len then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").remove:IndexOutOfBounds (" + I2S(index) + ")")
return $NONE$
endif
set .length = len
set other = Table(this).$TYPE$[index]
loop
exitwhen index == len
set Table(this).$TYPE$[index] = Table(this).$TYPE$[index + 1]
set index = index + 1
endloop
call Table(this).$TYPE$.remove(len)
return other
endmethod
method push takes $TYPE$ value returns thistype
local integer len = .length
set Table(this).$TYPE$[len] = value
set .length = len + 1
return this
endmethod
method pop takes $TYPE$ value returns $TYPE$
local integer len = .length - 1
if len < 0 then
return $NONE$
endif
set .length = len
set other = Table(this).$TYPE$[len]
call Table(this).$TYPE$.remove(len)
return other
endmethod
method indexOf takes $TYPE$ value returns integer
local integer i = 0
local integer len = .length
loop
exitwhen i == len
if Table(this).$TYPE$[i] == value then
return i
endif
set i = i + 1
endloop
return -1
endmethod
method has takes integer index returns boolean
return index >= 0 and index < .length
endmethod
method contains takes $TYPE$ value returns boolean
return .indexOf(value) != -1
endmethod
method containsAll takes thistype list returns boolean
local integer i = 0
local integer n = list.length
local integer j
local integer m = .length
loop
exitwhen i == n
set other = Table(this).$TYPE$[i]
set j = 0
loop
if j == m then
return false
endif
exitwhen other == Table(this).$TYPE$[j]
set j = j + 1
endloop
set i = i + 1
endloop
return true
endmethod
method isEmpty takes nothing returns boolean
return .length == 0
endmethod
method swap takes integer indexA, integer indexB returns nothing
debug if indexA < 0 or indexA >= .length or indexB < 0 or indexB >= .length then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").swap:IndexOutOfBounds")
debug return
debug endif
set other = Table(this).$TYPE$[indexA]
set Table(this).$TYPE$[indexA] = Table(this).$TYPE$[indexB]
set Table(this).$TYPE$[indexB] = other
endmethod
method clone takes nothing returns thistype
return create().addRange(0, this, 0, .length)
endmethod
method equals takes thistype list returns boolean
local integer i = .length
if i == list.length then
set i = i - 1
loop
exitwhen i < 0
if Table(this).$TYPE$[i] != Table(list).$TYPE$[i] then
return false
endif
set i = i - 1
endloop
return true
endif
return false
endmethod
private method setupTrigger takes code c returns nothing
call TriggerClearConditions(t)
call TriggerAddCondition(t, Condition(c))
endmethod
private method quicksort takes integer first, integer last returns nothing
if first < last then
set up = first
set down = last
set pivot = this[first]
loop
loop
set other = this[up]
exitwhen up >= last or TriggerEvaluate(t)
set up = up + 1
endloop
loop
set other = this[down]
exitwhen not TriggerEvaluate(t)
set down = down - 1
endloop
exitwhen up >= down
set other = this[up]
set this[up] = this[down]
set this[down] = other
endloop
set other = this[first]
set this[first] = this[down]
set this[down] = other
call .quicksort(first, down - 1)
call .quicksort(down + 1, last)
endif
endmethod
method sort takes code c returns nothing
debug if .length > 1400 then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "WARNING: $NAME$ArrayList.sort: Attempting to sort " + I2S(.length) + " elements.")
debug endif
call .setupTrigger(c)
call .quicksort(0, .length - 1)
endmethod
private method reverseInner takes integer first, integer last returns nothing
if first < last then
set other = this[first]
set this[first] = this[last]
set this[last] = other
call .reverseInner(first + 1, last - 1)
endif
endmethod
method reverse takes nothing returns nothing
//call reverseInner(0, .length)
set up = 0
set down = .length - 1
loop
exitwhen down <= up
set other = this[up]
set this[up] = this[down]
set this[down] = other
set up = up + 1
set down = down - 1
endloop
endmethod
method addRange takes integer destPos, thistype src, integer srcPos, integer range returns thistype
local integer srcSize = src.length
local integer len = .length
local integer i = destPos
if srcPos < 0 or srcPos == srcSize or destPos < 0 or destPos > len then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList.copy:IndexOutOfBounds")
return this
endif
if srcPos + range > srcSize then
set range = srcSize - srcPos
endif
loop
exitwhen i == len
set Table(this).$TYPE$[i + range] = Table(this).$TYPE$[i] // Make room
set i = i + 1
endloop
set i = 0
loop
exitwhen i == range
set Table(this).$TYPE$[destPos + i] = Table(this).$TYPE$[srcPos + i] // fill room with copies
set i = i + 1
endloop
set .length = len + range
return this
endmethod
method setRange takes integer index, integer range, $TYPE$ value returns nothing
local integer len = .length
debug if index >= len then
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60, "ERROR: $NAME$ArrayList(" + I2S(this) + ").setRange:IndexOutOfBounds (" + I2S(index) + ")")
debug endif
set range = index + range
loop
exitwhen index == range or index >= len
set Table(this).$TYPE$[index] = value
set index = index + 1
endloop
endmethod
endstruct
//! endtextmacro
endlibrary
Last edited: