« Xojo Developer Magazi… | Home | MBS Xojo Plugins, ver… »

For each loops in detail

Let's a look on the for-each loops in Xojo. They allow you to loop over a container data type very easily. That could be:

  • The entries in an array.
  • The keys in a Dictionary or the entries.
  • The FolderItems in a folder.
  • The rows in a Listbox or a RowSet.
  • The sessions, pages, controls, labels and views in the Web framework.
  • The characters or code points in a string.

To iterate over a container, it must implement the Iterable interface and return the new iterator object, which implements the Iterator interface. Xojo has over 20 classes built-in, that implement these interfaces.

Let's start with a container holding an array of integers and we fill it:

Var m As New MyContainer m.values.Add 1 m.values.Add 2 m.values.Add 3

Then we can just use for-each to lop over these entries:

// let Xojo manage iterator For Each Value As Integer In m System.DebugLog "Log: "+Value.ToString Next

Xojo does a few things behind the scenes and we can do the same ourselves. We request the iterator from the container and use a While Loop to go over the values. MoveNext will eventually return false when we reached the end and then our loop ends.

// let us manage the iterator ourselves Var it As Iterator = m.Iterator While it.MoveNext Var Value As Integer = it.Value System.DebugLog "Log: "+Value.ToString Wend

You may want to know how we create our own container to loop over. Let's start with a class, that implements the Iterable interface. That means we have an Iterator method, which returns the new Iterator. The iterator could be the same class or a different one. But it should keep a reference on the data itself and implement the Iterator interface. Our iterator has a constructor to take the object:

Class MyContainer Implements Iterable
Function Iterator() As Iterator // Part of the Iterable interface. Return New MyContainerIterator(Self) End Function
Property Values() As Integer
End Class

The iterator implementation needs to have a MoveNext and Value method. We implement these with an index moved forward by MoveNext. There we check if we finished and return false at the end. For the value method, we can just get the value from the underlaying array. The constructor takes the container and we reference the values array inside the iterator. By referencing the underlaying data source instead of the container itself, our iterator stays valid, even if the container would be destructed. The start index is zero as the for loop will do a MoveNext before asking for the first value. Take a look into the code:

Class MyContainerIterator Implements Iterator
Sub Constructor(container as MyContainer) // we keep a reference to the underlaying array Me.values = container.Values // move before first item, so MoveNext can go to the first one Me.index = -1 End Sub
Function MoveNext() As Boolean // Part of the Iterator interface. index = index + 1 return index <= values.LastIndex End Function
Function Value() As Variant // Part of the Iterator interface. Return values(index) End Function
Property index As Integer
Property values() As Integer
End Class

The whole thing with creating a variant here just for passing back through the interface doesn't happen if you use for-each on an array as the Xojo compiler optimizes this and makes it more efficient.

Enjoy for-each loops in your code and simplify the loops you have.

The biggest plugin in space...
23 02 25 - 09:38