dotNet iterables in MAXScript, a pain in the ass

loocas | dotNET,maxscript,Python,technical | Sunday, May 5th, 2013

Autodesk facepalm

Yes, that is true. Stuff that should be inherently and implicitly iterable, is not, when accessed from MAXScript.

I had to find out the hard way, so here I am sharing my findings and solutions so that you don’t have to waste time on this nonsense issue.

The problem I bumped into some time ago was when I tried to access Management Object Searchers in my code (for finding various HW IDs and serial numbers). When you call a dotNET iterable (or a collection) in MAXScript, you automatically assume you can iterate over its contents, just like with an array, for example.

WRONG!

Somebody at Autodesk decided that this isn’t the behavior you’d like and instead made you having to jump through hoops and obstacles to get to the object contents.

Anyways, lets look at this problem on a rather simple example of using Hashtables in MAXScript:

hsh = dotNetObject "System.Collections.Hashtable"


This will create a dotNET object, Hashtable, in MAXScript, so you can start adding Key:Value pairs to it. Which is the closest thing to Python’s Dictionary objects I’ve found. Very helpful.

Let’s add some data to the Hashtable for our demonstration purposes:

hsh.Add 1 "test"
hsh.Add "foo" "bar"


Now this data type is rather simple, it stores Key:Value pairs, so you can later call them similarly to calling array items: hsh.Item[KEY] and it will return the VALUE associated with the KEY.

hsh.Item[1]
"test"


hsh.Item["foo"]
"bar"


Brilliant!

But, what if you don’t know all the Keys, or you want to dump all the Values and the Keys into an array, or do something else with the data. Naturally, the first thing I wrote was:

for o in hsh do print o.Key


Well, the friendly message you receive is as follows:

-- No "map" function for dotNetObject:System.Collections.Hashtable


Which is essentially saying FUCK YOU! You cannot iterate over a collection!

But why? Why couldn’t I iterate over an obvious collection/iterable? I have no fucking idea!

So then the real question was, how the hell do I get the data out of the object if I don’t know the KEYs? How do I even get all the KEYs of the Hashtable?

The, not so obvious, answer was Enumeration. Simply put, you need to get an Enumerator object of the Collection and iterate on that in order to retreive the data in MAXScript. It’s cumbersome, nonintuitive and unnecessary, but that’s the only way I’ve found that works reliably.

In order to do just that, call the .GetEnumerator() method on the iterable object and store it in a variable. Then use that variable to “walk” through the Enumerator and get its data, like this:

hshEnum = hsh.GetEnumerator()
while hshEnum.MoveNext() do print hshEnum.Key


After running this code, you’ll get:

1
"foo"


Which you can then use later in your code to retreive the Value associated with these Keys.

Dumb, isn’t it? I spent hours looking on forums, searching Google and asking around to figure this out. It took me this long since in IronPython I can, naturally, iterate over collections and iterables without jumping through Enumeration hoops. Here’s the same program, except written in in IronPython:

from System.Collections import Hashtable as hsh
hsh = hsh()
hsh.Add(1, "test")
hsh.Add("foo", "bar")
for o in hsh: print o.Key


DONE! How smooth, simple, easy and intuitive is that?!

6 Comments »

  1. That’s really cool, thank you.

    Comment by Sergo — May 15, 2013 @ 02:50

  2. Oh man! I tried for hours to figure this one out. And failed. I stumbled over this blog post accidentally.
    Thanks so much for documenting this.

    Comment by Rhys — July 11, 2013 @ 06:20

  3. Sweet! Note that your enumerator varies based on the type of List/Dictionnary/Array.

    For instance for an object collection, the format is:

    colEnum = col.GetEnumerator()
    while ( col.MoveNext() ) do ( print colEnum.Current )

    Hope this helps!

    Comment by CL Audio — August 19, 2013 @ 02:32

  4. Hey, CL, thanks for the tip.

    Comment by loocas — August 31, 2013 @ 18:47

  5. Thanks! Got stuck on this as well, and after several hours of pain got here on the first hit. Many thanks for saving me additional wasted hours.

    ..and to Autodesk, thanks for nothing!

    Comment by Daniel — February 9, 2014 @ 13:38

  6. You could wrap the DotNet HashTable and some useful fns in a MXS struct.

    struct Hash
    (
    Table= dotNetObject “System.Collections.Hashtable”,
    Keys=#(),
    Values=#(),
    fn add key val=(this.Table.Add key val),
    mapped fn addKey key=(this.Table.Add key undefined),

    fn getKeys=(
    enumerator=this.Table.GetEnumerator()
    while enumerator.MoveNext() do (
    append this.keys enumerator.Key
    )
    this.Keys
    ),

    fn getValues=(
    enumerator=this.Table.GetEnumerator()
    while enumerator.MoveNext() do (
    append this.Values this.Table.Item[enumerator.Key]
    )
    this.Values
    ),

    fn ToString=(
    format”%:\n” This.Table.ToString()
    enumerator=this.Table.GetEnumerator()
    while enumerator.MoveNext() do (
    format”\t’%’:’%’\n” enumerator.Key this.Table.Item[enumerator.Key]
    )
    )
    –….etc
    )

    Comment by Mambo4 — October 8, 2014 @ 17:57

RSS feed for comments on this post. TrackBack URI

Leave a comment

Powered by WordPress | Theme by Roy Tanck