Assaf Lavie
Assaf Lavie

Reputation: 76143

weird behavior in Flex/as3 for-each loop

The original code is a bit complex, but it boils down to something like this:

for each (var person : Person in someArrayList.toArray())
{
    for each (var friend : Person in person.friendArrayList.toArray())
    {
        trace(person.name + " is friends with " + friend.name);
    }
}

There are 3 persons:

  1. A is friends with B
  2. B is friends with C.

And friendship is not transitive. That it, A is not friends with C.

For some reason, what this prints is:

A is friends with B
A is friends with C
B is friends with C

Now, if I modify the code above to use a temporary variable the printout is correct.

for each (var person : Person in someArrayList.toArray())
{
    var friends : Array = person.friendArrayList.toArray(); // temp
    for each (var friend : Person in friends)
    {
        trace(person.name + " is friends with " + friend.name);
    }
}


A is friends with B
B is friends with C

Is this by design? Am I losing my mind? What gives?

Upvotes: 2

Views: 733

Answers (3)

bug-a-lot
bug-a-lot

Reputation: 2454

When dealing with oddities like this, there are several ways to detect what's wrong. First one and the fastest would be debugging with a breakpoint before problematic piece of code, and then executing the code step by step.

Second way is to eliminate some of the code, either by creating a new minimized version of the current code, or by commenting some of the code.

Here is what I've tried: The application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
    creationComplete="onCreationComplete()"
    >
    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import weird.Person;
            private function onCreationComplete() : void
            {
                var a : Person = new Person();
                a.name = "A";
                var b : Person = new Person();
                b.name = "B";
                var c : Person = new Person();
                c.name = "C";

                a.friendArrayList.addItem(b);
                b.friendArrayList.addItem(c);

                var someArrayList : ArrayCollection = new ArrayCollection([a,b,c]);

                for each (var person : Person in someArrayList.toArray())
                {
                    for each (var friend : Person in person.friendArrayList.toArray())
                    {
                        trace(person.name + " is friends with " + friend.name);
                    }
                }
            }
        ]]>
    </mx:Script>
</mx:Application>

And the Person class:

package weird
{
    import mx.collections.ArrayCollection;

    public class Person
    {
        public var name : String = null;
        public var friendArrayList : ArrayCollection = new ArrayCollection();
    }
}

If you run the application you'll see that it executes as expected. Which means you're doing something else in your code that screws up with the iteration in one of the loops.

So if you don't figure out what is broken in your code, it would be interesting (for me at least) to see the rest of it.

Upvotes: 1

Aaron
Aaron

Reputation: 177

The for each..in statement iterates only through the dynamic properties of an object, not the fixed properties. A fixed property is a property that is defined as part of a class definition. To use the for each..in statement with an instance of a user-defined class, you must declare the class with the dynamic attribute.

Upvotes: 0

Marty Pitt
Marty Pitt

Reputation: 29300

You mention that this is a trimmed down version of the code.

Does the actual code do anything to modify person.frinedArrayList.toArray()?

There is a subtle difference in the two versions of your code.

In the latter version where you use the temp variable, the value of person.friendArrayList.toArray() is only evaluated once.

However, in the original version, person.friendArrayList.toArray() is evaluated at the start of every cycle of the loop. It's possible that the the return value as it's looping is different, causing the behaviour you're seeing.

For this reason alone, the second version is preferable, as it's more performant.

However, I suspect the real issue lies in the code within the loop, and it's impact on the result of the toArray() call.

Upvotes: 0

Related Questions