• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Solved] How to effectively loop through hashtables?

Level 6
Joined
Sep 10, 2022
Messages
91
Hello everyone, I am continuing with hashtables and I have some sort of typical question.
1. For example, imagine I have a hashtable in which I store a timer for the unit - unit_timer - as the value to be stored, then the Key is a unique Unit Handle of the Owner of the unit.
  • Hashtable - Save conf_timer as (Key (Picked unit)) of (Player number of (Owner of conf_target)) in conf_hash_table
If I understand correctly, my hashtable looks like this:

Hashtable cells:

[UnitHandle][Owner of Unit] = unit_timer
[103204][1] = 15
....
[103265][9] = 137

In another trigger, I am planning to loop over the hash table and change each count_timer until It won't be zero. So the question is how I can perform it effectively, cause the count_timer value can be an arbitrary number (from 0 to infinity). The same situation is with the unique unit Handle in this hashtable and it feels like For loop from integer A to B is not the best option...



So maybe the answer is to create 2 loops in different triggers, one from the start of the hashtable, and one from the end of the hashtable. I mean two loops at the same time are working until both reach the middle..?

2. I have one more question. I know that data in the hashtable also leaks, for example, we store position 1 and then replace it with position 2. And heard that we can prevent a leak by assigning the data in the hashtable cell to a variable, then we remove* the variable with DestroyLocation(), position 1 really disappears, and the data in the hashtable will also be cleaned by the time we assign in the cell new position. I am not sure... but is that true?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,583
I would look at the Hashtable like this:
[Parent Key][Child Key] = Value

So looking at this line here:
  • Hashtable - Save conf_timer as (Key (Picked unit)) of (Player number of (Owner of conf_target)) in conf_hash_table
And assuming that the (Owner of conf_target) is Red (1) and the Unit's handle is 123 and the Timer's handle is 456 then:

[1][123] = 456

In other words:

[Player #][Unit] = Timer

Also, since we're dealing with Units then a Unit Group variable comes to mind since it's the most convenient way to store and enumerate over multiple units. Here's how we could use one to manage all our Unit's Timers for a given Player:
  • Actions
    • Set Variable PN = (Player number of ...)
    • Unit Group - Pick every unit in MyUnitGroup[PN] and do (Actions)
      • Loop - Actions
        • Countdown Timer - Pause (Load (Key (Picked unit).) of PN in conf_hash_table)
This will Pause the timer for each unit inside of the unit group - assuming that you've stored a timer at those parent/child keys. By using a Unit Group array variable we're able to maintain the logic of storing unique data to each Player.

Regarding #2, a Hashtable is just like an Array in that it's storing a reference to something. If that reference is lost for good then you've created a memory leak (with the exception of integers, reals, booleans, and strings). Try not to think about it as "Hashtables also leak" and try to understand what a memory leak is in the first place.

But yes, what you said about cleaning these Point (location) memory leaks is true. Here's how you can do it:
  • Actions
    • Set Variable LoadPoint = (Load 0 of 1 in MyHashtable)
    • Custom script: call RemoveLocation( udg_LoadPoint )
If you want to avoid the variable usage then you can just call the Jass load function directly:
  • Actions
    • Custom script: call RemoveLocation( LoadLocationHandle(udg_MyHashtable, 0, 1) )
These both do the same exact thing.
 
Last edited:
Level 6
Joined
Sep 10, 2022
Messages
91
Ahh, I think I'm starting to understand. I thought that the first index when saving (in my case) is the handle of the unit, and the second index is the player. So the first place (parent key) is the number of players and the second (child key) is the unit handle.

Just to clarify, MyUnitGroup is a unit group array?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,583
  • Hashtable - Save conf_timer as (Key (Picked unit)) of (Player number of (Owner of conf_target)) in conf_hash_table
(Player number of (Owner of conf_target)) = Parent Key
(Key (Picked unit)) = Child Key
conf_timer = Value

You're saving the timer at child key X of parent key Y. Think about it in the sense that the Player is at the top of the hierarchy, then the Units are under it, and then the Timers are under the Units. The Player is the parent of the units and the units are essentially the parents of their timers.

The wording confuses everyone at first, it's a weird choice by blizzard.


Yes, MyUnitGroup is a Unit Group array.

Note that Unit Group arrays need to be initialized by setting their Size inside of the variable editor (don't do it outside of the editor - it'll bug).
So if your map uses Players 1 -> 24 then you would most likely want to set the Unit Group's array Size to 24. This will automatically create and store a Unit Group at each of those indexes [1] -> [24] at runtime. I would've needed to do this to make the Unit Group array variable in my previous post function properly.

Alternatively, you can manually create the Unit Groups yourself using Custom script:
  • Custom script: set udg_MyUnitGroup[4] = CreateGroup()
^ That would create a Unit Group at index [4]. I often use the above method with custom spells. I attach a Unit Group to a Unit by using it's custom value (Unit Indexing). That or I attach the Unit Group to an Index like in Dynamic Indexing.

Timer and Player Group arrays also need to be initialized the same way.
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
The solution would be to create a table of timer instances which you can efficiently iterate though as all entries are sequential or linked sequentially. Such a table would usually be made out of a set of arrays in JASS.

The hashtable would then map the unit to an entry in that table. If the unit is to map to multiple entries, the entries could reference each other and form a linked list.
 
Level 6
Joined
Sep 10, 2022
Messages
91
The solution would be to create a table of timer instances which you can efficiently iterate though as all entries are sequential or linked sequentially. Such a table would usually be made out of a set of arrays in JASS.

The hashtable would then map the unit to an entry in that table. If the unit is to map to multiple entries, the entries could reference each other and form a linked list.
Did it with arrays)
 
Top