- Joined
- Mar 29, 2016
- Messages
- 688
Multidimensional Array v1.2b
Description
MultidimensionalArray library allows users to have up to 5 dimensional arrays. I didn't go for more than 5 because I don't think its needed anyway and just in case someone might find a need for arrays with 5+ dimensions, they can simply copy-paste a struct and then edit 2 characters or so - yes, that's how easy it is to increase the available dimension. Though I might make it to support more dimensions later, depending on the situation but for now I'll leave it up to 5 because of the reasons above and it would also make the code a bit longer.
So why would someone use this when there is already the Table library? Here are few reasons:
- First of all, bacause of the 3-5 dimensional support which while already doable, it would not be something that the user would have to do every time he wants to make a public resource which needs it.
- This provides a syntax which resembles that of the default jass arrays (
- This allows you to have type-specific storage (But also has generic storage)
- This catches compile-time error when you do something silly like
Be sure to read the documentation inside the script for more details and information.
Script
Description
MultidimensionalArray library allows users to have up to 5 dimensional arrays. I didn't go for more than 5 because I don't think its needed anyway and just in case someone might find a need for arrays with 5+ dimensions, they can simply copy-paste a struct and then edit 2 characters or so - yes, that's how easy it is to increase the available dimension. Though I might make it to support more dimensions later, depending on the situation but for now I'll leave it up to 5 because of the reasons above and it would also make the code a bit longer.
So why would someone use this when there is already the Table library? Here are few reasons:
- First of all, bacause of the 3-5 dimensional support which while already doable, it would not be something that the user would have to do every time he wants to make a public resource which needs it.
- This provides a syntax which resembles that of the default jass arrays (
call KillUnit(u[1][2][3][4][5])
) which is nicer compared to something you do with your own by manually nesting tables (call KillUnit(u[1][2][3][4].unit[5])
or call KillUnit(HashTable(HashTable(HashTable(u[1])[2])[3])[4].unit[5])
)- This allows you to have type-specific storage (But also has generic storage)
- This catches compile-time error when you do something silly like
JASS:
local Unit2D u = Unit2D.create()
set u[1][2][3] = GetTriggerUnit() //Notice the excess of 1 dimension
Script
JASS:
library MultidimensionalArray /* v1.2b
*/uses /*
*/Table /* http://www.hiveworkshop.com/threads/snippet-new-table.188084/
[Resource Link] - http://www.hiveworkshop.com/threads/snippet-multidimensional-array.289785/
*///! novjass
/* This snippet allows you to have array storage. Unlike default arrays, you can use an index beyond 8190
since this snippet uses Tables which uses hashtable. Therefore, saving data with a really large index
such as using GetHandleId(handle) as the index would not be a problem.
But you may ask, why would we want to use this when there is already the Table library which can support
any type that we want to store? First, this has a feature which supports multidimensions that allows you
to have up to 5-dimensional array. Table already allows you to create multidimensional storage if you do
proper nesting but this library removes the need for users to do it on their own and this also helps set
a standard instead of having redundant scripts that work for the same puspose. Secondly, unlike Table,
this implements a type specific storage i.e., you can create an array storage that is only exclusive for
a specific type but of course, this also provides a generic storage like Table but with a nicer API.
Furthermore, this includes some safety precautions such as compile time safety which throws an error if
you're using an incorrect number of dimensions, as well as preventing the use of an Array instance which
isn't allocated in DEBUG_MODE. lastly, this gives users a nice and intuitive syntax which resembles that
of the original vanilla Jass arrays (call KillUnit(u[1][3])) without having a need for an ugly keyword
at the end (ex: call KillUnit(u[1].unit[3])). */
|=========|
| Credits |
|=========|
/* AGD : Author
Bribe : For the Table library, and for the algorithm of making n-dimensional storage by nesting Tables */
|-----|
| API |
|-----|
Creating an Array:
/* Creates a new array for a specific type */
local Unit1D u = Array.create()
local Unit3D u3 = Unit3D.create()
local Unit5D u5 = Timer4D.create() //You could actually use any of the dimensional array creator
local Array4D a4 = Array.create()
Storing inside an Array:
/* Stores data inside an array */
set u[GetHandleId(timer)] = GetTriggerUnit()
set u3[0x2000]['AAAA'] = GetTriggerUnit() //Syntax error: number of indexes does not match with the number of dimensions
set u5[1][2][3][4][5] = GetTriggerUnit()
set a4[1][2][3][4].unit = GetTriggerUnit()
Retrieving from an Array:
/* Retrieves data from an array */
call KillUnit(u[1234567])
call KillUnit(u3['A']['B']['C'])
call KillUnit(u5[1][2][3][4]) //Syntax error: number of indexes does not match with the number of dimensions
call KillUnit(a4[1][2][3][4].unit)
Checking storage vacancy:
/* Checks if there is data stored inside an array index */
return u.has(index) //Similar to Table(u).unit.has(index)
return u3[1][2].has(3)
return u5[1].has(2) //Checks if the fourth dimension has index 2
return a4[1][2][3].hasHandle(4)
Removing an Array index:
/* Destroys the table instance of an index and clears all its child nodes if there are any */
call u.remove(1)
call u3[1].remove(2)
call u5[1][2][3][4][5].remove(6) //Syntax error: cannot use remove() on a node which has no children
call a4[1][2][3].removeHandle(4)
Flushing an Array Index:
/* Flushes all child nodes attached to the specific index */
call u.flush() //Flushes all data inside the array, analogous to flushing a parent hashtable
call u3[1][2][3].flush() //Syntax error: cannot clear a node which has no children, use u3[1][2].remove(3) instead
call u5[1][2].flush() //Flushes all child nodes attached to the index "2" of the second dimension
call a4[1][2].flush()
Destroying an Array:
/* Destroys an array instance, flushing all data inside it */
call u.destroy()
call u3.destroy()
call u5[1].destroy() //If destroy() is called upon a node which is not a root node, it will work like clear() instead
call a4.destroy()
//! endnovjass
static if DEBUG_MODE then
private struct S extends array
static key allocated
endstruct
private function Debug takes string msg returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "|CFFFFCC00[MultidimensionalArray] |R" + msg)
endfunction
endif
private function AllocateIndex takes integer this, integer index returns integer
debug if not Table(S.allocated).boolean[this] then
debug return 0
debug endif
debug set Table(S.allocated).boolean[HashTable(this)[index]] = true
return HashTable(this)[index]
endfunction
/*============= For a uniform allocator syntax =) ==============*/
struct Array extends array
static method create takes nothing returns thistype
static if DEBUG_MODE then
local Table t = Table.create()
set Table(S.allocated).boolean[t] = true
return t
else
return Table.create()
endif
endmethod
endstruct
/*==============================================================*/
/*====================== Struct methods ========================*/
private module Methods
static method create takes nothing returns thistype
return Array.create()
endmethod
static if not thistype.remove.exists then
method remove takes integer index returns nothing
call HashTable(this).remove(index)
endmethod
endif
static if not thistype.has.exists then
method has takes integer index returns boolean
return HashTable(this).has(index)
endmethod
endif
method flush takes nothing returns nothing
call Table(this).flush()
endmethod
method destroy takes nothing returns nothing
call Table(this).destroy()
debug set Table(S.allocated).boolean[this] = false
endmethod
endmodule
/*==============================================================*/
/*================= Generic Type Array Storage =================*/
private struct Type extends array
static key index
method operator agent= takes agent value returns nothing
debug if not Table(S.allocated).boolean[this] then
debug call Debug("|CFFFF0000[Operator agent= ERROR] : Attempted to use a non-allocated array instance|R")
debug return
debug endif
set Table(this).agent[Table(this)[index]] = value
endmethod
//! textmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS takes TYPE
method operator $TYPE$ takes nothing returns $TYPE$
return Table(this).$TYPE$[Table(this)[index]]
endmethod
method operator $TYPE$= takes $TYPE$ value returns nothing
debug if not Table(S.allocated).boolean[this] then
debug call Debug("|CFFFF0000[Operator $TYPE$= ERROR] : Attempted to use a non-allocated array instance|R")
debug return
debug endif
set Table(this).$TYPE$[Table(this)[index]] = value
endmethod
//! endtextmacro
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("integer")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("real")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("string")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("boolean")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("player")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("widget")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("destructable")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("item")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("unit")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("ability")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("timer")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("trigger")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("triggercondition")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("triggeraction")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("event")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("force")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("group")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("location")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("rect")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("boolexpr")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("sound")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("effect")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("unitpool")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("itempool")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("quest")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("questitem")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("defeatcondition")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("timerdialog")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("leaderboard")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("multiboard")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("multiboarditem")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("trackable")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("dialog")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("button")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("texttag")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("lightning")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("image")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("ubersplat")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("region")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("fogstate")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("fogmodifier")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_OPERATORS("hashtable")
endstruct
struct Array1D extends array
//! textmacro GENERIC_DIMENSIONAL_ARRAY_METHODS takes NAME, TYPE
method has$NAME$ takes integer index returns boolean
return Table(this).$TYPE$.has(index)
endmethod
method remove$NAME$ takes integer index returns nothing
call Table(this).$TYPE$.remove(index)
endmethod
//! endtextmacro
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Integer", "integer")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Real", "real")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("String", "string")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Boolean", "boolean")
//! runtextmacro GENERIC_DIMENSIONAL_ARRAY_METHODS("Handle", "handle")
method has takes integer index returns boolean
return .hasInteger(index) /*
*/or .hasReal(index) /*
*/or .hasString(index) /*
*/or .hasBoolean(index) /*
*/or .hasHandle(index)
endmethod
method remove takes integer index returns nothing
call .removeInteger(index)
call .removeReal(index)
call .removeString(index)
call .removeBoolean(index)
call .removeHandle(index)
endmethod
implement Methods
method operator [] takes integer index returns Type
debug if not Table(S.allocated).boolean[this] then
debug return 0
debug endif
set Table(this)[Type.index] = index
return this
endmethod
endstruct
//! textmacro NEW_DIMENSIONAL_ARRAY_STRUCT takes DIM, RETURNED
struct Array$DIM$D extends array
implement Methods
method operator [] takes integer index returns Array$RETURNED$D
return AllocateIndex(this, index)
endmethod
endstruct
//! endtextmacro
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("2", "1")
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("3", "2")
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("4", "3")
//! runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("5", "4")
// If you want to increase the maximum number of available
// dimensions, just run the textmacros above once again like:
// runtextmacro NEW_DIMENSIONAL_ARRAY_STRUCT("LAST_MAX_DIM + 1", "LAST_MAX_DIM")
/*==============================================================*/
/*================ Type Specific Array Storage =================*/
//! textmacro NEW_DIMENSIONAL_ARRAY takes NAME, TYPE
struct $NAME$1D extends array
method remove takes integer index returns nothing
call Table(this).$TYPE$.remove(index)
endmethod
method has takes integer index returns boolean
return Table(this).$TYPE$.has(index)
endmethod
implement Methods
method operator [] takes integer index returns $TYPE$
return Table(this).$TYPE$[index]
endmethod
method operator []= takes integer index, $TYPE$ value returns nothing
debug if not Table(S.allocated).boolean[this] then
debug call Debug("|CFFFFCC00[ArrayType: $NAME$]|R |CFFFF0000[Operator []= ERROR] : Attempted to use a non-allocated array instance|R")
debug return
debug endif
set Table(this).$TYPE$[index] = value
endmethod
endstruct
struct $NAME$2D extends array
implement Methods
method operator [] takes integer index returns $NAME$1D
return AllocateIndex(this, index)
endmethod
endstruct
struct $NAME$3D extends array
implement Methods
method operator [] takes integer index returns $NAME$2D
return AllocateIndex(this, index)
endmethod
endstruct
struct $NAME$4D extends array
implement Methods
method operator [] takes integer index returns $NAME$3D
return AllocateIndex(this, index)
endmethod
endstruct
struct $NAME$5D extends array
implement Methods
method operator [] takes integer index returns $NAME$4D
return AllocateIndex(this, index)
endmethod
endstruct
//! endtextmacro
// If you want to increase the maximum number of available
// dimensions, just copy the last struct above and increase the
// number of dimension in the struct name and the returned struct
// of the operator [] by 1.
/*==============================================================*/
/*======== Implement textmacros for every storage type =========*/
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Integer", "integer")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Real", "real")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Str", "string")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Boolean", "boolean")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Player", "player")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Widget", "widget")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Item", "item")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Unit", "unit")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Ability", "ability")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Timer", "timer")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Force", "force")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Group", "group")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Location", "location")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Rect", "rect")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Sound", "sound")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Effect", "effect")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Quest", "quest")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Button", "button")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Image", "image")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Region", "region")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_DIMENSIONAL_ARRAY("Hashtable", "hashtable")
/*==============================================================*/
endlibrary
JASS:
//! novjass
|------------------------------------|
| MultidimensionalArray Sample Usage |
|------------------------------------|
function Test takes nothing returns nothing
local Array1D a = Array.create()
local Unit2D u2 = Array.create()
local Unit1D u = Array.create()
local Unit5D u5 = Timer1D.create() //Could use any dimensional array creator
set a[StringHash("A")].timer = CreateTimer()
set u['AAAA'] = CreateUnit(Player(0), 'hpea', 0, 0, 0) //Storing value to an array
set u2['AAAA']['AAAB'] = CreateUnit(Player(0), 'hfoo', 0, 0, 0)
set u5['AAAA']['AAAB']['AAAC'][0x01][0x10] = CreateUnit(Player(0), 'Hblm', 0, 0, 0)
call BJDebugMsg(GetUnitName(a[StringHash("A")].unit)) //Displays "(null)"
call BJDebugMsg(GetUnitName(u['AAAA'])) //Displays "Peasant"
call BJDebugMsg(GetUnitName(u2['AAAA']['AAAB'])) //Displays "Footman"
call BJDebugMsg(GetUnitName(u5['AAAA']['AAAB']['AAAC'][0x01][0x10])) //Displays "Bloodmage"
call a.removeHandle(StringHash("A")) //removes the stored handle
call u.destroy() //Destroys an array and flushes it
call u2.flush() //Flushes all data stored in u2
call u5['AAAA'].destroy() //Works similar to u5['AAAA'].flush()
set u2[1][2] = GetSpellTargetUnit() //This works since "u2" was only flushed, not destroyed
set u[0x10101] = GetTriggerUnit() //Does nothing if DEBUG_MODE is on since UnitArray instance "u" was just destroyed
set u5[1][2][3][4][5] = GetTriggerUnit() //This works even in DEBUG_MODE since the destroyed instance is not "u5" itself
call u2[1].flush()
call u2.remove(1)
//The last two functions work similar except that aside from clearing the index, u.remove(1) also destroys The table instance
//saved into that index therefore avoiding Table instance leaks.
if a.hasHandle(1) then
call a.removeHandle(1)
elseif a.hasReal(1) then
call a.removeReal(1)
elseif a.hasString(1) then
call a.removeString(1)
elseif a.hasBoolean(1) then
call a.removeBoolean(1)
elseif a.hasInteger(1) then
call a.removeInteger(1)
endif
//Or if you want to remove whatever type is saved in a certain index of the generic type array 'a', you could simply do:
if a.has(1) then
call a.remove(1)
endif
endfunction
//! endnovjass
v1.2b
- Changed some method APIs
- Changed removeInt to removeInteger and hasInt to hasInteger
- Changed removeStr to removeString and hasStr to hasString
- Added two generic array methods has() and remove()
v1.2
- Added an agent store method to the generic type storage
- Changed AllocateIndex from being a module to a function for a lesser script size
- Updated the documentation
- Fixed some errors
v1.1b
- Fixed some errors in the documentation and in the code
- Shortened some parts of the code using modules
- Adapted some methods from the Table library
- Added debug messages
- Other changes
v1.1
- Added a new struct named
- Updated structnames and APIs
- Fixed a bug regarding remove() method for a 1D $TYPE$Arrays
- Updated documentation
v1.0
- First Release
- Changed some method APIs
- Changed removeInt to removeInteger and hasInt to hasInteger
- Changed removeStr to removeString and hasStr to hasString
- Added two generic array methods has() and remove()
v1.2
- Added an agent store method to the generic type storage
- Changed AllocateIndex from being a module to a function for a lesser script size
- Updated the documentation
- Fixed some errors
v1.1b
- Fixed some errors in the documentation and in the code
- Shortened some parts of the code using modules
- Adapted some methods from the Table library
- Added debug messages
- Other changes
v1.1
- Added a new struct named
Array
for generic storage- Updated structnames and APIs
- Fixed a bug regarding remove() method for a 1D $TYPE$Arrays
- Updated documentation
v1.0
- First Release
Bribe : For the Table library and the algorithm for making multidimensional arrays by nesting Tables.
Last edited: