[vJASS] [Snippet] Multidimensional Array

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
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 (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
Be sure to read the documentation inside the script for more details and information.


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 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:
Level 13
Joined
Nov 7, 2014
Messages
570
Can we reserve the name array for a collection that stores its elements contiguously (next to each other), knows its size and can be iterated?
JASS:
    local IntegerArray2D d2 = IntegerArray2D.create()
    set d2[1][98] = 4
    set d2[4][2] = 0

    // whats the size of d2 for each of its dimensions, how do you iterate all the elements of d2?

The destroy method seems useless:
JASS:
    local IntegerArray2D d2 = IntegerArray2D.create()

    set d2[1][1] = 11
    set d2[1][2] = 12
    set d2[2][1] = 21
    set d2[2][2] = 22

    call d2.destroy()
    // only the d2 table is destroyed
    // the intermediate tables: d2[1], and d2[2] are not destroyed
    //
    // we have to manually call d2[1].destroy() and d2[2].destroy() and then d2.destroy()
    // if we want to properly destroy d2

The performance would not be good in my opinion:
JASS:
    local IntegerArray3D d3 = IntegerArray3D.create()

    set d3[1][2][3] = 4
    // the above looks nice but compiles to a lot of function calls and hashtable lookups even in release mode:

//! novjass
    call SaveInteger(Table___ht, ((((((s__HashTable__getindex((((s__HashTable__getindex(((d3)),(1))))),(2)))))))), ((3)), ( ( 4))) // INLINED!!

    function s__HashTable__getindex takes integer this,integer index returns integer
        local integer t= (LoadInteger(Table__ht, ((this)), (index))) // INLINED!!
        if t == 0 then
            set t=s__Table_create()
            call SaveInteger(Table__ht, ((this)), (index), ( t)) //whoops! Forgot that line. I'm out of practice! // INLINED!!
        endif
        return t
    endfunction

    function s__Table_create takes nothing returns integer
        local integer this= (LoadInteger(Table__ht, ((Table__listK)), (0))) // INLINED!!

        if this == 0 then
            set this=Table__more + 1
            set Table__more=this
        else
            call SaveInteger(Table__ht, ((Table__listK)), (0), ( (LoadInteger(Table__ht, ((Table__listK)), (this))))) // INLINED!!
            call RemoveSavedInteger(Table__ht, ((Table__listK)), (this)) //Clear hashed memory // INLINED!!
        endif

        return this
    endfunction
//! endnovjass

At least its type safe, I guess...
 

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
Can we reserve the name array for a collection that stores its elements contiguously (next to each other), knows its size and can be iterated?
Yes it would be nice I guess, but the downside is that it would make the operations even slower.

JASS:
    call d2.destroy()
    // only the d2 table is destroyed
    // the intermediate tables: d2[1], and d2[2] are not destroyed
    //
    // we have to manually call d2[1].destroy() and d2[2].destroy() and then d2.destroy()
    // if we want to properly destroy d2
Yes. For now, the user have the responsibility of removing the parent nodes themselves. Perhaps in the future, if I can implement what you suggested above in your first point, flushing all nodes would be easy. But as I also stated above, I will have to weight the pros & cons first before implementing it.

The performance would not be good in my opinion:
Based on my experience(albeit limited), some sacrifice on performance is a mandatory consequence when you want to implement n-dimensional arrays that would give you convenience in return.
 

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
Could say it looks just like to Dimensional Array.
I haven't studied that library in-depth but from what I see, it's only limited to 4D (correct me if I'm wrong here) and no, it's not same as this. This can handle 1-5 dimensions and this simulates the nature of the default jass arrays (having type-specific storage and its API).

Why write a multidimensional system "inside" another that in my opinion does not take advantage of the potential that it has (Table)?...
I'm not sure if you've read the code but if you did, you should've noticed that all of this are actually wrappers to the Table & HashTable struct.
This for example set u[1][2][3][4][5] = GetTriggerUnit() is same as set HashTable(HashTable(HashTable(u[1])[2])[3])[4].unit[5] = GetTriggerUnit()
so I don't actually get when you said this does not take advantage of the potential of the Table library.
 
Last edited:
can you run:

JASS:
scope SSS initializer Test
    function Test takes nothing returns nothing
        local Table t = Table.create()
        local UnitArray u = UnitArray.create()
      
        set t.unit['AAAA'] = CreateUnit(Player(0), 'hpea', 0, 0, 0)
        call BJDebugMsg(GetUnitName(t.unit['AAAA']))
        call t.unit.remove('AAAA')
        call BJDebugMsg(GetUnitName(t.unit['AAAA']))
      
        call BJDebugMsg("===========")
      
        set u['AAAA'] = CreateUnit(Player(0), 'hpea', 0, 0, 0)
        call BJDebugMsg(GetUnitName(u['AAAA']))
        call u.remove('AAAA')
        call BJDebugMsg(GetUnitName(u['AAAA']))
    endfunction
endscope

---

how to remove for example an entry u2['hfoo']['hpea']?

---

so the type of handle is specified at declaration, this makes your syntax a bit less, because one does not use "unit" keyword or so,
but on the other side it does limit usage of an instance to only one type, which is.. maybe not so good.
 
Last edited:

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
how to remove for example an entry u2['hfoo']['hpea']?
Oh I see the mistake now. It's because I used HashTable(this).remove(index) instead of Table(this).remove(index) on the 1D array.

so the type of handle is specified at declaration, this makes your syntax a bit less, because one does not use "unit" keyword or so,
but on the other side it does limit usage of an instance to only one type, which is.. maybe not so good.
Yeah, I guess one nice thing about hashtables is that you can have generic storage so I'm thinking about making another struct named Array for generic storage. Perhaps on the next update.

"IntegerArray4D" -> "Integer4D".
Hmm can be
 
Level 11
Joined
Dec 19, 2012
Messages
411
Since you mentioned my currently developing system, there is 1 thing I would like to ask about :

How does it perform? For Bribe's Table, I could do something like this :
JASS:
    local HashTable4D ht4d = HashTable.create()
    local Table tb = ht4d[1][2][3]

    set tb.unit[0] = someUnit
    set tb.real[1] = someReal
    set tb.integer[2] = someInteger

    //instead of
    local HashTable4D ht4d = HashTable.create()

    set ht4d[1][2][3].unit[0] = someUnit
    set ht4d[1][2][3].real[1] = someReal
    set ht4d[1][2][3].integer[2] = someInteger
Which this could drastically improve the performance as no repeated calculation is involved. Could your system provide such facility to avoid repeated calculation?
 
Level 11
Joined
Dec 19, 2012
Messages
411
Well yea, I guess so since I'm doing that manually.

Even though your system does contains several features that I prefer more compare to the HashTable that Bribe's created (better syntax, compile-time checking), but so far the development stage of my system is a bit far away to implement yours (in the other word I'm lazy to change all of the Tables/HashTables).
 
.has feature is currently not adapted from hashtables.

I still somehow think it's better to name "flush" over "clear" due to standards, hm..

I tried this from API docu and got syntax error:

JASS:
scope SSS initializer Test
    function Test takes nothing returns nothing
        local Array4D a4 = Array.create()
        set a4[1][2][3][4].unit = CreateUnit(Player(0), 'hpea', 0, 0, 0)
        call a4[1][2][3].removeHandle(4) // here
    endfunction
endscope

The type-specified arrays work correctly now, nice.
 

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
Updated to 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


Added the has() functionality, renamed clear() to flush() and fixed the careless mistake above.
Also added instruction for users how to increase the max number of dimensions in case they want to :)
 

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
UPDATED to 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
- Improved debug messages
- Fixed some errors

Btw, when removing a saved data from a specific type array, the syntax is .removei(index) not .removeHandle/Int/Str/Etc. unlike the generic type storage. In your example above, it should be call a[1][2][3][4].remove(5). The bug regarding has() not returning a boolean is fixed.
 

AGD

AGD

Level 14
Joined
Mar 29, 2016
Messages
678
Because in generic type, you need to specify what is the type of the data to be removed like RemoveSavedHandle() unlike in type specific storage where there is no need to specify since the method remove() already knows the type to remove i.e., the type of it's storage.
I thought of adding a remove method for generic type removal with similar syntax to the specific type removal but it would be something like
JASS:
        method remove takes nothing returns nothing
            local integer i = Table(this)[index]
            call Table(this).integer.remove(i)
            call Table(this).real.remove(i)
            call Table(this).boolean.remove(i)
            call Table(this).string.remove(i)
            call Table(this).handle.remove(i)
        endmethod
The user doesn't anymore have to specify the type but it would be a bit slower. What do you think?
 
Top