Introduction to NVSE arrays

From GECK
Jump to: navigation, search

Foreword

For a brief overview of Array Variables, see Array Variable.

For a more in-depth tutorial for arrays, see Tutorial: Array Variables.

This introduction was adapted from Introduction to OBSE arrays (CSWiki).

Much of the information for OBSE arrays still holds true for NVSE arrays - as such, this tutorial may quote the OBSE documents.


Arrays

An array is a collection of related information put together in a list-like structure.

Being a list, an array may hold many values, as opposed to regular script variables that hold only one information.

Some examples of everyday lists that could be stored in arrays:

Shopping list Agenda Personal data
- Bread 08:30 - Sign in, Coffe Name: Easy Pete
- Butter 09:00 - Introduction Occupation: Retired Prospector
- Milk 09:30 - Presentation A Location: Goodsprings
- Eggs 10:30 - Coffe break Task: Taking it easy
11:00 - Presentation B Race: African American
ReferenceID: EasyPeteRef


In NVSE, arrays are accessible as a new script variable type.

In the same way that a number can be represented by the Int variable type, arrays are declared as array_vars:

    array_var MyArray

Just like any other script variable, the contents of array_vars are save-baked, the only difference being that they are saved in an NVSE cosave.


Array keys and values

Each element of the array is composed of a Key and a Value.

Key
The Key identifies the element and must be unique within an array.
An array key may be a number or a string, but all the keys in an array must be of the same type.
Arrays are ordered by key, in ascending order.
The key is represented within brackets, e.g., MyArray[0].
Value
Value is the information stored in the array element.
The value may be of any type: a string, a number, a FormID (base object or reference) or another array.
Any mix of value types may be stored in the same array.


Array types

There are three types of arrays, depending on the key used: Array, Map or StringMap.

Array

The "Array" type of array uses integers as keys.

The first element must have key = 0 and subsequent keys must be the next integer: 1, 2, 3, 4 etc.

There must be no gaps in the key sequence.

If an element is inserted or removed from the array, the keys are renumbered to comply with the above rules.

The Shopping List example would look like this when stored in an Array:

Key Value
0 Bread
1 Butter
2 Milk
3 Eggs


Quote from the OBSE doc:
1. Array: An Array behaves like arrays in most programming languages: the key is an unsigned integer starting at zero, and there are no gaps between elements. (In other words, if an element exists at indexes 1 and 3 then an element necessarily exists at 0 and 2). Attempting to access an element using a key which is greater than the highest key in the array results in an error. The only exception to this rule is during assignment: it is okay to assign a value to the key which is one greater than the highest key in the array.


Map

The Map array uses numbers as keys.

Keys may be any number, including negative and floating point numbers

The Agenda example would look like this when stored in a Map array:

Key Value
8.5 Sign in, Coffe
9.0 Introduction
9.5 Presentation A
10.5 Coffe break
11.0 Presentation B
Quote from the OBSE doc:
2. Map: A Map associates numeric keys with values. Unlike an Array, a Map allows negative and floating point numbers to be used as keys and allows gaps to exist between elements.

StringMap

The StringMap array uses strings as keys.

The Personal Data example would look like this when stored in an StringMap array:

Key Value Value Type
"Name" "Easy Pete" String
"Occupation:" "Retired Prospector" String
"#Dymanite" 2 Int
"Unarmed Damage" 1.10 Float
"Location" "Goodsprings" String
"Race" AfricanAmericanOldAged Ref
"ReferenceID" EasyPeteRef Ref
"BaseFormID" GSEasyPete Ref


  • Note: Although only this last example has a mixture of value types, arrays of ANY type may have mixed value types.


Quote from the OBSE doc:
3. StringMap: Like a Map, except the keys are strings. Keys are case-insensitive, so array["INDEX"] and array["index"] both refer to the same value. There is no practical limit on the length of the strings used as keys. StringMaps can be used to simulate C-style structs by associating named properties with data values.


Declaring and initializing arrays

Arrays must be declared as an array_var variable, e.g., "array_var MyArray"

An array_var must be initialized before it can be used in expressions, either by


1. Explicitly initializing it using ar_Construct

When initializing an array with ar_Construct, the type of array must be provided as the argument:
      let MyArray := ar_Construct "array"
      let MyArray := ar_Construct "map"
      let MyArray := ar_Construct "stringmap"

2. Assigning the value of another array_var to it

In this case MyArray will be of the same type of SomeArray (actually, both refer to the same array)
      let SomeArray := ar_Construct "array"
      Let MyArray := SomeArray


3. Assigning it the return value of a command returning an array, such as GetAllItems:

      Let MyArray := player.GetAllItems

...Or Ar_List:

      Let MyArray := Ar_List 1, 2, 3, 4, "I declare a thumb war"

Populating arrays

Arrays are populated by assigning values to its elements.

An array element is identified by the array name followed by the element key within brackets.

The keys, of course, must comply with the type of array:

    let MyArray := ar_Construct "array"
    Let MyArray[0] := 123
    Let MyArray[1] := 456
    Let MyArray[2] := 789
    let MyArray := ar_Construct "map"
    Let MyArray[-1.5] := 123
    Let MyArray[2.5] := 456
    Let MyArray[3] := 789
    let MyArray := ar_Construct "stringmap"
    Let MyArray["Cost"] := 123
    Let MyArray["Qty"] := 456
    Let MyArray["Taxes"] := 789

Accessing array elements

Using the data stored in arrays usually requires that you copy the information into a 'normal' script variable because array elements cannot be passed directly to most commands as arguments.


The Let statement is array-aware and is the most common way of accessing array elements

    float MyFloat

    Let MyFloat := MyArray["PosX"] 
    Player.setpos x MyFloat
    ref MyRef

    Let MyRef := MyArray["ReferenceID"] 
    MyRef.Kill
    string_var MyString

    Let MyString := MyArray[3.7]
    MessageBoxEx "The element text is: %z" MyString


In all examples up to here, keys were explicitly coded, but they may also be in a variable and the variable itself going within the brackets:

    string_var MyString

    Let MyString := "Item value"
    Let MyArray[MyString] := 300
    short MyShort

    Let MyShort := 3
    Let MyArray[MyShort] := "Text"

Which is very handy for . . .


Walking an array

Walking an array means going over each element of the array, one at a time.

NVSE provides functions to do exactly that: ForEach and While loops


ForEach

ForEach loops iterate over the elements of an array.

    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

At each iteration, "item" is initialized with two elements:

  • "key", which holds the key of the current element
  • "value", which holds the value associated with that key

Therefore, as in the example above, within a ForEach loop you can access both fields via item["key"] and item["value"].


While

While loops are handy for handling Arrays because the keys are known in advance and are consecutive (0,1,2,3, ...)


The same example as above using a While loop:

    array_var MyArray
    short MyIndex
    string_var MyString

    Let MyIndex := 0
    While MyIndex < ar_size MyArray
      Let MyString := MyArray[MyIndex]
      MessageEx "The element %g  is: %z" MyIndex MyString
      MyIndex += 1
    Loop

Notice that with while loops, you have to increase the index yourself, as opposed to ForEach loops that automatically go to the next element.

Notice also that `ar_size MyArray` is evaluated in every iteration of the loop. For smaller arrays this is fine, but for larger ones, it is recommended to store the size of the array in a variable before the loop.


A somewhat different (perhaps more elegant) way of increasing the index:

    short MyIndex
    int iSize
    string_var MyString

    Let MyIndex := -1
    Let iSize := ar_size MyArray
    While (MyIndex += 1) < iSize 
      Let MyString := MyArray[MyIndex]
      MessageEX "The element %g  is: %z" MyIndex MyString
    Loop


While loops also allows for walking Arrays backward:

    short MyIndex
    string_var MyString

    Let MyIndex := ar_size MyArray
    While (MyIndex -= 1) >= 0
      Let MyString := MyArray[MyIndex]
      MessageEx "The element %g  is: %z" MyIndex MyString
    Loop

This is useful in case certain iterated elements need to removed from the array mid-loop, without having to break the loop.


Uninitializing arrays

Array variables can have their contents reset via Ar_Null, setting them back to their pre-initialized state.

xNVSE v6+ keeps track of the number of references to each array and destroys the array when no references to it remain. This makes it unnecessary for scripts to worry about destroying arrays explicitly, unless the mod author intends to keep backwards compatibility with older versions of NVSE.

If the array is only meant to be used once and discarded, it is recommended to nullify it once its work is finished.

If an array_var has its contents replaced by another array_var, its past contents will be properly destroyed. This means that in the example below, using Ar_Null is unnecessary:

    array_var aArray

    let aArray := GetActorsByProcessingLevel 0
    ;(do things with aArray)

    let aArray := ar_Null  ;unnecessary
    let aArray := GetActorsByProcessingLevel 1


Some final, miscellaneous notes

  • All elements within an array must have the same type of key.
  • References cannot be used as array keys. If this is needed, look into RefMap Arrays (which are NOT array_vars).
  • An array can contain any mix of types for its values.
  • As array elements may contain any type of data, it is the script's responsibility to know which is which, so you don't try to assign a string to a ref variable. TypeOf may be used to verify the type of an element.
  • ForEach and While loops both define structured blocks in the same way that If and Endif or Begin and End do. Every While or ForEach in a script must be matched by exactly one Loop command.
  • Category:Array Functions has a list of every NVSE and NVSE-plugin array functions.