Saturday 5 June 2010

Ordered enumeration

The other day while writing some WSH code (yes, this old, but really powerful, technology is still alive and well, and I guess it will take a very long time until it gets fully replaced by .Net and PowerShell), I came up with something that could be good to share.

I have different scripts each one using a different logInfo "data object" that is passed to a generic logToCSVFile method. It's no wonder that the method enumerates the data fields in the logInfo object and writes a line to the csv file.
So, I need to enumerate these data fields always in the same order (and also get the data fields name, so that each time I create a new log file I write those names as column headers).
I had a problem here:
Traversing a JavaScript object (in the end a dictionary) with for in does not guarantee the order in which items will be returned. Well, in practice it seems it does, but we can say it's an implementation detail, not an interface contract. So, in principle, I can't count on fields being returned in the same order as I added them. Also, it could happen that I would not always add them in the same order... so I definitely need something more...

In C# (and Python) setting the enumeration order would be that simple and elegant as using an iterator block (a generator in Python) letting the compiler generate the Enumerator object for us. (for obtaining the Fields order I had to do some extra code, see the source)



public class LogInfo:IEnumerable<String>

{

    public IEnumerator<String> GetEnumerator()

    {

        yield return this.User;

        yield return this.Computer;

        yield return this.Result;

    }

 

    IEnumerator IEnumerable.GetEnumerator()

    {

        return this.GetEnumerator();

    }

}



but in JavaScript I had to write some more code (well, JavaScript 1.8, implemented in last Firefox versions, provides generators, but that's rather unknown to the %systemroot%\System32\jscript.dll engine used by WSH...) In the end I came up with an object that can be iterated with 2 slightly different for-in constructions:



var orderedEnumerator = function(orderAr, source)

{

    this.order = orderAr;

    this.enumerable = source;

    this.curPos = -1;

};

orderedEnumerator.prototype.get = function(i)

{

    return this.enumerable[this.order[i]];

};

orderedEnumerator.prototype.count = function()

{

    return this.order.length;

};

orderedEnumerator.prototype.current = function()

{

    return this.enumerable[this.order[this.curPos]];

};

orderedEnumerator.prototype.moveNext = function()

{

    if (this.curPos < this.order.length-1)

    {

        this.curPos++;

        return true;

    }

    return false;

};



You can get the code here:

  • javascript (you can easily test it with cscript, rhino or firebug)

  • c# (I've added some stuff to get the "columns order" thing)

No comments:

Post a Comment