SCRIPTING:

Natalia Estrada
THE ARRAY, MATRIX AND XML (DOM) MAPPING UNFOLDERS
Powerful scripts that can unfold a Matrix (array of how many arrays you prefer, from zero onward) either if it is an Associative Array (literal indexes) or a standard Array (numerical indexes) or a XML/Html node in a page.
The algorithms reach the end of the branches and report all the ending values found (whatever they are as long as they're ultimative).
Moreover, they report the original map of the position of each object (the sequence of either numerical or literal indexes that lead to the unfolded entry)!
Moreover, any associative array gets mapped as a standard array as well!
November 2002
{ @ }
The model above is Natalia Estrada
LOADS OF NATALIA ESTRADA ON THE NET


YOU CALL IT SLEEP
Your man in Habana today reports...

«Sun of the sleepless! (...) Powerless rays, a Night beam Sorrow to behold!»
[G. Byron]
«I feel like Monday, but one day I'll be Saturday Night»
[Bon Jovi]

Perhaps
Rescuing after a V2 bombing
A nazi V2 rocket attack on London kills 200 civilians in Farringdon Market, March 1945. Rescue teams reach the place.
you know how does it feel when you go a season to hell and then you come back. Hemingway and Rimbaud did. You too.
Well, that's precisely how I feel after having devoted 10 days to these scripts. I now do not claim they're doubtlessly faultless, you never know when the Devil is in the game; but I can assure you that on a wide range of testings they worked smoothly, their logics is convincing, and a few things they do are nearly unique, as far as I know, in the field of the (java)scripts for free. So I can release them: my minimal quality requirements are met.
If they betray you, blame some bad luck before than them. And of course if you find something questionable, just email me.
They costed me some depression when, brainstormin' over them, I had to spend some sleepless nights; and then therefore insomuch because at daybreak... with the rays of the sun sneaking through the blinders I found my hands clutching a fistful of ashes, scribbled papers by then mysterious to myself scattered all around, and I had to stare at my failure and at my spectral look in the smoky mirror, cosidering a coffee at the bar and consequently I...; oh bars are cute in the morning: you trudge to them feeling like a wreck, the mocking monkey on your shoulder, and once inside they watch you seemingly tired and they assume you just got up: you got up early today, Alberto. Jesus, I didn't get down at all. They believe I just got up when they see me sleepy at 5pm you see!
Why people just doesn't quit assuming, since they should already know they invariably assume for the wrong? They just make me feel like Holden Caufield.
 
But eventually I did them, the goddam mapping & unfolding scripts.
So when you use this stuff use it quite freely but remember there is a guy who did paied a price for them: thus keep the commented web address in their codes, plz.
Morevoer, it is like composing acrostics while the barbarians plunder. If you'd only know how I feel: like saturday night at 5.30am, when the streets in Paris are like those in Vietnam.

FULLY REVISITED & IMPROVED on FEBRUARY 7, 2004
Additionally: Php implementations of these functions - click (by the way, plus php file finders and php directory scanners. Rather powerful).

ONE OF THE MOST BEAUTIFUL POEMS OF THE WORLD
[no, I'm not english]
 


THE CODES AND WHAT THEY DO
The codes and what they do, return, and what arguments they accept

a Basquiat painting
a Jean-Michel Basquiat painting
THE scanArray CODEX THE scanAssociative CODEX

lines:

lines:
WHAT IT DOES: it scans a numerically indexed array or matrix whose shape is vastly irregular and reports all its edges (that is, only the data at the end of each branch).

ARGUMENTS: only one, it must be an Array.

RETURNS: an output array whose shape is as follows:
array[ array[item, array] ]
That is, each entry of the returned array is an array of two entries. These latter ones are:
  1. The eventual edge item found at the end of each branch.
  2. An array again, whose each entry is a number: that is the map of all the traversed indexes to get at the reported item.

If unsuccessful: null or an empty array.
WHAT IT DOES: it scans an associative array or matrix namely an array or matrix indexed with whatever type of Data Type (not just numbers, that is), and whose shape is vastly irregular. The function reports all its edges (that is, only the data at the end of each branch).

ARGUMENTS: only one, it must be an Array (preferably associative: using this function on numerically indexed arrays won't cause errors but would be dysfunctional).

RETURNS: an output array whose shape is as follows:
array[ array[item, array, array] ]
That is, each entry of the returned array is an array of three entries. These latter ones are:
  1. The eventual edge item found at the end of each branch.
  2. An array again, whose each entry is one of the traversed indexes to get at the reported item.
  3. An array again, whose each index is a number faithfully reproducing, this round in a numerical map, the original associative map traversed to get at this item. Thta is, it is a conversion of an associative array path into a numerically one!

If unsuccessful: null or an empty array.
THE scanDom CODEX THE scanAttributes CODEX

lines:

lines:
WHAT IT DOES: it scans a DOM node matrix and reports all the nodes traversed to get at it.

ARGUMENTS: only one, it must be a valid DOM node (such as even keyword document would be).

RETURNS: an output array whose shape is as follows:
array[ array[item, array] ]
That is, each entry of the returned array is an array of two entries. These latter ones are:
  1. Each node met and traversed in the process.
  2. An array again, whose each entry is a number: that is the map of all the traversed indexes to get at the reported node item.

If unsuccessful: null or an empty array.
WHAT IT DOES: it scans a the attributes of a tag (Dom/xml capable browsers).

ARGUMENTS: see below.

RETURNS: see below
If unsuccessful: false or an empty array.
Kristanna Loken
The model above is Kristanna Loken
LOADS OF KRISTANNA LOKEN ON THE NET
THE scanArrayAll CODEX THE scanAssociativeAll CODEX

lines:

lines:
WHAT IT DOES: it scans a numerically indexed array or matrix whose shape is vastly irregular and reports all its edges plus all the traversed branch objects as well (that is, not only the data at the end of each branch, but also each branch; in sequence).

ARGUMENTS: only one, it must be an Array.

RETURNS: an output array whose shape is as follows:
array[ array[item, array] ]
That is, each entry of the returned array is an array of two entries. These latter ones are:
  1. The edge or branch item met.
  2. An array again, whose each entry is a number: that is the map of all the traversed indexes to get at the reported item.

If unsuccessful: null or an empty array.
WHAT IT DOES: it scans an associative array or matrix namely an array or matrix whose indexes can be of whatever data Type and not just numerical, and whose shape is vastly irregular and reports all its edges plus all the traversed branch objects as well (that is, not only the data at the end of each branch, but also each branch; in sequence).

ARGUMENTS: only one, it must be an Array.

RETURNS: an output array whose shape is as follows:
array[ array[item, array, array] ]
That is, each entry of the returned array is an array of three entries. These latter ones are:
  1. The eventual edge item found at the end of each branch.
  2. An array again, whose each entry is one of the traversed indexes to get at the reported item.
  3. An array again, whose each index is a number faithfully reproducing, this round in a numerical map, the original associative map traversed to get at this item. Thta is, it is a conversion of an associative array path into a numerically one!

If unsuccessful: null or an empty array.
THE find CODEX THE findAll CODEX

lines:

lines:
WHAT IT DOES: it scans a numerically indexed array or matrix whose shape is vastly irregular and reports the first array element whose value matches with the second argument.

ARGUMENTS: two; first must be an Array, second the value searched for.

RETURNS: an output array whose shape is as follows:
array[ item, array ]
That is, an array of two entries. These latter ones are:
  1. The eventual edge item found at the end of each branch.
  2. An array again, whose each entry is a number: that is the map of all the traversed indexes to get at the reported item.

If unsuccessful: null or an empty array.
WHAT IT DOES: it scans a numerically indexed array or matrix whose shape is vastly irregular and reports all the elements whose value match with the second argument.

ARGUMENTS: two; first must be an Array, second the value searched for.

RETURNS: an output array whose shape is as follows:
array[ array[item, array] ]
That is, each entry of the returned array is an array of two entries. These latter ones are:
  1. The eventual edge item found at the end of each branch.
  2. An array again, whose each entry is a number: that is the map of all the traversed indexes to get at the reported item.

If unsuccessful: null or an empty array.
Kristel Kama
The model above is Kristel Kama
LOADS OF KRISTEL KAMA ON THE NET
THE domFind CODEX THE domFindAll CODEX

lines:

lines:
WHAT IT DOES: it scans a DOM nodes matrix whose shape is vastly irregular and reports the first node whose property (given by the second passed argument) matches with the value passed as third argument.

ARGUMENTS: three; first must be a Node, then a property to search for (instance: nodeValue, nodeName) second the value searched for.

RETURNS: an output array whose shape is as follows:
array[ item, array ]
That is, an array of two entries. These latter ones are:
  1. The eventual edge item found at the end of each branch.
  2. An array again, whose each entry is a number: that is the map of all the traversed indexes to get at the reported item.

If unsuccessful: null or an empty array.
WHAT IT DOES: it scans a DOM nodes matrix whose shape is vastly irregular and reports all the nodes whose property (given by the second passed argument) matches with the value passed as third argument.

ARGUMENTS: three; first must be a Node, then a property to search for (instance: nodeValue, nodeName) second the value searched for.

RETURNS: an output array whose shape is as follows:
array[ array[item, array] ]
That is, an array whose each entry is an array of two entries. These latter ones are:
  1. The eventual edge item found at the end of each branch.
  2. An array again, whose each entry is a number: that is the map of all the traversed indexes to get at the reported item.

If unsuccessful: null or an empty array.
THE associativeFind CODEX THE associativeFindAll CODEX

lines:

lines:
WHAT IT DOES: it scans an associative array or matrix namely an array or matrix whose indexes can be of various Data Types and not just numerical; reports the first item whose value matches with the second argument.

ARGUMENTS: two; first must be an Array, second the value searched for.

RETURNS: an output array whose shape is as follows:
array[ item, array, array ]
That is, an array of three entries. These latter ones are:
  1. The eventual edge item found at the end of each branch.
  2. An array again, whose each entry is one of the traversed indexes to get at the reported item.
  3. An array again, whose each index is a number faithfully reproducing, this round in a numerical map, the original associative map traversed to get at this item. Thta is, it is a conversion of an associative array path into a numerically one!

If unsuccessful: null or an empty array.
WHAT IT DOES: it scans an associative array or matrix namely an array or matrix whose indexes can be of various Data Types and not just numerical; reports all of the items whose value matches with the second argument.

ARGUMENTS: two; first must be an Array, second the value searched for.

RETURNS: an output array whose shape is as follows:
array[ array[item, array, array] ]
That is, each entry of the returned array is an array of three entries. These latter ones are:
  1. The eventual edge item found at the end of each branch.
  2. An array again, whose each entry is one of the traversed indexes to get at the reported item.
  3. An array again, whose each index is a number faithfully reproducing, this round in a numerical map, the original associative map traversed to get at this item. Thta is, it is a conversion of an associative array path into a numerically one!

If unsuccessful: null or an empty array.
Gena Lee Nolin
The model above is Gena Lee Nolin
LOADS OF GENA LEE NOLIN ON THE NET
Private Murdoch is back
London. Private Hector Murdoch arrives at his prefabricated house to be greeted by his wife and son: Hector had been away for four and a half years, most of which he had spent as a Japanese prisoner of war.
Usa paratroopers show a nazi flag
Usa paratroopers display a Nazi flag captured in an assault on a French village, not long after the D-Day landings.


lines:

WHAT IT DOES:it is a snippet named reget. It gets in two arguments, an object and its map as yielded by one of the scanners and finders above. When run, it will rebuild from that object and that map the corresponding branch.
A third argument is optional, and if set would rebuild the object taking in all the indexes present in the map except the last one: this allows for those cases when you know that the yielded item by following that map would be final data, but you don't want it but the branch that holds it.
Family in Uk gets rationes in 1947
London, 1947. Families collect their fuel rations from the South metropolitan gas Company. Things our youth knows nothing of. Winds of the World give answer: Where's that kid now, where's that woman? Behold the eyes of the kid, then consider the eyes of the woman.


INNER WORKINGS
General explanations on how the matrixes are scanned, mapped, and how the items are found for the functions that perform a search and not only the scan report

The following section explains some technical ideas working inside these scripts. If you're not interested in them just skip this section.

While writing an article, I came to rethink these functions and I realized at least three elements could be changed:
  1. A couple of conditional statements within the previous versions were not really necessary: I included them because, as you may have noticed, I am over-cautious: when we release into a promiscuous environment such as the whole public of the internet a few functions, you never know what type of mischief a third part could do while using them.
    Thus my tendency to forestall "uncompetent" users led me to a couple of moot statements that were not really needed.
  2. A misconception has been fixed: within the function I included an if conditional statement that, though not detrimental in the least for the validity of the output, was indeed useless. That has been "fixed". Sleepless nights can sneak into codes these things.
  3. An attempt to overcame initializations of strings or numbers like:
    new Object("String here")
    has been dropped: the functions are thus faster, though they don't detect any longer this type of exceptions. Yet, these exceptions are not something an algorithm should really take care of, but are something a matrix should not include in the first place: strings should never be initialized as Object Data Types!
  4. Eventually, the arrangement of the original functions is so powerful, that it was possible also to implement a wealth of similar new functions, recently added here: functions that not only scan but also find elements within an irregular matrix.
    So it is not that the original functions were bad, it is that they were good enough to lead to a dramatic set of improvements.
These functions deal with matrixes (though would work also with simple linear arrays).
A matrix is a data structure where there can be a variety of arrays nested within each other.
Such object could sport a regular pattern in their skeleton, yet the assumption we'll hold throughout the scripts is that we don't know if such patterns are actually there, and if they are we don't know what their recurrences are, and/or most likely they don't have any regularity in their structures at all.

This problem engrossed me since ever for you're more likely to find this type of objects when scripting for web documents, rather than structured trees such as binary trees and the alike: the soundenss of this fixation of mine and the truthfullness of my assertion can be proved true when you all of a sudden face a tree of Html or Xml nodes: they are nearly undefined and "unstructured" by definition: who knows how many tags the guy included in that section, isn't it?

So, the problem boils down to this, basically: having a HTML or a XML document or a DOM set of deeply nested nodes, collect and report all the eventual data reached at the edges of each ramification.

As said, he structure of the tree is assumed as unknown by definition. No pattern is anywhere to be foreseen.

Obvious variations of the algorithm must be able to report not just all the eventual edges (instance: an Array typically carries relevant data only at its ends), but also the whole set of traversed nodes in between, because they could raise to significance (instance: childNodes are all valid objects for further manipulation, regardless of their final or interlocutory position).

Additionally, for all the algorithms a map of all the trails undertaken (that is, the traversed indexes) in the process is required as well.
This would allow not only for reporting for instance the edges, but also for re-finding where they were located in the original input object/matrix; which of course is not of immaterial importance.

For associative matrixes, namely matrixes whose indexes can be of mixed Data Type and not merely of the Numerical Type, both maps are required: the one carrying the path of associative indexes, and one of numbers, emulating the sequences of the associative indexes orders; so it would be possible to convert an associative matrix into a numerically indexed array whose index-progressions would faithfully reproduce the scheme of the original associative version.
This is not trivial an accomplishment.

I didn't yield to the vanity of making the algorithm recursive, namely a function returning itself. Recursion is the main source of Stack Overflow errors in javascript, and DOM nodes could flock in by the thousands, making overflows nearly a certainty (in the test form further on, running the scan dom function on one table yields as many as nearly 3000 nodes in this very same document).
Thus, we'll convert whatever recursive exigency that may be likely to arise, into a conventional loop.

To implement these objectives, we grab a stack.
A stack is an on-the-fly built up Array whose lifespan will be interested by the following phenomena: whenever we push a new element on its head (I consider the head its increasing index entry), upon revisiting the Array the last pushed in element is the one that is going to be popped off: LIFO, acronym for Last In First Out. By the way its conceptual opposition is the FIFO queue: First In First Out.

For the algorithm to work, each element of this stack is bound to include information on the currently pushed element, and not just the element itself. Thus each entry of your stack cannot contain one item, it must contain more because it must allow for information fields; and we're to find out that three slots suit our case.

var stack= new Array( new Array(0, 0, 0, new Array()) );

As you see, each stack instance is an array of as many as four information fields, the last of which is an array once again.
Let's feed the first two entries with a dom node or whatever object with a length; our placeholder name for this incoming generic tree is 'node', and we assume we can know its length (array.length, childNodes.length):

var stack= new Array( new Array(node, node.length, 0, new Array()) );

This is just the seed to feed our algorithm.

The runtime loops through the stack by a while cycle. while(stack.length){}

the stack, having being fed, has a starting length of 1 (one nested array), so the loop ignites.
Its only currently available index for its last (and for now only) entry (LIFO) is thus zero, or:

var currentStackIndex=stack.length-1;

You know that stack[currentStackIndex][0] is an object.
Now, let's wonder whether stack[currentStackIndex][0], namely the first entry of the array nested in the currently inspected index of stack, is an Object (array, node):

if(typeof( stack[currentStackIndex][0] ) == "object")

Also, has this currently engaged object a length, and is thus another valid object (array, node) on its own right? Do not mistake the length of the stack by the length of the stack entry currently piled on it simply because at the beginning they coincide.

Remember that we have passed such length as the second entry of the nested stacked element, so our question now boils down to this twofold one:

if(typeof( stack[currentStackIndex][0] ) == "object" && stack[currentStackIndex][1]){}

If the above is true, we never met this branch (object, node) before.
We're on an object with a length of branches; we never visited this current object's branch, so we now need to know the index of the first entry on this branch. Most assuredly, it is given by an operation like:

stack[currentStackIndex][0].length - stack[currentStackIndex][1];

In fact, the first is the object itself and we're drawing out its length, and the second is the length of such object itself once again, but referred as we passed it to the stack. They are the same only for now alone. The tautology is only apparent, as you'll soon see.
Now, would you dare contend such a difference yields zero now, and that such number is precisely the index of the first offset of this input object's first branch?
So, store this index on a temporary variable:

var nextObjectIndex=( stack[currentStackIndex][0].length - stack[currentStackIndex][1] );

Thus, since that is an Index, the actual object (branch) entry which is indexed by such number is exactly:

stack[currentStackIndex][0][ nextObjectIndex ];

Now, let's do the critical trick, which you'll understand shortly: let's decrease by 1 the numerical value of the length of the current object and which is stored in stack[currentStackIndex][1], so:
--stack[currentStackIndex][1];

Don't forget what we have done.
Now, you have a third entry in each nested array of the stack that you have never used yet, and which you initialized as a mere number zero, remember? it is:

stack[currentStackIndex][2]

We will use it like a flag to specify that this dish on the stack has already been visited, by setting it equal to 1:
stack[currentStackIndex][2]=1;

And now finally push on the stack the new object, remembering each stack element must be an array and taking care of populating it with the three fields it requires:

stack.push( new Array( stack[currentStackIndex][0][ nextObjectIndex ], stack[currentStackIndex][0][ nextObjectIndex ].length, 0 ) );

Now, imagine this part of the algorithm in action:
In fact assume an Array is of 5 entries. I put this array5 on the stack.
array5LENGTHmemorize=array5.length
stack.push( new Array(array5, array5LENGTHmemorize, 0, new Array(0)) )
Then I start inspecting its 1st entry, namely [0]:
I firstly decrease the second entry by one:
--array5LENGTHmemorize
which thus now holds 4.
Then I add the first object found at index [0] on the stack:
behold: I put on the stack this latest object:
stack.push( new Array(array4, array4LENGTHmemorize, 0, new Array(0)) )
Now to simplify say the recursion at the next iteration whereas it inspect this newly produced items finds this object is the end of a branch, say a string or whatever: it pops it off the stack, as you'll see, and saves it to the output.
We thus come back to the previous element in the stack array5. How can you know you must now not inspect again index [0] but you'd start from index [1]?
Well, "simple"...:
I just interrogate what
array5.length-array5LENGTHmemorize
returns, and since the length is 5 and I previously decreased array5LENGTHmemorize by one thus making it equal 4, guess what: it tells me... 1!
There we go: grab index [1], and soon after obviously decrease array5LENGTHmemorize by one again to repeat the trick the next time the popping out dishes bring the level of the searched stack back to this plate!
At each round you're thus sure that you don't consider twice an already exhausted branch.

Thus, the algorithm adds on the stack only objects, and upon revisiting an already visited object, thanks to the operation

--stack[currentStackIndex][1]

is smart enough to understand it has to grab the new subsequent index it has not scanned yet, and not once again the visited one(s).

We're left at this stage with two options:
1] we meet an entry which is not an Object data type and has no length both (instance: String, Number).
2] we meet an exhausted stack branch - which has such still does is an object (array) and does has a length, but we simply scanned it all already.

For these cases, we arrange an else statement matching the main if statement; an else statement where all items that are no longer object Data Type, or whose branch length is exhausted because all sub-branches have been visited already, are delivered.
In fact, remember that if a stacked object has reached the condition:
stack[currentStackIndex][1]=0
which represents its currently scanned length, it won't qualify to enter the 'if' conditional statement again.
That will invariably happen because each time we re-visited a stack entry we declared:
--stack[currentStackIndex][1]

Thus we can deliver each entry which has attained such conditions to an 'else' statement, within which we perform what follows: we firstly verify whether the till now forgot stack[currentStackIndex][2] element has been set to 1. This happens when a branch offset has already passed in the jaws of the algorithm.

The reason we do this is to avoid collecting data which is not final, but which is, conversely, exhausted branches; exhausted stack branches have no longer a length stored into stack[currentStackIndex][1], though still being objects: thus they would not enter the main 'if' statement, for they don't qualify as far as the length is concerned, though still being objects (branches); yet, we don't want to collect them, because they are not eventual items, but whole (already visited) branches. So with such 'if' statement within that 'else' we make sure we skip them and we collect only the actual edges.
Note that this would include edges represented by empty arrays!
Done that, we pop the stack; that is: we completely trash this stack entry.

The last thing we said, we want to map it.
We put in the code:
var map=stack[currentStackIndex][3].concat([stack[currentStackIndex][0][ nextObjectIndex ]Index]);

As you see, it just concatenates all the traversed indexes while it traverses them.
The javaScript method concat requires its argument being an array, and appends its unfurled elements, not the array as a whole. Additionally, concat yields the repleted array as a brand new object, unrelated with the original ones.

I need to account for its use, for it may seem just like a 'push' method emulation: it is.
The reason concat and not push is employed is this: javascript implements pointers with non primitive Data Type; that is, you manipulate an Object and you assign it to a new variable as 'map' is, with the assumption it bequeaths an independent copy; but it does not.
If you would use push, always the same (originally pointed) array gets affected & thus transmitted to each map, with the consequence that you'd eventually find every stack item carrying the same map. That's bad.

You can push on the stack as a whole, because it does is meant to be always referred as the same one object; but you need fully autonomous copies for the singular objects that populate each stack instance. You must dodge the pointer: you first need to make sure you have a brand new object, and later push; concat achieves both purposes in one strike.
There is an alternative: initializing inline a new array each time, but that may eventually lead to a somewhat over-complexified output shape. Try it.

Then when you add to the output, add two items at a time since you want to report the maps too; arguably, your output will thus be an array of arrays, namely an array of scanned edges, whose each entry is an array of two elements: the reported edge, and whose second entry is again an array listing all the numerical indexes traversed to get at it:

output=array[ item, array[] ]

To make the variations collect and report with the output also all the visited nodes we just have to:

a] include a copy of the 'if' statement meant to reside inside the else, soon after the main 'if' statement, because we have to report anything anyway, regardless of whether it is an edge or not: so we don't reap only within the else, but also within the main if statement!.
Such if' statement still requires checking whether stack[currentStackIndex][2] is zero, to make sure you don't report twice a node upon revisiting it. If this check holds true, push on the output this stack entry.
c] you perform this as soon as you enter the outermost 'if' conditional statement.
By the way, by doing this the root node -first output entry- will report no associated index/es. Correctly.

For DOM usage, you introduce childNodes statements:

As already said, the "shortcoming" of the algorithm (which could be addressed adding more code, which I won't do here) is that the matrix has to be populated in a non tricky way; if you initialize an edge as:

node[X]=new Object("I'm a string. No, yes really.");

that would return "object" to the typeof query, and the algorithm will be fooled thinking it is such and will try to scan it. But, like a two headed eagle or a two edged ax, it is also a string: thus a conflict will arise among what is reported by the chosen initialization (Object) and what is yielded by the execution of such initialization (String). Moreover, releasing a string it does has a (chars) length: thus would fully qualify for the first 'if' conditional statement, and would grisly enter it.

Would you initialize strings like that? I would not, yet it is my duty to highlight to you these types of nasty habits that could show up in matrixes. But don't worry: DOM nodes don't do that, only humans do.

To tailor these functions to scan associative arrays, you must keep in mind that you cannot safely rely at all, with associative matrixes namely with objects that are indexed after whatever Data Type and not just after numbers, on the trick we used namely that of the predecremental of the entry:
--stack[currentStackIndex][1]
Thus, what we need to do is to find a way to make this become possible.

To achieve that, you add a few more fields in the stack instances to carry additional information.
Then, as soon as we enter an object of the stack, we verify if we have already collected a map of all its indexes; this sadly yet inevitably, requires looping at least once (I managed to do this once and once alone for each branch) all the indexes of each newly found branch and gather them in a collection.
While you gather this collection, you collect these associative indexes as values of a numerically indexed array named collected whose indexs are, as said, numbers, and yet whose held values are the associative indexes themselves.
In this way you have now a numerically indexed array (the just collected array), and being an array you have also its length, which we can now pre-decrement in order to implement what I called the "critical trick". Thus, we are back, for an associative array too, to the same type of implementations we used for standard numerically indexed arrays: we have a length to decrease, and all the associative indexes are linked to a corresponding numerical index match.


USAGE OF THE SCRIPTS
How to use the scripts and what they return

You may think that you can use the script that unfolds hash arrays (namely literal indexed arrays) for all the purposes, including unfolding numerically indexed arrays Arrays: actually it is possible and the script would work fine (but not vice versa: you cannot use the scanArray to scan associative -hash- Arrays) but I recommend to you to use, whenever possible, the scan Array for numerically indexed arrays and the associative scan for literally indexed arrays or matrixes whose indexes nature is uncertain or unknown to you: this because scanning a numerically indexed array with a tool tailored to scan literals is somewhat dysfunctional: looping associative objects implies in the scanAssociative function the constant repetition of an inner loop to re-determine the last inspected property: this trick is not required when scanning arrays, for the numerical index they already provide let you pinpoint the last scanned level immediately by a merely mathematical subtraction.
 
Conversely the scan dom is meant to scan only html or xml nodes, and cannot be lent to other usages.
As far as the dom scan is concerned, maybe you are thinking to use it to unfold javaScript objects such as window or navigator: a legitimate request and need, but well, do not do that; we have a twofold issue there:
  1. Some of those objects are tautologies, namely have entries that define themselves in function of themselves, triggering therefore a potential infinite loop: an example in case is the couple window/self.
     
    It is certainly possible to work around this issue by empowering the script with an internal array meant to store all the already inspected items and thus skipping the ones that reiterates themselves more than once assigning them to such array and comparing each loop with the items already assigned and belonging there; none the less you're to see that also this approach would eventually be useless because of the following point (and by the way this is one of the reasons I've renounced to introduce anti tautologic approaches in these scripts. The second reason is that an anti tautologic approach would also strip off those entries that possibly and quite legitimately have been initialized through the same constructor: which would mean that an attempt to introduce in these scripts anti tautologic features may end up skipping all those entries that begin with... =new Array! In fact the retieration of such syntax twice would tantamount, to a tautologic feature, the same Object that repeats itself you see: and thus the script might not loop the matrix at all!)
  2. Some of the built in javaScript object such as navigator must be, in my humble intuition, protected classes: this means that when performing on some of them some trivial operations allowed on all the other properties, you may find some property that being an Object and consequently initalized by a constructor, might have in such constructor's signature a protected declaration: this would be enouhgh to make a simple attempt to loop some property trigger a sudden, unexpected, mysterious error...
    I can't see any other viable explanation for behaviours like those that when looping navigator.plugins trigger an Error: alert(typeof navigator.plugins); for(var i in navigator.plugins){alert(i)} The loop yields an error (in Explorer 6 at least), although the typeof returns Object and therefore the object should have been liable to be looped by a for-in cycle like absolutely all objects are!
The functions return the following shapes:

THE SCAN ARRAY
It gets one argument which is the main array (matrix arguably, namely array of arrays) to unfold, conventionally called root or dorsal.
var foo=scanArray(BigBranchedARRAY)
 
Returns an array [x] whose each entry is an array of two entries [x][0] and [x][1]:
foo[5][0]; foo[5][1]
the first [x][0] is one unfolded element, the second entry [x][1] is, in the shape of another array (therefore: [x][1][x]), a collection of subsequent numbers representing all the numerical indexes that if added to the root object pinpoint the entry [x][0] in the root object.

THE SCAN ARRAY TEST FORM
A matrix to unfold - edit at your own "risk"!
[no alert on focus? » ]
Rene Magritte
A Rene Magritte painting
[use scanArrayAll: ]


THE ASSOCIATIVE SCAN
It gets one argument which is the main array (matrix arguably, namely array of arrays) to unfold, conventionally called root or dorsal.
var foo=scanArray(BigBranchedARRAY)
 
Returns an array [x] whose each entry is an array of three entries [x][0] and [x][1] and [x][2]:
foo[5][0]; foo[5][1]; foo[5][2]
the first [x][0] is one unfolded element, the second entry [x][1] is, in the shape of another array (therefore: [x][1][x]), a collection of subsequent literals representing all the literal indexes that if added to the root object pinpoint the entry [x][0] in the root object.
 
The third [x][2] is the same as [x][1][x] but each last x numerical index is not yielding this time a literal index in the path leading to the unfolded element in the root object, but a numerical index: using them you may, in case you want it, build up a whole matrix equivalent in its shape to the associative matrix (indexed with literals, that is) but this time rebuilding its indexing with... numbers!

THE ASSOCIATIVE SCAN TEST FORM
a Rene Magritte
A Rene Magritte painting
A matrix to unfold - edit at your own "risk"!
[no alert on focus? » ]
[use scanAssociativeAll: ]


THE SCAN DOM
It gets one argument which is the document element to unfold passed as an Object (without quotes, that is); such elements can be generated by grabbing a tag node returned by
document.getElementsByTagName("TABLE")[0]
Note that in this latest built in javaScript method the tag name goes in between quotes; also, the syntax getElementsByTagName is case sensitive (if you want to know more about getElementsByTagName and why you have to add an index too, and you want a wider dissertation on how to use its arguments, you can have a look at this file).
 
 
Alternatively, you can pass to scanDom an ID of a tag and in this case it must be in between quotes, to make the scanDom inspect it by, in its inner workings when it sees the argument is a String,
document.getElementById("idHere")
So scanDom gets one argument and it must be either an Object or a String: in the latter case the String must be the ID of a tag.
 
Returns an array [x] whose each entry is a node included within the given root: you can derive the name of the node (which tantamounts to the name of the tag) and the value (which tantamount mostly to null, and in text only if and in case it is a text node what you're checking. Feel free to test in the form below) by appending at each returned entry either .nodeName or .nodeValue.
Example:
var foo=scanDom("anId");
//or: var foo=scanDom(document);
//or: var foo=scanDom( document.getElementsByTagName("table")[3] )
//now foo stores an array of nodes. To inquiry on one of them:
foo[10].nodeName;
foo[10].nodeValue;


THE SCAN DOM TEST FORM
Shelves in UK in 1940
Business as usual at its most absurd: readers browse among the charred remains of a library in England, 1941
1 A] Select an Array of TAG nodes:
 
document.getElementsBytagName( )
1 B] Do pick it:
 
2] Then pick an index:
 
3] Now click RUN scanDom:
 
produced nodes:
.nodeName=
.nodeValue=



Behaves as scanDom but unfolds the attributes of one tag as nodes, thus is more limited in its scope. It reveals on all browsers capable of reading getElementById only those attributes that have been declared in within the inspected tag, returning each node as an object arrayed in the output array. You can therefore use on each of the returned entries both nodeName and nodeValue to read them.
 
If you pass a second parameter named explicit as 1, it will produce as output an Associative Array whose each entry is indexed by the name (nodeName) of each found attribute: you're to see soon what is the great utility of running my scanAttributes in the explicit mode.
 
Whether you're aware of this or not, this snippet of code is precious: in fact the scanning it performs is necessarily ultra-fast (scanning the attributes of a tag can't be long anyway!), and the returned output allows you to set/change the values of each attribute on the fly in modern browsers!
For instance:
<a href="http://www.foo.com" onClick="return false" myProperty="cool" id="testLink" target="_blank"> semi fake link text </a>
We grab it as follows:
var aname=scanAttributes( document.getElementById("testLink") )
The returned output is an Array whose each entry is numerically indexed and is an attribute. By inspecting it with a small loop you can for instance locate where is the entry for the href property (in our example at index 0) and then change it on the fly by:
aname[0].nodeValue="http://www.newfoo.com"
What I want you to notice is that you cannot just set the assignement as merely = to:
aname[0]="http://www.newfoo.com"
But you do have, in order to set it, to add the new DOM2 keyword for nodes: nodeValue.
Eventually, remember that also even event handlers are returned as nodes (evcents such as onMouseOver, onClic and so on...) but their nodeNames are always all lowercased.
 
But if you run it in the explicit mode you can do even better:
var aname=scanAttributes( document.getElementById("testLink"), 1 )
You can now instantaneously address (knowing beforehand what the names of the attributes could be - and if you attempt to get a non-existant name you'd yield "undefined") your property as follows (the if check is for good measure of safety, but is not indespensable at all): if(aname["href"]){ aname["href"].nodeValue="http://www.newfoo.com"; } //or: wanna read the value? var avalue = aname["href"].nodeValue;
Here's the real version of our example link outlined above:
semi fake link text
 
You may have noticed in the exposition of its code above, that the onClick event it includes is set to return false, namely clicking it the redirection doesn't occur.
Try in the Test Form below to click the Run att-test button and then select from the appearing list of properties the onclick: then in the field "write NEW value here" write:
return true
Then click the SET to button.
Now click the link: you're to be redirected: you changed the property (an event handler in this case) on the fly!
 

THE ATT SCAN TEST FORM





All the functions are liable to return false if some minimal conditions for the functionality of the function are not met (for instance there is no tag with the passed ID name).