"For Each" vs. "for in"


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.

Comments (22)

  1. Blake says:

    Mmm, so JScript.NET is in scope for questions too?

  2. Eric Lippert says:

    Sure. I mean, I’m no Peter Torr, but I know a thing or two about it.

  3. 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

  4. Eric Lippert says:

    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.

  5. Jay says:

    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.

  6. Chris says:

    God bless you. This was my first stop to google for a solution.

  7. Eric Lippert says:

    You’re welcome; I live to serve. :-)

  8. Jason says:

    "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?

  9. Eric Lippert says:

    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?

  10. Jason says:

    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.

  11. Eric Lippert says:

    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.

  12. Jason says:

    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.

  13. Adam Ralph says:

    Thanks very much for this blog entry. It gave me exactly the info I was looking for.

  14. Till says:

    Many thanks for all of those informations.

    However, I’d like to go back to Jason’s question. I have similar needs, and I know now I can acheive it with JScript (thaks to you).

    Let say I have an object "MyObject" that have the properties "height", "width" and "depth".

    If I want to write a function that addresses only one of these properties at a time, a JScript code would look like :

    function treatDimension(selDim)

    {

    <action on > MyObject[selDim]

    }

    how can I write such a code in VBScript ?

    buy now, one solution is something like :

    Function treatDimension ( selDim)

       Select Case selDim

            Case "height"

                    <action on > MyObject.height

            Case "width"

                    <action on > MyObject.width

            Case "depth"

                    <action on > MyObject.depth

       End Select

    End Function

    of course, I’d like to avoid such a "select case" statement, since the actual code might grow much more complicated.

    I’m desperately browsing the web for such a solution, but I have hard time to find it yet …

    Hope you have an answer to it …

  15. EricLippert says:

    The switch statement version is pretty much the best you can do in VBScript.  There is a gross way to do what you want which is

    function treatDimension ( selDim)

          Execute "<action on > MyObject." & selDim

    End Function

    However, this is incredibly dangerous if you cannot guarantee that the caller is only going to pass in sensible strings.  If selDim happens to be "height: codetodeleteyourharddisk()"  then that’s what’s going to run.  I would avoid such a situation if at all possible and stick with the safe, easy-to-understand select statement if you have to do this in VBScript.

  16. Till says:

    Many thanks for your answer I’ll try it anyway.

    Why is there such a difference in both languages ?

    It happens so many time, for me, that I needed to "parametrize" the name of methods or proprieties.

    In fact, as long as I understand it clearly, what I’d like to have is a sort of "scripting layer" that would resolve property names only at run time, which is somewhat what your "execute" tip is doing.

    I was more looking for a syntaxic approach, with escape characters or whatever (similar to JScript).

    <action on> MyObject.[`selDim`] for instance, or else

    Do you think we can expect such enhancements in the future ?

  17. EricLippert says:

    > Why is there such a difference in both languages ?  

    VBScript and JavaScript were invented by different people at different companies at different times to solve different problems, so it should not be surprising that the languages are in many ways different.

    > Do you think we can expect such enhancements in the future ?

    No new features have been added to VBScript in about six years now, so I wouldn’t hold my breath waiting if I were you!

  18. Till says:

    Hmmm.

    > VBScript and JavaScript were invented by different people at different companies at different times to solve different problems, so it should not be surprising that the languages are in many ways different.

    Of course.

    My guess is that they both try to finally cover user’s needs that can be araised during their use.

    Don’t you think those needs make any sense ?

  19. Gary D. says:

    I have read and re-read this blog entry, trying to make sense of the first line of the 4th to the last paragraph. It reads "In JScript to enumerate properties of an object, use the Enumerator object…".  Should it not read "In JScript to enumerate members of a collection, use the Enumerator object…".

    A few lines earlier you wrote  "In JScript, [the "]for in[" syntax] enumerates the properties of an object. " which would make the enumerator object unneeded for that purpose. The problem that got the blog entry started was people used to using vb trying to use "for in" in jscript to enumerate members of a collection so it would seem logical that this code snippet would address their need.  If I am correct, do you have the ability to fix the blog and delete this comment?

    I always hate being confused by something and then having to search the comments to see if it has been adressed or not.  It would be much easier to just revise the content when the issue was discovered.

  20. EricLippert says:

    Your analysis is correct. I’ve fixed the error. Thanks!