Thursday, February 01, 2007

 

Arrays are Associative

I owe this technique to Peter Truskier who posted a script on the U2U forum in early January that blew me away. See Peter's post. Look at the way Peter formed that array named thePageNames. Incredible. I just never thought of doing that even though I had been aware all along that JavaScript arrays are associative.

What this means is that if you have a set of named entities and you want to filter out all the duplicates, rather than use some kind of isInArray test, you can use an associative array. For example, this morning, I wanted a list of all the paragraph style names used in a run of text. Recalling this example by Peter, I came up with this function:
function getParaStyles(text) {
    var tempArray = new Array();
    var textStyles = text.paragraphs.everyItem().appliedParagraphStyle;
    for (var j = textStyles.length - 1; j >= 0; j--) {
      tempArray[textStyles[j].name] = "";
    }
    var styles = new Array();
    for (var j in tempArray) {
      styles.push(j)
    }
    return styles
  }
I'm so pleased with this that I had to share it. Thanks a bunch Peter!

The first loop walks through all the paragraphs getting the name of the style used by the paragraph creating/setting a value (could be any value; we don't care about the value) for the element of tempArray with that name. Thus, by the end of the loop we have precisely one member of that array for each style that was present in the text.

The second loop pushes those names into a regular indexed array and returns the list of names to the calling script. Great!

Comments:
Hallo Dave,

Thanks for this post, it inspired me to some experiemnts with arrays.

As I found, your statement "Arrays are Associative" is not actually true. In fact, javascript up to the current version 1.5 does not actually support associative arrays.
What is called an "associative array" in javascript is actually just an array-like object, or collection of objects.
Even if you first declare your tempArray as "new Array()", it lacks all the properties and functions of the Array class after being poulated, including e.g. the length property. This is why it can only be browsed using a for...in loop.
If you declared it as "new Object", it would know at least the toSource() function of the Object class, even after being populated.
Objects allow their properties to be accessed via s.th like myObject["itsPropery"], which looks much like an associative array, but it's definitely not. Your tempArray object ends up with unique properties (the paragraph styles) just because an object cannot have several properties with the same name.
After some experiments I found the following solution, which does the same as your function with basically the same technique:

function getParaStyleNames( ofText ) {

var ParaStyles = [ ofText.paragraphs.everyItem().appliedParagraphStyle, {} ];

for ( var ParaNo in ParaStyles[0] )
{
ParaStyles[1][ParaStyles[0][ParaNo].name] = "";
}

for ( var ParaStyle in ParaStyles[1] )
{
ParaStyles.push( ParaStyle );
}

return ParaStyles.slice( 2 );
}
Note that the only "associative" array, i.e. the object instantiated by {}, is ParaStyles[1]. ParaStyles itself is a real array with numeric keys, as well as ParaStyles[0], which is the array of applied paragraph styles. Both of them are no longer needed at the end and therefore removed from ParaStyles immediately before the function returns the array.

Don P.
 
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?