Tutorial: Array Variables 4
This is is the fourth part of a tutorial on NVSE Array Variables, which overviews the available functions related to them. The third part of the tutorial can be found here.
Dumping Arrays
Sometimes you'll want to test a bunch of things out in game, so you may want console readouts of what's in your arrays. You get them with the Ar_Dump function:
Ar_Dump someArrayVar
will print out:
** Dumping Array #1 ** Refs: 1 Owner 3D: DSTest36.esp [ 0.000000 ] : Value0 [ 1.000000 ] : Value1 etc.
- Stringmaps will obviously have strings between those square brackets, where the keys are printed.
- Refs: 1 means there is only one reference to this array, in this case the array var in the script I called the Ar_Dump function on.
- The owner is the mod that created the array, DSTest36.esp, and you also get its LO number.
- The array ID (#1 above)is the number assigned to the array when it is created. You can call an array dump by specifying the ID rather than calling it on an array var:
Ar_DumpID someIDInt
But know that as arrays are removed, being no longer referred to, their IDs get recycled. Ar_DumpID is mostly useful when you call it from the console; you just need to be sure of the ID number.
Both ar_dump functions work like PrintC; they don't depend on debugmode. It's really easy though to create a debugmode-dependent version in a UDF:
call DBArDump somearrayvar
scn DBArDump array_var a ; parameter Begin Function {a} if GetDebugMode ; or check some quest var int you set via MCM or whatever ar_dump a endif End
Actually reading your console readouts in the console can be a drag, so we have the handy Con_SCOF function that dumps everything that appears in your console to a text file in your root folder:
con_scof "somefilename.txt"
Finding Stuff
Ar_Size will return the size of the array as an int. -1 means it's not initialized yet, 0 that it is but it's empty.
let someInt := ar_size someArrayVar
Ar_Find will return the key for the value you wish to find.
let iSomeInt := ar_Find ElementToSearchFor ArrayToSearch let fSomeFloat := ar_Find ElementToSearchFor MapToSearch let sv_SomeStringVar := ar_find ElementToSearchFor StringMapToSearch
If the value's nowhere to be found, it'll return Ar_BadNumericIndex (-99999.0) for regular arrays and maps, and Ar_BadStringIndex (an empty string, length 0) for stringmaps.
Ar_HasKey will check if an element exists with the key you specify, returning a simple bool.
if ar_HasKey ArrayToSearch someKey ; found! endif
Note that if you have equivalent key-value elements (they're both numbers or both strings), it takes a bit less time/cpu power to find what you're looking for with Ar_HasKey than with Ar_Find, so if one side is something you think you'll need to search for more than the other, use them as keys.
Ar_Keys returns a regular array containing all the keys of the source array/map/stringmap:
let somearray := ar_Keys sourcearray
The following should be pretty handy with maps and stringmaps (entirely unnecessary with regular arrays though):
Ar_First returns the first key:
let someInt/Float/StringVar := ar_First SomeMapVar
Ar_Last returns the last key:
let someInt/Float/StringVar := ar_Last SomeMapVar
Ar_Next returns the next key from the one specified.
let someInt/Float/StringVar := ar_Next SomeMapVar, someKey
Ar_Prev returns the previous key from the one specified.
let someInt/Float/StringVar := ar_Prev SomeMapVar, someKey
Ar_Next and Ar_Prev return a 'bad index' value when they don't find anything. You need to request a 'bad index' to compare, using Ar_BadNumericIndex or Ar_BadStringIndex depending on your array type:
let fFloat := ar_Next SomeMapVar someFloatKey if eval fFloat != ar_BadNumericIndex ; do stuff, you've got a valid key there endif let svStringVar := ar_Prev someStringMapVar someStringKey if eval svStringVar != ar_BadStringIndex ; do stuff, you've got a valid key there endif
Copying
Ar_Copy copies the elements from one array to another:
let someArray2 := ar_Copy someArray1
If someArray1 in turn holds a few arrays as values, someArray2 will contain references to the same arrays, not copies. In order to copy sub-arrays 'cleanly', you use Ar_DeepCopy:
let someArray2 := ar_DeepCopy someArray1
I can't advise you to choose one over the other - this really depends on your mod structure, which type of scripts you're in (persistence-wise), and what you're trying to do, ie whether or not you want to allow changes you make to those subarrays via someArray1 affect those subarrays under someArray2. Your choice, just be clear on which does what.
Erasing Elements
Ar_Erase - fancy that it should be named this way - does the trick:
ar_erase YourArrayVar KeyOfTheElementToErase
It can also erase a range of elements if you specify that range in slice notation, with the first key and last key to erase separated by a colon:
ar_erase YourArrayVar FirstKey:LastKey
If you erase an element or a range of elements from a regular array, the elements with higher key numbers will shift down automatically.
Warning: If you erase an element from an array from within a Foreach loop when that element is the one under iteration, that will most likely break the loop. With regular arrays, it doesn't always, but it does if you for instance pass the array as parameter to a UDF from within the loop. It'll always break with maps and stringmaps. The thing to do here is do your erasing when the loop is done, by for instance adding elements to erase to a new array during the loop and erase them from the original afterwards, or add the elements to keep to a new array and let the old one be a copy of that one afterwards.
Part 5: Further Overview of available functions