Tutorial: Array Variables 3
This is is the third part of a tutorial on NVSE Array Variables, the second part can be found here.
Walking Arrays
If one wishes to create an array with more than 20 elements, or does not know how many elements may be required, several techniques are possible. By walking an array, one can select each element in turn and do something with it.
Suppose one has two arrays and wishes to merge them. With regular arrays, because the keys are consecutive integers, we could use the Ar_Size function (returns total elements stored) and use that to build a While loop in which we add each value held in array2 to array1 with the Ar_Append function (adds a new element to the end):
let array1 := ar_list value1, value2, value3, value4, ... value20 let array2 := ar_list value1, value2, value3, value4, ... value20 ; * the values could be anything - in docs, that is called a 'multi' type let iSize := ar_size array2 ; * we could skip this step let iNum := -1 while (iNum += 1) < iSize ; * the lowest index is 0 and the highest possible index is total size - 1 let someVar := array2[iNum] ar_append array1 someVar loop
And that'll work fine, as long as each value in array2 is the same type (forms, numbers, strings or arrays) so that we know what 'someVar' is supposed to be declared as. The trouble is that arrays can hold any combination of such values. Strictly speaking, we could use the handy TypeOf function to let us know the type of value in the form of a string:
let iNum := -1 while (iNum += 1) < (ar_size array2) let sv_somestring := TypeOf array2[iNum] ; * TypeOf determines the type of the value under array2[iNum] and ; * passes it to our string var if eval sv_somestring == "Form" ; * TypeOf returns "Form" for forms and refs let rRefVar := array2[iNum] ar_append array1 rRefVar continue ; * we got what we need, let us skip the rest of the body and ; * move on with our loop elseif eval sv_somestring == "Number" ; * TypeOf returns "Number" for floats and ints let fFloatVar := array2[iNum] ar_append array1 fFloatVar continue elseif eval sv_somestring == "String" ; * TypeOf returns "String" for strings let sv_StringVar := array2[iNum] ar_append array1 svStringVar continue else ; * TypeOf returns either "Array", "Map" or "StringMap" for arrays - in this ; * case we just need to catch them all let ar_ArrayVar := array2[iNum] ; * remember this makes ar_ArrayVar be a _reference_ to the array ; * under array2[iNum], not a copy ar_append array1 ar_ArrayVar continue endif loop
But that's a rather roundabout way isn't it, and either way a while loop won't work to walk maps (gaps, floats) or stringmaps (string keys). Enter the foreach loop. (The Ar_InsertRange function is also useful here, but will be covered later)
Foreach Loops
Foreach loops select each element of a collection type in a forwards loop, and like while - loop you need to see foreach - loop as a block. For strings they select each character, for containers each inventory reference, and for any type of array they select each array element. As with while loops, you interrupt a foreach loop with the "Break" command, and skip an instance of the loop body with the "Continue" command.
When you use a foreach loop on an array, it'll copy each element to a stringmap- the iterator, that you need to declare - the key of the element being inspected will be found under SomeStringMap["key"], the value under SomeStringMap["value"]:
["key"] : the key of the element ["value"] : the value of the element
The iterator, ie the temporary stringmap, will only ever have those 2 elements in it: "key"::KeyOfTheInspectedElement and "value"::ValueOfTheInspectedElement. You don't need to initialize it - the foreach command does that - and when the loop is done it is immediately nullified/uninitialized.
array_var arraytowalk array_var tempstringmap ; * for iterators, I like to use 'entry' or 'ar_entry' as the variable name, like you're leafing through an encyclopaedia foreach tempstringmap <- arraytowalk ; * you need that arrow-like symbol there ; check out or do something with tempstringmap["key"] ; and/or tempstringmap["value"] loop
so that earlier lengthy while loop could become this short foreach loop:
array_var array1 array_var array2 array_var ar_entry let array1 := ar_List Value1, Value2, ... Value20 let array2 := ar_List someRef, someFloat, "someString", someStringVar, someArray, ... Value20 foreach ar_entry <- array2 ar_append array1 ar_entry["value"] ; * no matter what type each value in array2 is, we can refer to each one ; * with ar_entry["value"] loop
An example with our map that's holding some exterior cells by their X coordinates: let's say we want to split it up into a map holding western cells (-X) and one with eastern cells (+X).
array_var CellMap array_var CellMapWest array_var CellMapEast array_var entry float fXPos let CellMap := ar_Map -21::MojaveOutPost -9::MojaveDriveIn 7::188TradingPost 12::BoulderCity 17::BitterSprings let CellMapWest := ar_construct Map let CellMapEast := ar_construct Map foreach entry <- CellMap let fXPos := entry["key"] ; * passing the key, the X position, to a float if 0 > fXPos ; * float is negative, so the cell is west let CellMapWest[fXPos] := entry["value"] ; * storing the cell - entry["value"] - under the fXPos key in the West map else let CellMapEast[fXPos] := entry["value"] endif loop
But do we really need the variable, fXPos? No.
foreach entry <- CellMap if eval 0 > entry["key"] let CellMapWest[entry["key"]] := entry["value"] else let CellMapEast[entry["key"]] := entry["value"] endif loop
Part 4: Overview of available functions