Making a hash map struct...

krainert

Member
Reaction score
10
Greetings,

I'm currently working on a hash map struct, but I'm not sure whether I should base it on a hashmap or two arrays of size 8191. I wish to be able to efficiently iterate through all mappings as I intend to provide a for-each functionality, but I'm not sure which is more efficient: always taking up space for two arrays with capacity 8191 or always iterating through all possible key values. Both seem pretty bad. Also: Is it really true that array capacities cannot be dynamically defined - must they really be set when the array is declared? I suppose a compromise would be having both a hashtable and one array of size 8191 where the array stores every key in the hashtable with an assigned value. This would cut the overhead in half while rendering the tedious iteration through all possible keys superfluous, but it sure as hell isn't pretty.

Thanks.
 

Accname

2D-Graphics enthusiast
Reaction score
1,463
Of course you have to initialize an array with a set size. Do you know what arrays are? There is no point for having them if they got dynamic size.
 

krainert

Member
Reaction score
10
Of course you have to initialize an array with a set size. Do you know what arrays are? There is no point for having them if they got dynamic size.
*Cough* Java *Cough* C# *Cough* JavaScript even (the capacities of arrays in the latter are actually fully dynamic). Not every language has to be C++ :)
Can anyone confirm that there is no possible way to declare an array variable in vJass without simultaneously specifying its capacity?
 

Accname

2D-Graphics enthusiast
Reaction score
1,463
*Cough* Java *Cough* C# *Cough* JavaScript even (the capacities of arrays in the latter are actually fully dynamic). Not every language has to be C++ :)
Can anyone confirm that there is no possible way to declare an array variable in vJass without simultaneously specifying its capacity?
Then it isnt an array.
An array needs to have a set size in order to be fully efficient.
Containers with indices and dynamic size are vectors, but even those work the same as arrays but with a buffer. That means they have a larger size set from the beginning and when this size is reached they will copy their entire content to a new position with an even larger capacity. This process is alot of work for the computer which should be avoided whenever possible.

This is still scripting a computer programm and not magic in wonderland.

Getting bit off-topic though.
 

Solmyr

Ultra Cool Member
Reaction score
30
Then it isnt an array.
An array needs to have a set size in order to be fully efficient.
Containers with indices and dynamic size are vectors, but even those work the same as arrays but with a buffer. That means they have a larger size set from the beginning and when this size is reached they will copy their entire content to a new position with an even larger capacity. This process is alot of work for the computer which should be avoided whenever possible.

You have obviously never heard of dynamic arrays.

Can anyone confirm that there is no possible way to declare an array variable in vJass without simultaneously specifying its capacity?

Nope, unfortunately, there isn't.
 

Accname

2D-Graphics enthusiast
Reaction score
1,463
You have obviously never heard of dynamic arrays.
Did you read that article yourself? That was what i told him.
Those "dynamic arrays" or vectors have a fixed capacity as well, and when this capacity is reached they will just copy themself into an even bigger space. But this process is very time consuming.
 

krainert

Member
Reaction score
10
Actually, I'm not looking for dynamic array functionality; what I need is any static/dynamic sized array the capacity of which is not determined upon declaration albeit possibly specified upon initialization.

Another question: hashmaps are dynamically sized, am I right? Is there a significant overhead to their use compared to that of arrays? I'm considering doing the following:
Have one hashtable mapping from integer keys to integer values.
Have one hashtable mapping from growing indices to keys and values in the first hashtable.
Continuously maintain both arrays, keeping them in sync.
Use the first hashtable for key-to-value referencing.
Use the second hashtable during iterations over entry pairs.
Perhaps introduce a third hashtable allowing efficient value-to-key referencing? (EDIT: Whoa, no, this should be handled in a separate class using two simple hash maps if at all)

I suppose it all depends on the overhead of hashtables.
 

Accname

2D-Graphics enthusiast
Reaction score
1,463
You can do all that in a single hashtable, theoretically you never need a second one.

But maybe you might try to tell us what exactly you want to do in the first place. Maybe some other user already has a system for whatever you are trying to accomplish.
 

krainert

Member
Reaction score
10
Unrelated question: Structs are referenced as integers. Which values can these take when JassHelper does its thing? Are struct references always positive integers? Can they be 0? Negative?
EDIT: I've asked this here: http://www.thehelper.net/forums/showthread.php/168540-Can-vJass-structs-have-integer-value-0

EDIT:
You can do all that in a single hashtable, theoretically you never need a second one.
I assume hashtable can do lookups faster than I can if I have to iterate through n elements in the process, amrite? In that case having supporting hashtables would improve efficiency.

But maybe you might try to tell us what exactly you want to do in the first place. Maybe some other user already has a system for whatever you are trying to accomplish.
I'm making a suite of integer-only hash tables.
To begin with I'm making a simple one - basically a hashtable which one column and a much, much simpler API (has, load, save, remove, clear, and size).
Then I'll add an interable version, that is a hash table that supports efficient for-each loops using closures. This'll contribute just a few methods (forEach, efficient equals, efficient clone) to the API of the simple hash table.
Then I'll add a two-way version. This'll contribute value-based duplicates for pretty much every method (examples: value-based has, load, and save) in the simple hash table's API.
Finally I'll add an iterable two-way version - just for the heck of it. As far as I can see, the most efficient way to do this one is with three hashtables: key-value, value-key, and index-key,value. This one will extend the first two-way hash table as well as the first iterable one and add two methods (forEachKey and forEachEntry) on top of that.
 

krainert

Member
Reaction score
10
Here we go:

JASS:
library HashMaps

globals
    private constant integer MAX_MAPPINGS   = 8191
    private constant integer DEF_RETURN_VAL = -1
endglobals

function interface IterationHandler takes integer value returns boolean
function interface ReverseIterationHandler takes integer key returns boolean
function interface TwoWayIterationHandler takes integer key, integer value returns boolean

interface Map
    public method has takes integer key returns boolean
    public method load takes integer key returns integer
    public method save takes integer key, integer value returns boolean
    public method remove takes integer key returns boolean
    public method clear takes nothing returns boolean
    public method size takes nothing returns integer
endinterface

interface IterableMap
    //Map
    public method has takes integer key returns boolean
    public method load takes integer key returns integer
    public method save takes integer key, integer value returns boolean
    public method remove takes integer key returns boolean
    public method clear takes nothing returns boolean
    public method size takes nothing returns integer
    //IterableMap
    public method forEach takes IterationHandler handler returns boolean
endinterface

interface TwoWayMap
    //Map
    public method has takes integer key returns boolean
    public method load takes integer key returns integer
    public method save takes integer key, integer value returns boolean
    public method remove takes integer key returns boolean
    public method clear takes nothing returns boolean
    public method size takes nothing returns integer
    //TwoWayMap
    public method hasValue takes integer value returns boolean
    public method loadValue takes integer value returns integer
    public method saveValue takes integer value, integer value returns boolean
    public method removeValue takes integer value returns boolean
    public method keyOf takes integer value returns integer
endinterface

interface IterableTwoWayMap
    //Map
    public method has takes integer key returns boolean
    public method load takes integer key returns integer
    public method save takes integer key, integer value returns boolean
    public method remove takes integer key returns boolean
    public method clear takes nothing returns boolean
    public method size takes nothing returns integer
    //IterableMap
    public method forEach takes IterationHandler handler returns boolean
    //TwoWayMap
    public method hasValue takes integer value returns boolean
    public method loadValue takes integer value returns integer
    public method saveValue takes integer value, integer value returns boolean
    public method removeValue takes integer value returns boolean
    public method keyOf takes integer value returns integer
    //IterableTwoWayMap
    public method forEachKey takes ReverseIterationHandler handler returns boolean
    public method forEachEntry takes TwoWayIterationHandler handler returns boolean
endinterface

struct HashMap extends Map
    private hashtable map   = InitHashtable()
    private integer   count = 0
    
    method operator [] takes integer key returns integer
        return .load(key)
    endmethod
    
    method operator []= takes integer key, integer value returns nothing
        call .save(key, value)
    endmethod
    
    method operator < takes thistype other returns boolean
        return .count < other.count
    endmethod
    
    public method has takes integer key returns boolean
        return HaveSavedInteger(.map, key, 0)
    endmethod
    
    public method load takes integer key returns integer
        if .has(key) then
            return LoadInteger(.map, key, 0)
        else
            return DEF_RETURN_VAL
        endif
    endmethod
    
    public method save takes integer key, integer value returns boolean
        if .has(key) then
            call SaveInteger(.map, key, 0, value)
            return true
        else
            if .size != MAX_MAPPINGS then
                call SaveInteger(.map, key, 0, value)
                set .count = .count + 1
            else
                call BJDebugMsg("HashMap error: attept to create mapping from " + I2S(key) + " to " + I2S(value) + " thereby exceeding the mapping limit being " + I2S(MAX_MAPPINGS))
            endif
            return false
        endif
    endmethod
    
    public method remove takes integer key returns boolean
        if .has(key) then
            call FlushChildHashtable(.map, key)
            set .count = .count - 1
            return true
        else
            return false
        endif
    endmethod
    
    public method clear takes nothing returns boolean
        if .count != 0 then
            call FlushParentHashtable(.map)
            set .map = InitHashtable()
            set .count = 0
            return true
        else
            return false
        endif
    endmethod
    
    public method size takes nothing returns integer
        return .count
    endmethod
endstruct

struct IndexedMap extends IterableMap
    private Map       map   = HashMap.create()
    private hashtable index = InitHashtable()
    
    method operator [] takes integer key returns integer
        return .load(key)
    endmethod
    
    method operator []= takes integer key, integer value returns nothing
        call .save(key, value)
    endmethod
    
    method operator < takes thistype other returns boolean
        return .map.size() < other.map.size()
    endmethod
    
    public method has takes integer key returns boolean
        return .map.has(key)
    endmethod
    
    public method load takes integer key returns integer
        return map.load(key)
    endmethod
    
    public method save takes integer key, integer value returns boolean
        if map.save(key, value) then
            call SaveInteger(.index, .nextIndex(), 0, key)
            return true
        else
            return false
        endif
    endmethod
    
    public method remove takes integer key returns boolean
        if map.has(key) then
            call map.remove(key)
            call FlushChildHashtable(.index, .keyIndex(key))
            return true
        else
            return false
        endif
    endmethod
    
    public method clear takes nothing returns boolean
        if map.size() != 0 then
            call map.clear()
            call FlushParentHashtable(.index)
            set .index = InitHashtable()
            return true
        else
            return false
        endif
    endmethod
    
    public method forEach takes IterationHandler handler returns boolean
        local integer size = map.size()
        local integer value
        local integer i = 0
        loop
            exitwhen i == size
            if HaveSavedInteger(.index, i, 0) then
                set value = map.load(LoadInteger(.index, i, 0))
                if value != DEF_RETURN_VAL then
                    if handler.evaluate(value) then
                        return true
                    endif
                endif
            endif
            set i = i + 1
        endloop
        return false
    endmethod
    
    public method size takes nothing returns integer
        return .map.size()
    endmethod
    
    private method nextIndex takes nothing returns integer
        local integer size = map.size()
        local integer i = 0
        loop
            exitwhen i == size
            if not(HaveSavedInteger(.index, i, 0)) then
                return i
            endif
            set i = i + 1
        endloop
        return size
    endmethod
    
    private method keyIndex takes integer key returns integer
        local integer current
        local integer i = 0
        loop
            set current = LoadInteger(.index, i, 0)
            if current == key then
                return i
            endif
            set i = i + 1
        endloop
        return 0
    endmethod
endstruct

endlibrary


An issue, though: I get four of these errors: "Symbol value multiply defined". Any clues?
Note: No structs currently implement TwoWayMap or IterableTwoWayMap.

Unrelated: Is there really no way to make one interface extend another?
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Ghan Ghan:
    Howdy
  • Ghan Ghan:
    Still lurking
    +3
  • The Helper The Helper:
    I am great and it is fantastic to see you my friend!
    +1
  • The Helper The Helper:
    If you are new to the site please check out the Recipe and Food Forum https://www.thehelper.net/forums/recipes-and-food.220/
  • Monovertex Monovertex:
    How come you're so into recipes lately? Never saw this much interest in this topic in the old days of TH.net
  • Monovertex Monovertex:
    Hmm, how do I change my signature?
  • tom_mai78101 tom_mai78101:
    Signatures can be edit in your account profile. As for the old stuffs, I'm thinking it's because Blizzard is now under Microsoft, and because of Microsoft Xbox going the way it is, it's dreadful.
  • The Helper The Helper:
    I am not big on the recipes I am just promoting them - I use the site as a practice place promoting stuff
    +2
  • Monovertex Monovertex:
    @tom_mai78101 I must be blind. If I go on my profile I don't see any area to edit the signature; If I go to account details (settings) I don't see any signature area either.
  • The Helper The Helper:
    You can get there if you click the bell icon (alerts) and choose preferences from the bottom, signature will be in the menu on the left there https://www.thehelper.net/account/preferences
  • The Helper The Helper:
    I think I need to split the Sci/Tech news forum into 2 one for Science and one for Tech but I am hating all the moving of posts I would have to do
  • The Helper The Helper:
    What is up Old Mountain Shadow?
  • The Helper The Helper:
    Happy Thursday!
    +1
  • Varine Varine:
    Crazy how much 3d printing has come in the last few years. Sad that it's not as easily modifiable though
  • Varine Varine:
    I bought an Ender 3 during the pandemic and tinkered with it all the time. Just bought a Sovol, not as easy. I'm trying to make it use a different nozzle because I have a fuck ton of Volcanos, and they use what is basically a modified volcano that is just a smidge longer, and almost every part on this thing needs to be redone to make it work
  • Varine Varine:
    Luckily I have a 3d printer for that, I guess. But it's ridiculous. The regular volcanos are 21mm, these Sovol versions are about 23.5mm
  • Varine Varine:
    So, 2.5mm longer. But the thing that measures the bed is about 1.5mm above the nozzle, so if I swap it with a volcano then I'm 1mm behind it. So cool, new bracket to swap that, but THEN the fan shroud to direct air at the part is ALSO going to be .5mm to low, and so I need to redo that, but by doing that it is a little bit off where it should be blowing and it's throwing it at the heating block instead of the part, and fuck man
  • Varine Varine:
    I didn't realize they designed this entire thing to NOT be modded. I would have just got a fucking Bambu if I knew that, the whole point was I could fuck with this. And no one else makes shit for Sovol so I have to go through them, and they have... interesting pricing models. So I have a new extruder altogether that I'm taking apart and going to just design a whole new one to use my nozzles. Dumb design.
  • Varine Varine:
    Can't just buy a new heatblock, you need to get a whole hotend - so block, heater cartridge, thermistor, heatbreak, and nozzle. And they put this fucking paste in there so I can't take the thermistor or cartridge out with any ease, that's 30 dollars. Or you can get the whole extrudor with the direct driver AND that heatblock for like 50, but you still can't get any of it to come apart
  • Varine Varine:
    Partsbuilt has individual parts I found but they're expensive. I think I can get bits swapped around and make this work with generic shit though
  • Ghan Ghan:
    Heard Houston got hit pretty bad by storms last night. Hope all is well with TH.
  • The Helper The Helper:
    Power back on finally - all is good here no damage
    +2
  • V-SNES V-SNES:
    Happy Friday!
    +1

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top