"For Each" vs. "for in"

"For Each" vs. "for in"

  • Comments 22

While we're on the subject of semantic differences between seemingly similar syntaxes, let me just take this opportunity to quickly answer a frequently asked question: why doesn't for-in enumerate a collection?

A VB programmer is used to this printing out every item in a collection:

For Each Item In MyCollection
    Print Item
Next

VB programmers introduced to JScript always make this mistake:

for (var item in myCollection)
{
    print(item);
}

and they are always surprised when this prints out a bunch of unexpected strings, or perhaps nothing at all.

The difference is quite simple.  In VBScript, For Each enumerates the members of a collection. In JScript, for in enumerates the properties of an object. In JScript if you have

var foo = new Object();
foo.bar = 123;
foo.baz = 456;

then you can enumerate the properties:

for (var prop in foo)
    print (prop + " : " + foo[prop])

JScript needs such a control flow structure because it has expando objects and sparse arrays. You might not know all the properties of an object or members of an associative array. It's not like VBScript where objects have fixed members and arrays are indexed by dense integer tuples. VBScript doesn't need this ability, so it doesn't have it.

Incidentally, in order to implement the JScript for in loop we needed to extend the functionality exposed by a dispatch object. Hence IDispatchEx, which gives the caller the ability to enumerate dispids and go from dispid back to name.

In JScript to enumerate members of a collection, use the Enumerator object:

for (var enumerator = new Enumerator(myCollection) ; !enumerator.atEnd(); enumerator.moveNext())
{
    var item = enumerator.item(); 
    // ...

The reaction I get from people who have not seen object-oriented enumerators before is usually "yuck!" This is an unfortunate reaction, as enumerator objects are extremely powerful. Unlike lexical For Each loops, enumerators are first-class objects. You can take out multiple enumerators on a collection, store them, pass them around, recycle them, all kinds of good stuff.

The semantics of the for in loop in JScript .NET are kind of a hodgepodge of both styles, with several interesting extensions. First off, if you pass an enumerator object itself to the JScript .NET for in loop, we enumerate it. If the argument is a JScript object (or a primitive convertible to a JScript object) then we enumerate its properties as JScript Classic does. If it is a CLR array, we enumerate its first dimension indices. If it is a collection, we fetch an enumerator and enumerate that. Otherwise, you've passed a non-enumerable object and we throw an exception

UPDATE: The sequel to this entry can be found here.

  • Mmm, so JScript.NET is in scope for questions too?
  • Sure. I mean, I'm no Peter Torr, but I know a thing or two about it.
  • Eric..your article displays the common mistake done by most of the VB Programmers (includes me) when moved to JScript. I had a similar problem ;). I liked this article. Probs you can also write an article on the common mistakes done by the VB programmers migrating to VB .NET
  • Though I was for a time on the VB .NET design committee, and implemented a very small portion of the VB.NET IDE, I actually do not know much about the practical ins and outs of the language compared to some people around here! The page you want to read is my colleague Paul Vick's blog. Go to http://www.panopticoncentral.net -- he invites questions about the design of VB .NET. He can give you a lot better information than I can.
  • Instead of the for/in syntax, I've been using one that takes advantage of anonymous functions and closures: enumerate( collection, function( item ) { WScript.echo( item ); } ); Where: function enumerate( coll, f ) { for( var en = new Enumerator(coll); !en.atEnd(); en.moveNext() ) { f( coll.Item() ); } } The actual function also enumerates arrays & objects.
  • God bless you. This was my first stop to google for a solution.
  • You're welcome; I live to serve. :-)
  • "then you can enumerate the properties:

    for (var prop in foo)
    print (prop + " : " + foo[prop]) "


    ...So close to what I need. Is there a VBScript syntax that performs the same duty as the listed JScript syntax, i.e. enumerating the properties of an object when the property names are not known beforehand?
  • Which part of "VBScript doesn't need this ability, so it doesn't have it. " was unclear?

    :-)

    What's the scenario where you have an object in VBScript but don't know the properties?
  • When the reference materials aren't clear / are difficult to find.

    In particular, what I'm working on now is a tool to display group membership. We have a single-domain forest that trusts an external domain. Some of our groups contain users from this external domain.

    In code, I connect to one of our computers, enumerate the groups, and use Group.Members to enumerate the users in the group. Then, I display several properties for that user as follows:

    strComputer="."
    Set colGroups = GetObject("WinNT://" & strComputer & "")
    colGroups.Filter = Array("group")
    For Each objGroup In colGroups
    WScript.Echo objGroup.Name & " members:"
    For Each objUser in objGroup.Members
    If objUser.Class = "User" Then
    WScript.Echo objUser.Name & objUser.Fullname & objUser.Description
    End If
    Next
    Next

    I'm not certain what kind of object objGroup.Members is returning, so I don't _really_ know what properties/methods objUser supports. I would guess this kind of situation would come up fairly often, when you're working with someone else's objects.

    This code shows information for both users from the local domain and from the trusted domain, but I don't know of a clear way to display whether the user belongs to my domain or to the trusted domain. If I could get a list of properties and/or methods that are valid for my objUser, I'd at least have a clue where to begin.

  • The problem is that the object has to be specially written in order to determine what its properties are AT RUNTIME. JScript objects are written that way, most objects are not. That's why VBScript doesn't have this feature -- it is only useful for JScript objects and the browser object model.

    If you want to determine what "static" methods an object supports, you can write a C++ program that queries the type information, either by statically analyzing the type library on disk, or by calling the live object via IDispatch, obtaining its type information, and walking that.

    However, not every object is guaranteed to provide type information at run time.

    Stay tuned -- I may someday do a blog entry on how to dynamically and/or statically read type information about an object.
  • Thank you, I'll be anxiously awaiting it...it would be a real timesaver for those of us who can't always get access to good documentation.
  • Thanks very much for this blog entry. It gave me exactly the info I was looking for.
Page 1 of 2 (22 items) 12