For-loop [AHK_L 59+]

Repeats a series of commands once for each key-value pair in an object.

For Key [, Value] in Expression

Parameters

Key

Name of the variable in which to store the key at the beginning of each iteration.

Value

Name of the variable in which to store the value associated with the current key.

Expression

An expression which results in an object, or a variable which contains an object.

Remarks

Expression is evaluated only once, before the loop begins. If its result is not an object, execution jumps immediately to the line following the loop's body; otherwise, the object's _NewEnum() method is called to retrieve an enumerator object. At the beginning of each iteration, the enumerator's Next() method is used to retrieve the next key-value pair. If Next() returns zero or an empty string, the loop terminates.

Although not exactly equivalent to a for-loop, the following demonstrates this process:

_enum := (Expression)._NewEnum()
if IsObject(_enum)
    while _enum.Next(Key, Value)
    {
        ...
    }

Existing key-value pairs may be modified during the loop, but inserting or removing keys may cause some items to be skipped or enumerated multiple times. One workaround is to build a list of keys to remove, then use a second loop to remove the keys after the first loop completes. Note that Object.Remove(first, last) can be used to remove a range of keys without looping.

A for-loop is usually followed by a block, which is a collection of statements that form the body of the loop. However, a loop with only a single statement does not require a block (an "if" and its "else" count as a single statement for this purpose). The One True Brace (OTB) style may optionally be used, which allows the open-brace to appear on the same line rather than underneath. For example: for x, y in z {.

As with all loops, Break, Continue and A_Index may be used.

COM Objects

Since Key and Value are passed directly to the enumerator's Next() method, the values they are assigned depends on what type of object is being enumerated. For COM objects, Key contains the value returned by IEnumVARIANT::Next() and Value contains a number which represents its variant type. For example, when used with a Scripting.Dictionary object, each Key contains a key from the dictionary and Value is typically 8 for strings and 3 for integers. See ComObjType for a list of type codes.

[v1.0.96.00+]: When enumerating a SafeArray, Key contains the current element and Value contains its variant type.

Related

Enumerator object, Object.NewEnum(), While-loop, Loop, Until, Break, Continue, Blocks

Examples

; List the key-value pairs of an object:
colours := Object("red", 0xFF0000, "blue", 0x0000FF, "green", 0x00FF00)
; The above expression could be used directly in place of "colours" below:
for k, v in colours
    s .= k "=" v "`n"
MsgBox % s
; List all open Explorer and Internet Explorer windows:
for window in ComObjCreate("Shell.Application").Windows
    windows .= window.LocationName " :: " window.LocationURL "`n"
MsgBox % windows
/*
Class: CEnumerator

Generic enumerator object that can be used for iterating over numeric keys.
The array must not be modified during iteration, otherwise the iterated range will be invalid.
It's possible to define a custom MaxIndex() functions for array boundaries.
If there are missing array members between 1 and max index, they will be iterated but will have a value of "".
This means that real sparse arrays are not supported by this enumerator by design.
To make an object use this iterator, insert this function in the class definition:

    _NewEnum()
    {
    	return new CEnumerator(this)
    }

Source: http://www.autohotkey.com/board/topic/2667-suggestions-on-documentation-improvements/?p=531509
*/

; Iterate over the enumerator
For k, v in Test
	MsgBox %k%=%v%

; Test class for demonstrating usage
class Test
{
	static Data := ["abc", "def", "ghi"]

	_NewEnum()
	{
		return new CEnumerator(this.Data)
	}
}

class CEnumerator
{
	__New(Object)
	{
		this.Object := Object
		this.first := true
		; Cache for speed. Useful if custom MaxIndex() functions have poor performance.
		; In return, that means that no key-value pairs may be inserted during iteration or the range will become invalid.
		this.ObjMaxIndex := Object.MaxIndex()
	}

	Next(ByRef key, ByRef value)
	{
		if (this.first)
		{
			this.Remove("first")
			key := 1
		}
		else
			key ++
		if (key <= this.ObjMaxIndex)
			value := this.Object[key]
		else
			key := ""
		return key != ""
	}
}