SCRIPTING:

Nina Brosh
MAKING AN HASH TABLE OUT OF AN ARRAY
How to make an hash table (familiar name for Associative Array) out of an array to help you dramatically speed up looping processes or avoid them entirely.
A good way to use array indexes for more than just looping, too.
December 2001
{ @ }

The model above is Nina Brosh
LOADS OF NINA BROSH ON THE NET


WHAT'S AN HASH AND WHAT THIS SCRIPT ACHIEVES
What is an hash table, some comments on Jon Bentley and a programming secret, and the purpose of this script

Stephanie Seymour
What is a hash table? The term has been borrowed, basically, by a language known as Perl. A hash table is an array whose indexes are literals instead of numbers.
Normally an array entry has a shape which might look like:
myArray[12]="blah blah"
whereas the 12 is the index, that is: it indicates the level, the height in within the array where the object (in this case a string saying "blah blah" is located).
What you must be aware of is that nothing -absolutely nothing- dictates that the instance type in between the square brackets must be a number and nothing but a number. It can be a literal as well: instance:
myArray["john's words"]="blah blah"
That is perfectly legitimate. You could get the contents held by that entry in an alert box by referencing it like, for instance:
alert(myArray["john's words"])
Now, you may have heard of a guy named Jon Bentley and/or of a book of his titled Programming Pearls. It's a book with a reputation, although I found it a bit confusing. None the less you can find there many insights, and among them I found one that, although has not been explicit outspokenly, it can however be fairly deduced. You can consider it a programming secret... This is: whenever you face a programming problem, always consider:
  1. making an array is the data structure that might probably solve your problem
Van Gogh
  1. do not be so dumb to believe that the only purpose an index [what is in between the square brackets in an array entry, that is] can have only a limitative scope such as furnishing the array with a number in order to loop it through.
Bentley provides an enlightening example when he tells the story of the programmer who had to index phone numbers (ten millions...!) and assign to each entry the phone number and a flag to reveal whether it was already assigned or available for a new user: his problem was that looping through a ten millions entries array took too long (would you guess?).
Bentley rightly points out that the right solution was to use the index in between the brackets to store and reveal some data instead of being there merely passively to hand out a handle to loop the array with: so the solution was: build an array of ten millions entries, and then consider each index a... phone number! Then assign to the entry the value zero if unassigned or 1 if already taken. In such a fashion whenever the programmer had to check whether a phone number, for instance 5556012, was taken, he had not to loop the whole array at all but simply to grab instantly a precise index: phoneNumbers[5556012] and verify whether it held number 1 or zero. In such a way no looping at all was necessary.
The only required thing was to build up the array on start up (booting) of the system.

As you can see, this is a way that, by using the index of an array like an outlet capable of revealing data and carry more meaning than just being used to be looped, you can certainly solve many a problem.
I'm going, here, to provide you exactly with a tool that might help you: we will make out of an array an hash table, so that each index will be the contents of the array which is going to be hashed, and whose each value will be an index: that is, if you have an array like:
dumbArray=new Array("john","mary","elgar","anthony")
we're to build out of it another array whose each entry will carry the string as an index, and the index position where the string was located in the previous array as its contents, so such an array on the previous example might look like:
hashedArray=new Array()
hashedArray["john"]=0
hashedArray["mary"]=1
hashedArray["elgar"]=2
hashedArray["anthony"]=3


Can you guess what this would be for? Very simple: if you can afford to build such a table (that is: you have a very long array, and enough memory to keep a copy of it), whenever you have to locate where, for instance, the word "anthony" is, you do not have to loop it but you just have to ask for:
dumbArray[hashedArray["anthony"]]
That is: you nest in between the brackets of the array you want to find the value from, a reference to its hashed copy carrying as an index the literal which you're looking for: such a reference would bring back a number (the value held by the hashedArray entry) which will be used to locate at once and with no loop the location of the desired object in the original dumbArray.
You can imagine: all those situations where you where to loop an array dozens of times, can now be solved looping it just once and for all, using the hashed reference to extract all the positions of the desired values in the original dumbArray!

Additionally, we will shape our script so that the hashed array will carry as entries an array in order to be able to collect for each literal index all the positions in which a certain string might be found in the original dumbArray to take into account also those occurrences when a literal might appear more than once in an array. of course, we will also store values which are not literals, and we can upon request build a separated hash array out of the original hash array, in the former of which we could collect references to all those entries in the original so called dumbArray that were either undefined or carried false or null values.
Cool, isn't it?
Here's your code:


function hasher(array,outputType, noPrepend){
var hash=new Array(0)
var nullHash=new Array(0);
if(!outputType){
 for(var H=0;H<array.length;H++){
  var H2=(!noPrepend)?"#"+array[H]:array[H];
  if(hash[H2]){
   hash[H2].length++
   hash[H2][hash[H2].length-1]=H
  continue;}
 hash[H2]=new Array();
 hash[H2].length++
 hash[H2][hash[H2].length-1]=H
 }
}
else if (outputType){
  for(var H=0;H<array.length;H++){
  var H2=(!noPrepend)?"#"+array[H]:array[H];
   if(!array[H]||array[H]==null){
    if(nullHash[H2]){
     nullHash[H2].length++
     nullHash[H2][nullHash[H2].length-1]=H
    continue}
    nullHash[H2]=new Array();
    nullHash[H2].length++
    nullHash[H2][nullHash[H2].length-1]=H
   continue;}
  if(hash[H2]){
   hash[H2].length++
   hash[H2][hash[H2].length-1]=H
  continue;}
 hash[H2]=new Array();
 hash[H2].length++
 hash[H2][hash[H2].length-1]=H
 }
}
return (!outputType)?hash:
(outputType==1)?nullHash:
new Array(hash,nullHash)
/*keep this comment to reuse freely
http://www.unitedscripters.com */}

Renoir picture


IMPLEMENTATION OF THE FUNCTIONS
How it works and one bug

You pass to the function two arguments:
  1. The array which must be the template to build a relative hash
  2. The second argument can be:
    1. none: the function returns an hashed array. In such fashion a code like:
      var myHash=hasher(array)
      would make of myHash an array with literal indexes
    2. you pass number 1 as second argument: the function returns an hash which contains only those entries which were undefined or null, using like indexes things like: ["undefined"] or ["false"] or ["null"] or maybe ["0"] depending upon the undefined type encountered [as Strings, so all in between quotes. Actually, as you are to see soon, all these indexes would be returned as well as the indexes in the instances below, with pre pended a # sign: so, more accurately: "#null". You'll see later on why]. In such fashion a code like:
      var myHash=hasher(array,1)
      would make of myHash an array with literal indexes
    3. if you pass number 2 like second argument, the function will return an array whose unique 2 entries will be the first the hash array of defined values, and the second the hash array of UNdefined values. In such fashion a code like:
      var myHash=hasher(array,2)
      would make of myHash an array of two entries each of which is an array of its own with literal indexes. Thus in such case to grab an entry fro the defined values you'd use a double index:
      myHash[0]["myString here"] for the defined values hash, or:
      myHash[1][null] for the UNdefined values hash. Remember that in this latest case, myHash[1] would carry like literals only those expressions which may be UNdefined keywords in the language: such as null, false, 0 etc... That is, you should not search in it for other text than keywords flagging false or undefined or null values.
  3. Once you have built your hash, to find a reference to the original array, you could make:
    //attention: WRONG
    array[myHash["searched value"]]

    But this snippet would cause a problem, since each entry of the just produced myHash is an array!. We devised this in order to allow the case we find multiple instances of a same string. But, of course, also if such multiple instances have not been found but only one single instance has been found, each entry in myHash would still be responding, none the less, to the array settings, so either you add a zero index such as:
    //attention: CORRECT
    array[myHash["searched value"][0]]

    The example above can be used only if you're positive there may be only one entry.
    If you don't know for sure, then you just make a tiny loop if you want to get all the positions where the given value is located in the original array, such as:
    for(var i=0;i<myHash["searched value"].length;i++){
    //... more stuff here, probably
    array[myHash["searched value"][i]]
    }

    Whatever the method, it is useless to point out that in scripts where you have to make repetitive locations of values from within a long original array, a hashed version of it is to save you tons of looping time!
  4. Warning on a bug: for reasons that defy reasons, a well affirmed capability of javaScript here fails. Like even that wonderful javaScript programmer that is Danny Goodman says, to turn a number into a string you just have to add to it an empty string, such as:
    var myNum=283
    myNumToString=""+myNum

    Well, I obviously took into consideration the chance the original array might hold numbers like values (such as array[3]=458000) so to convert them into strings I was originally using the method above. Well, to my extreme puzzlement, it would not work: hashing an original array entry that might happen being a number, would not turn the number into its literal equivalent by preponing to it an empty string: the dire consequence would have been that myHash[failedAttempt to Turn a numberToString here] would get an increased length to match the number!
    On the other hand, I found that if I did included something in the expression
    myNumToString="X"+myNum
    then everything worked smoothly.
    Of course I could have added just a white space, but in such way you might have been confused by overlooking it and thinking the two quotes encapsulated nothing. So I decided to add a # symbol, such as:
    var myNum=283
    myNumToString="#"+myNum

    It carries a consequence: when you look for a string in the hashed table you must thus always pre-pone the # sign, such as:
    array[myHash["#searched value"][0]]
    without white spaces either before or after the # sign.
     
    If you want to skip this pre pending of the # sign for you don't bother whether your output array may get lengthy, you can pass a third parameter as number 1 (and the outputType parameter consequently and necessarily either as zero or 1 or 2, but in this case you have to specify it even if it is zero). In any case keep in mind that such a move is not going to guarantee to you any difference between, say, an authentic false value held as a Boolean by an entry, and a String just carrying the written text: "false", for in both cases the indexes are dealt as literals.
    This is not a shortcoming of the script, but this is the very same way a scripting compiler works: so either with the # sign or without, you're always to address your indexes as literals; and where you to address them with, say, a boolean index, yet it won't discriminate between an authentic Boolean held by the entry or a stringed entry just holding a text which by chance matches a boolean keyword.
    If you never noticed that addressing an array index 2 as array[2] or array["2"] makes no difference (both calls do conjure the same object!!), you have not to regret: you're not alone for I thought there should have been a difference between Strings and Numbers as well, and with us other far more remarkable programmers. But for more on this, see the documentation below.
    I do understand and I am the first to acknowledge this # story to avoid relinquishing arrays of one millions of entries if one value is a long number, is quite disturbing and annoying a fact; notwithstanding, believe me that that was really unexpected a behaviour by javaScript (on all browsers) which finds no confirmation in whatever book.
    I am assuming that the javaScript interpreter attempts to guess what the programmer means when assigning a number to an array index, and if it is not adamantly straightforward that the number is not meant to be dealt with as a number but we want to deal with it like a string, the interpreter forces it to be still considered like a number when inserting it into an array as an index, no matter if you attempted to add an empty string to convert its type from numeric to literal.
    Making it unmistakable (that is: adding not an empty string but a string with a letter in it or a symbol) would save the day and force the interpreter to do what it should have been meant to do since ever: obey the programmer, instead of helping out amateurs!


AN INTERESTING OUTCOME
Going deeper into this issue, and looking for some highly competent insight, we can find another pretty interesting conclusion

Thinking over the issue above we came across, I regarded it more and more unusual so I thought this was a good chance to call in the «heavy artillery» and, being positive that such behaviour in the script made little sense, I asked for the most qualified advice I could conceivably think of; for when you're at JavaScript you can find many books out there but the highest allure still belongs to «The JavaScript Bible» by Danny Goodman (available on Amazon): I believe I've read and studied so far about 10 thick books on javaScript and although many are excellent, if not all of them, I would still advice the JavaScript Bible as the best approach (object by object) and absolutely the most comprehensive JavaScript resource available out there.
So, here is what we found by combining a JS Bible excerpt with the following Danny Goodman's email:

A CONTRIBUTION OF HIGH INTEREST
I did some digging on this to understand what's happening inside the JavaScript interpreter. The behavior you site is actually correct in the eyes of the ECMA specifications for how array index values are to be processed. Some of the oddities come from the close relationship between JavaScript arrays and objects (applying the typeof operator on either value type returns "object""; an Array object is a special type of JavaScript Object object).

Why, then, does a string version of an integer not survive its "string-ness" when assigned as an array index? Deep inside the interpreter, the value you assign as the index is validated to make sure it: a) doesn't yet exist; or b) can be used as an array index (and, thus be able to cause the array's length property to bump up by one once the assignment operation is complete). It is in that validation process that the index value (which actually arrives at this point of the interpreter process as an internal string value, regardless of how you assign it) is automatically converted to an integer. That integer is compared against all other integer index values that belong to the array to see if the index already exists. If the index doesn't already exist, then it is that integer value that gets assigned as the index. If the index cannot convert to an integer before comparison, then the array entry assignment becomes a property assignment of that array object. The hash name string becomes the property name, which your scripts can then retrieve as either arrayName["hashName"] or arrayName.hashName. But the property name and its value do not become part of the iterable list of array items. That's why the length property of one of these pseudo-hash tables is zero, and why all Array object methods don't do anything, yet don't throw any errors: in the methods' eyes, the array is empty.

I was trying to think back why this behavior never bit me. It must have to do with my overall coding practices and how I use hash tables. Although it isn't necessary in more strongly typed languages, I tend to think of hash table "index" values as words that could also act as object or property identifiers, rather than as numbers--a quick way to perform a lookup by word strings, rather than looping through numeric index values in search of a match. In truth, such hash names _are_ property names, and therefore must follow the same naming restrictions for JavaScript items, such as variable names and the like. If I need numeric values associated with array data, my practice is to create an array of objects; one property of the object is the numeric value associated with the array entry. That leaves me with the greatest flexibility: I can iterate through the outer array for lookups if I want, and I can sort that outer array on the numeric property of the objects in the array, giving my outer array the current order of the numeric values associated with the entries.

So, that's the deal.

Danny
http://www.dannyg.com

[Reprinted by permission]


This insight is valuable. I immediately grabbed my JavaScript Bible copy and I perused it to the right page, and actually there was a longed for reference to this possible issue (something you may easily overlook in a 1000 pages long book): so let's quote this amazingly complete book:
if you define array entries by property name, you cannot access those values via their index. In Navigator 2, the array assignments can be retrieved by their corresponding index value (...) in Navigator 3 or 4, however, because those entries are defined as properties, they must be retrieved as properties, not index values.
Now, let's see what all this amounts to: we have an array whose indexes are literals:
some["literalIndex"]="hallo world"
What we see is that this literal index is, actually, handled like a property name, thus you could retrieve the hallo world string either by:
some["literalIndex"]
or even (!):
some.literalIndex

Therefore, being the literal index handled like a property, it should, like Goodman points out in his email, follow the rules valid for every property name, such as they must start either with a literal or with an underscore but not with a number.
Therefore, if you assign to a literal a string starting with a mere number, the javaScript interpreter cannot handle it like a correct name (starts with a number) and forces it back to the numeric data type; on the other hand, if you put spaces in between a literal such as:
some["John's surname"]="Smith"
You can still retrieve the Smith value by:
some["John's surname"]
That would work; but the javaScript interpreter would not allow for:
some.John's surname
So here is a first conclusion, which I'd define a quantic conclusion: literal array indexes are properties of the object, therefore you cannot start them with a number, and this restriction strictly applies: on the other hand the JavaScript interpreter is still aware you're assigning property names, but not through direct assignment but like strings, so it is still able to handle some of them like valid invocations when put in between square brackets although it cannot convert all of them into a property name.

But the most engrossing conclusion we can draw by these precious insights provided by Goodman is this: hash-like arrays could not be scoured with a loop: having no index, they cannot be included in within a loop like, for instance, for(var i=0;i<hashedArray.length;i++).
Even more: Goodman stresses the fact these objects have no length at all (hashedArray.length=0), which makes perfectly sense: if literals indexes are actually dealt by the interpreter like property names (that is: try envisioning them like sort of branches branching out of the object in all directions: not a list that stretches the object in one single direction) and not like indexes, the array has actually no real length. Think of it, for it makes sense.

This problem already caught my attention in the past and being unaware of the inner workings of the interpreter, I was considering ways to achieve the looping of an hash-like array by building objects: a complex approach, without doubt. In languages like Perl we do have built-in methods like the foreach loops that can scout an has table and retrieve its values by its keys (its literal indexes, that is) but javaScript apparently had nothing similar.
Well, Mr. Goodman's contribution is incredibly valuable for it provides us with an avenue to finally loop even hash-like arrays without building complex approaches: javaScript has the following syntax, which takes avail of a not frequently used keyword in, whose purpose is to cycle through all the properties of an object:


for(var i in someObject){
//stuff...
}

Such loop can inspect all the properties that belong to an object: but if like Goodman reveals literal index in an array are actually properties of the array, there is your foreach-like loop: you can loop an hash-like array by a for-in loop!
for(var i in myHashLikeArray){
myHashLikeArray[i]//stuff...
}

A NOTE
For the sake of completeness, you'd keep in mind that when inspecting the properties of an object by the for - in loop, such loop will yield to you every value held by the hashed indexes, in case of an hashed array, since we have seen these indexes are considered and arranged like properties of the array: but if by chance you have also defined other properties in some process independent of the indexes of the given hashed array build up, also these other properties will be inspected.
So, for instance, if by keyword prototype you have defined some property which belongs to every array object such as:
Array.prototype.someProperty="foofoo" or
Object.prototype.someProperty="foofoo"
then also these properties would be looped.

It is not necessary that you understand this, in case you're not experienced enough (so to say): just keep in mind this note. I do not want you to define a prototype that belongs to every Array, for instance, and then loop an hashed array with the purpose of updating, for instance, a number that counts the number of its property and eventually find yourself with an amount of properties which exceeds the actual amount of hashed indexes: and all this simply because you forgot that elsewhere previously in your code you may have defined some more global property instructing it to pertain to every generic Object or to every Array type object.


If it were only my own stuff, I would not suggest to you to bookmark this page, but being not so I fell like encouraging you to keep in mind this approach:
  1. Make an hash-like array out of an array, hash-like array whose literal indexes are the values held by the original (non hashed) array
  2. Store, in each entry of this latest hash-like array, another tiny array which holds a reference to the indexes of the positions held by all its literal index in the original (non hashed) array (whereas they still were values and not indexes).
  3. Now, whenever you have to spot an entry in the original array, you don't have to loop it any longer.
  4. And finally, which squares the circle thanks to Mr. Goodman, you can even loop the hashed array if needed.
Behold now what could be the impressive advantage:
You have for instance to count how many times each value appears in an array (such as: let's count how many times array1 holds a value corresponding to "john" and how many times array1 holds a value corresponding to "mary" and how many times a value corresponding to "phil"):
without this approach you'd have to:
  1. Loop the array
  2. within each cycle, loop another array (call it countArray) which stores all the references found so far in order to update a calculations on how many times each of these references eventually recurs (a loop in within a loop: a quadratic "algorithm" we could say).
  3. When done, you have to loop the yielded countArray to count the occurrences of each entry.
With this alternative approach you just have to:
  1. loop the array once to make an hash with something like our hasher function out of it
  2. loop the hash to get the entries amount.
    You're done.
Whenever you're facing an application whose purpose is to perform several looping tasks, this approach is going to save to you a dramatic amount of looping time: an original array with traditional looping, plus an hasher like array, plus for-in looping. The sketch is complete, the improvement is meaningful, and we all have to thank Mr. Goodman for such a meaningful and precious insight.
Every one concerned with actual scripting for real browsers, and not for browsers in Wonderland, should have his/her Goodman's JavaScript Bible copy.

DELETING ITEMS FROM HASH AND NOT HASH ARRAYS
I have come across a request in the recent days: how to delete an entry from an associative array (so called as well hash array) in the very same way an unset function in a language known as PHP would do. I therefore thought it might be useful to provide you with a snippet of code that can do this.
The purpose is to completely eliminate the entry being sure no remains would linger around at all.
If you just set to null an array entry, that entry would not be actually eliminated for the length of the array would go unaffected.
An alternative way would be to use the delete() static function which has been added since browsers version 4, but also this method doesn't actually accomplish or meet the request of emulating a PHP unset static function, for the javaScript delete function would delete the entry and also resize the length property, but the indexes would be kept unaffected (that is, if you delete by using the delete function say entry 5 out of a 7 entry arrays, the indexes would be as follows: [0,1,2,3,5,6]: see, there is an index gap between 3 and 5: that's not a PHP like unsetting, which as such must leave no remains around).
Here is a function which unsets properly:

function unset(array, valueToUnset, valueOrIndex, isHash){
var output=new Array(0);
for(var i in array){
  if(!valueOrIndex){//search value
    if(array[i]==valueToUnset){continue};
    if(!isHash){
    output[++output.length-1]=array[i];
    }
    else{
    output[i]=array[i];
    }
  }
  else{//search index (or key)
    if(i==valueToUnset){continue};
    if(!isHash){
    output[++output.length-1]=array[i];
    }
    else{
    output[i]=array[i];
    }
  }
}
return output}

You use it as follows:
  1. You pass as array argument the array itself, either associative or not.
  2. You pass as valueToUnset argument either the value, or the array index, or the array key (key is the name for a literal index in a hash like array) that you look for and that you do not want to see any longer in your array.
  3. You pass as valueOrIndex argument either zero or 1: if zero, it searches for the given valueToSearch in the values, if 1, it searches for the given valueToSearch in the index (numbers) or keys (indexes of associative array, namely literals)
  4. You pass as isHash argument either nothing (or zero) or 1: if nothing or zero, means the passed array is not associative, if 1 means it is an hash array.
     
    I could have detected the type of the array by arguing on its length: since associative arrays have no length an array without a length might have been conceived as a likely associative (hash) array; the problem is, I don't program by hypothesis if I can and when I realize my idea was just an hypothesis: in fact, you can have associative arrays which might exhibit some length in case they're a mixture of literal indexes (as such only accessible through literal indexing) and numerical indexes (that can be accessed only by their relative number, and if they show up in an associative array would... give to the array a length, as you could see by reading the treatise above). In fact such objects can exist, mixing literals with numerical indexes.
    Also, arguing by the type of the second argument (String? Number?) the type of the array object, is unsafe in my opinion: for maybe you have reasons to want, precisely, a mixed object as your output of choice!
The unset function returns a copy: to affect the original do as follows (arguments are set just as an example, just note that the original array is provided on both sides of the assignment, namely like the object to alter and the passed 1st argument. This gives to you the greatest flexibility in case you want an array without some entries but without affecting the original, which might well be):
original=unset(original, "valueOrNum",0,0)


DOUBLE HASHER
An additional snippet: create two hashed arrays whose each entry refers to a matching entry of the other

A remarkable, although simple in its implementation, consequence of associative (hashed) arrays is that, being their indexes literals and not numbers, you can device associative arrays whose each entry carries a reference to a corresponding index of another associative array; example:
var array1=new Array() //one array
var array2=new Array() //a second array
array1["entry1_array1"]="entry1_array2"
array2["entry1_array2"]="entry1_array1"

If you inspect that small snippet above, you're to see that in array1 its first entry (and just for brevity also its only entry...) carries as a value the name of a (by now) corresponding entry in array2.
 
What can such a procedure be useful for?
Well, just for an example, imagine you have an array (say array1) carrying a list of html tag names such as, say:
array1["img"]
array1["tr"]
array1["table"]
etc etc...
You may want that such entries can immediately reference a corresponding translation of that tag into its equivalent in another language (an example I had in mind was asp.net), and you may want that also the opposite process is possible (that is: from html to, say, Asp and from Asp to html as well!). The best way to do this is to criss-cross two associative arrays:
array1["img"]="HtmlImage"
array1["table"]="HtmlTable,"
array1["tr"]="HtmlTableRow"
 
array2["HtmlImage"]="img"
array2["HtmlTable,"]="table"
array2["HtmlTableRow"]="tr"

If you have such a criss crossed table, you can instantly convert one name in the other and vice versa without having to perform a loop of the arrays and thus saving considerable time and machine work.
In fact some unusual call of a statement like:
array2[ array1["img"] ]
would tantamount to having called: array2["HtmlImage"]
 
Now, as long as the two arrays uniquely invoke each other you have not gained much, despite the interesting trick which may be behind it is arguably already almost apparent and clear to your intuition. But if you arrange things so that each entry of each of our two arrays do not carry uniquely the name of a matched entry in the other array but also an adjunctive set of data, there we go: by instantiating an entry in array1, you can instantaneously retrieve from array2 its corresponding entry/translation and a set of adjunctive values specifically attached to it.
How to attach them?
 
My suggestion is to make of each entry an array itself, whose first entry is the name of the literal (associative) index in the other array, and whose second entry is another array itself which you can later populate at your ease. So the structure would be, in pseduo code:
ARRAY["literal"]= ARRAY["otherIndexName", ARRAY]
 
Within such a structure there would exist the following objects:
array1["img"][0]="HtmlImage"
array1["img"][1]=new Array()
//you could now populate this latest array
    //thence:
    array1["img"][1][0]="foo"
    array1["img"][1][1]="foo1"
    array1["img"][1][2]="foo2"
    //and so on at your will...

 
The following snippet is meant to help you build on the fly and from scratch a set of two similar crossed associative arrays, whose each entry carries as its structure an array of two entries whose first entry (index 0) is the name of a corresponding entry in the other associative, and whose second entry (index 1) is an empty array which you can populate at a second time. This saves a lot of typing (writing down associative arrays may be very time consuming) and prevents errors.
function doubleHasher(onlyOne, nameFirst, indexesFirst, splitterFirst, nameSecond, indexesSecond, splitterSecond){
if(!window[nameFirst]){window[nameFirst]=new Array();};
  if(typeof(indexesFirst)=="string"){/*indexes passed as a string*/
    splitterFirst=(splitterFirst)?splitterFirst:" ";
    indexesFirst=indexesFirst.split(splitterFirst);
  }
      if(onlyOne){/*only populate with fake values ONE associative made of such indexes*/
      for(var i=0; i<indexesFirst.length; i++){
      window[nameFirst][indexesFirst[i]]=onlyOne;
      }
      return window[nameFirst];/*just to return something*/
      };
if(!window[nameSecond]){window[nameSecond]=new Array();};
  if(typeof(indexesSecond)=="string"){/*indexes passed as a string*/
    splitterSecond=(splitterSecond)?splitterSecond:" ";
    indexesSecond=indexesSecond.split(splitterSecond);
  }
/*Bad exception below: different lengths! Curtail to shorter one:*/
if(indexesFirst.length!=indexesSecond.length){
  var minimal=Math.min(indexesFirst.length, indexesSecond.length);
  indexesFirst=indexesFirst.slice(0,minimal);
  indexesSecond=indexesSecond.slice(0,minimal);
}
//run:
for(var r=0; r<indexesFirst.length;r++){
window[nameFirst][indexesFirst[r]]=
new Array(indexesSecond[r], new Array(0));
window[nameSecond][indexesSecond[r]]=
new Array(indexesFirst[r], new Array(0));
}
return window[nameFirst];//just to return something
/*keep this comment to reuse freely
http://www.unitedscripters.com */}

This function's arguments work as follows:

DOUBLE HASHER FUNCTION ARGUMENTS
onlyOne
If passed as zero, does nothing. If passed as something different than zero or boolean false, it will bypass the normal behavior of the function, and it would simply grab the argument named nameFirst (which has to be a String - see further on), would initialize out of it a global variable with that name (does that even if you didn't initialize it on your own!) and would make of it an associative array whose each literal index is an entry passed as the indexesFirst argument (to learn how such argument works, see further on).
So if this argument is passed, it just initializes a mere associative array. Its literal indexes as indexesFirst dictates, and each of such entries carrying as a fake value whatever you have passed as onlyOne argument (in this way you can build a whole associative array with specific indexes populating it with homogeneous fake values).
This argument must be passed always as you see, for it is the first: so can be passed as zero (0) if you want it to do nothing.
nameFirst
Must be a String (that is, passed in between quotes), and must be the name of the global variable that you want to assign as the first of your associative arrays. Can initialize such a global Array with such a name, even if you didn't do by yourself!
indexesFirst
A list of the literal indexes that must be assigned as indexes of the nameFirst Array.
The can be passed either as an Array of strings
example:
doubleHasher(0, "fooName1" new Array("a val", "a val 2"))
 
or alternatively it can be a whole string separated by a specific separator.
example, separated by a / sign:
doubleHasher(0, "fooName1", "a val / a val 2")
splitterFirst
If indexesFirst is passed as a whole String, this arguments must carry the splitter which must be used to split the whole string into its component to be later assigned as literal indexes of the firstly produced associative array (nameFirst).
example:
doubleHasher(0, "fooName1", "a val / a val 2", "/")
nameSecond
Must be a String (that is, passed in between quotes), and must be the name of the second global variable that you want to create. Can initialize such a second global Array with such a name, even if you didn't do by yourself!
indexesSecond
Same list as for indexesFirst but affects the second array! Therefore it must be the list of indexes meant to be assigned as literals to the second array.
splitterSecond
See splitterFirst: the same but affects the second array.

An example of a call to such function for the values in the example previously made:
doubleHasher(0, "array1", "img table tr", " ", "array2", "HtmlImage HtmlTable HtmlTableRow", " ")
 
Such a call has now created from scratch two associative arrays named as:
array1
array2

which are interweaved with each other in the fore said manner and whose each entry is an array whose
[0] index is the string name of the matching literal index in the other array, and
[1] is an empty array which you can now populate with whatever value/s you prefer (or just leave empty if this makes sense to you). You can even change this latest one to something different than an array type by simply stating/assigning, as an example:
array1["table"][1]="now it holds a mere String!"
 
Note the splitter arguments as an empty space for we separated the strings to be assigned as indexes by one white space.
Note that inserting one extra white space may result in errors, and that the order you pass (either as an array or as a string of subsequent texts separated by a given separator as we chose in the calling example) the literals meant for the first associative array must match with the order you mean to establish with the texts passed as literal indexes of the second associative array.