Compartilhar via


"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

  • Anonymous
    September 22, 2003
    Mmm, so JScript.NET is in scope for questions too?
  • Anonymous
    September 22, 2003
    Sure. I mean, I'm no Peter Torr, but I know a thing or two about it.
  • Anonymous
    September 22, 2003
    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
  • Anonymous
    September 23, 2003
    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.
  • Anonymous
    November 04, 2003
    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.
  • Anonymous
    March 31, 2004
    The comment has been removed
  • Anonymous
    March 31, 2004
    You're welcome; I live to serve. :-)
  • Anonymous
    April 01, 2004
    The comment has been removed
  • Anonymous
    April 01, 2004
    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?
  • Anonymous
    April 01, 2004
    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.

  • Anonymous
    April 01, 2004
    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.
  • Anonymous
    April 01, 2004
    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.
  • Anonymous
    October 20, 2005
    Thanks very much for this blog entry. It gave me exactly the info I was looking for.
  • Anonymous
    March 19, 2006
    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 ...
  • Anonymous
    March 21, 2006
    The comment has been removed
  • Anonymous
    March 21, 2006
    The comment has been removed
  • Anonymous
    March 23, 2006
    > 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!
  • Anonymous
    March 23, 2006
    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 ?
  • Anonymous
    December 20, 2007
    The comment has been removed
  • Anonymous
    December 20, 2007
    Your analysis is correct. I've fixed the error. Thanks!