ForEach
A function added by the New Vegas Script Extender.
Description
Used to repeat a script block for each entry in a collection. The collection may be either a container, array or string.
Since xNVSE 6.2.5, ForEach can also be used to iterate directly over a FormList; the past method was to convert the formlist to an array using GetListForms.
ForEach loops should only be used with stringmap arrays due to taking lot of overhead, since it is necessary to create a temp associative single-pair stringmap array for each of the iterations. Use while loops for arrays with indexes instead to avoid this extra overhead. NOTE: since xNVSE v6+, ForEach now only performs slightly worse than While, so it is far more acceptable to use.
Within foreach loops, the command continue may be used to skip any remaining loop code for that entry, and move on to the next. In the same context, the command break may be used to end the loop immediately, ignoring any remaining code and entries.
"<-" is part of the ForEach expression and is synonymous with the "in" operator in python.
Syntax
ForEach
Syntax and Usage
In the case of a container, an entry is a temporary reference to an item in its inventory. This means it may be used as a calling reference, but becomes invalid once the loop ends. (See also: GetInvRefsForItem, CopyIR, RemoveMeIR, IsEquipped).
ref Item ref Container foreach Item:tempref <- Container:ref ; Item is a temporary reference to an item in the container loop
For an array, each entry is a stringmap with two fields, "key" and "value". The key is the index of the entry in the collection (0, 1, 2... for regular list arrays). (Note: you can replace 'entry["value"]' with the shorthand '*entry', see NVSE Expressions)
array_var Entry array_var Collection foreach Entry:array <- Collection:array ; Entry["key"] is the key (index) of each entry (0, 1, 2... for lists) ; Entry["value"] is the value of each entry in the array loop
For a string, each entry is a string containing a single character.
string_var Char string_var Source foreach Char:string <- Source:string ; Char is a single character in String loop
For a FormList source, each entry is a form.
ref rForm ForEach rForm <- ExampleFormList ; rForm is the nth form in the ExampleFormList loop
Example
ref rItem ref rActor foreach rItem <- rActor if rItem.IsEquipped rItem.UnequipMe endif loop ; Will unequip all equipped items from rActor
array_var Beatles array_var Entry ref rMusician int iPosition let Beatles := ar_List JohnREF, PaulREF, GeorgeREF, RingoREF foreach Entry <- Beatles let iPosition := Entry["key"] let rMusician := Entry["value"] Print "Entry #" + $iPosition + " is " + $rMusician loop ; Will print in game: ; Entry #0 is John Lennon ; Entry #1 is Paul McCartney ; Entry #2 is George Harrison ; Entry #3 is Ringo Starr
Using continue:
foreach Entry <- Beatles let rMusician := Entry["value"] if rMusician.GetDead continue ; * Go direct to next entry: we ignore dead members endif rMusician.AddItem Beer, 1 loop ; Every living member of the Beatles is given a beer
Using break:
foreach Entry <- Beatles let rMusician := Entry["value"] if rMusician.GetInWorldSpace Liverpool rMusician.AddItem Beer, 1 else break ; * End Loop immediately if we find a member is not in Liverpool endif loop ; Give a beer to each member of the Beatles until one is found not to be in Liverpool- assume that all Beatles except Paul are in Liverpool; since Paul is the second entry of the array, only John, the first, gets a beer.
You can also use the Ar_Range command to approximate the traditional 'C' style for loop. The code below prints the numbers 0-10:
foreach Entry <- (Ar_Range 0, 10) Print $Entry["value"] loop
Note that above we reference Entry["value"] in the Print function directly, rather than use an intermediary variable (let SomeVar := Entry["value"]..). This is only possible when using NVSE aware functions or the Script Compiler Override.
Notes
- When you use Foreach to loop through an array type, using Ar_Erase on the key that is currently being iterated, while inside the loop, will cause issues. If you're looping through a regular array, consider using a backwards While loop instead. For maps and stringmaps, you can either append the keys that need removing to a separate array first and do the erasing there afterwards:
let aSomeArray := ar_construct "array" foreach entry <- aSomeMap if somecondition ar_append aSomeArray, entry["key"] endif loop let iKey := ar_size aSomeArray while -1 < (iKey -= 1) ar_erase aSomeMap, aSomeArray[iKey] loop
or use the foreach loop to populate a new map or stringmap that excludes the keys you need erased, and then replace the old one with the new one:
let aSomeMap2 := ar_construct "map" foreach entry <- aSomeMap if somecondition continue endif let aSomeMap2[entry["key"]] := entry["value"] loop let aSomeMap := aSomeMap2
- The above also holds true for erasing characters from a string when you're ForEach-ing through a string. Use similar techniques to avoid the issue.
See Also
- While
- Ar_ForEach
- ForEachInList
- Array Variables
- Let
- Eval
- Label/Goto
- GetListForms (converts a form list to an array, so you can use foreach with it if you can't use xNVSE 6.2.5 or greater)
- NVSE Expressions
External Links
- List of supported expression found in OBSE, these are equivalent in NVSE
- Tutorial on syntax and expressions in NVSE 4 (this link requires pre-setting user details for this site)