Arrays in Version 2

Version 2 of AGK introduced some new features to arrays, in particular the ability to sort, insert, and pass by reference into functions.

Array Declaration and Resizing

To maintain backwards compatibility arrays can still be defined with DIM, but version 2 introduces a new method of defining arrays that will become particularly important when defining them in types. Note that both DIM arrays and new style arrays perform exactly the same after they are declared so any functions mentioned here will also work on DIM arrays. The new method is mainly for consistency. The new method is as follows

myArray as integer[5]

The keyword DIM is not used and the size is moved to the end of the declaration. Arrays declared like this can only be declared once, unlike DIM arrays which can be declared multiple times to resize the array. The prefered method of resizing an array is now:

myArray.length = newSize

Where newSize can be any expression that could be assigned to a variable. An array length can also be discovered by using it as an expression like so

print( myArray.length )

Note that because arrays start at index 0 and allow the size parameter as a valid index, an array defined as "DIM myArray[2]" or "myArray as integer[2]" would have 3 elements (0,1, and 2) but array.length will return "2" to reflect the size parameter that was used to declare it. Therefore an array of length 0 has one element and an empty array will return the length "-1".

For simplicity it may be beneficial to ignore index 0 at first, and assume that arrays start at index 1. This way the length parameter matches the number of assumed elements in the array and index 0 can be introduced as an additional element later. Some people used to use index 0 to store the length of the array for convenience, but with the new changes this is no longer necessary.

Inserting and Removing

Elements can now be inserted and removed from arrays which will increase or decrease their size accordingly. For example

myArray as integer[] // creates an empty array
myArray.insert(5) // insert 5 at the end of the array
myArray.insert(7) // array now has the elements 5,7
print( myArray.length ) // will display "1" meaning it has two elements
myArray.remove() // removes the 7
myArray.remove() // removes the 5
print( myArray.length ) // will display "-1" meaning the array is empty

If an array is declared with a size parameter then elements will be inserted after the last existing element, for example

myArray as integer[5] 
myArray.insert(15) // array now has the elements 0,0,0,0,0,0,15
print( myArray.length ) // will display "6" meaning it has seven elements
myArray.remove() // removes the 15
myArray.remove() // removes the last zero
print( myArray.length ) // will display "4"

Elements can also be inserted at specific points in the array by adding an index parameter to the insert or remove command like so

myArray as integer[4] 
myArray[0] = 10
myArray[1] = 11
myArray[2] = 12
myArray[3] = 13
myArray[4] = 14
myArray.insert(15,2) // insert 15 at the second index of the array
print ( myArray[2] ) // will print "15"
// the array curently looks like this [10,11,15,12,13,14]
myArray.remove(0) // removes the element at index 0
// the array now looks like this [11,15,12,13,14]
myArray.remove(3) // removes the element at index 3
// the array now looks like this [11,15,12,14]

Sorting and Searching

Arrays can now be sorted using the .sort() command and searched using the .find(item) command. For example

myArray as integer[5] = [3,4,1,5,2,6]
myArray.sort() // array is now [1,2,3,4,5,6]
index = myArray.find(4) // will return "3" as array indexes start at 0

.sort() will always sort in ascending order and .find(item) will only work on arrays that are in ascending order. If .find(item) cannot find the item you are looking for it will return "-1". You can sort arrays of Integers, Float, Strings, and Types. When sorting types the first variable of the type will be used to compare elements.

type myType
    ID as integer
    name as string
endtype
myArray as myType[3]
myArray[0].ID = 5 : myArray[0].name = "Bob"
myArray[1].ID = 4 : myArray[1].name = "Alice"
myArray[2].ID = 1 : myArray[2].name = "Carol"
myArray[3].ID = 2 : myArray[3].name = "David"
myArray.sort()
remstart
    the array now looks like this
    [0]: ID=1, name="Carol"
    [1]: ID=2, name="David"
    [2]: ID=4, name="Alice"
    [3]: ID=5, name="Bob"
remend

There is also a special insert command named .insertsorted(item) that will insert into a sorted array and maintain the array's ascending order so it can still be searched. For example, taking the array above

myItem as MyType
myItem.ID = 3
myItem.name = "Eve"
myArray.insertsorted(myItem)
remstart
    the array now looks like this
    [0]: ID=1, name="Carol"
    [1]: ID=2, name="David"
    [2]: ID=3, name="Eve"
    [3]: ID=4, name="Alice"
    [4]: ID=5, name="Bob"
remend

Misc

There are some additional array commands that don't fit in the above categories, they are .swap(index1,index2) to swap two elements in an array (including types and multidimenional arrays) and .reverse() to reverse the elements in an array.

Arrays in Types

Arrays can now be defined in types like so

type myType
    ID as integer
    mySubArray as integer[5]
endtype
myVar as myType
myVar.mySubArray[0] = 6

The array could also be defined as an array of another type if desired so you can do things like this

type myType1
    ID as integer
    mySubArray1 as integer[5]
endtype
type myType2
    name as string
    mySubArray2 as myType1[8]
endtype
myVar as myType2
myVar.mySubArray2[0].mySubArray1[4] = 6
// or 
myVar.mySubArray2[3].mySubArray1.sort()

Passing by Reference

Previously only types could be passed into functions and they were passed by value, meaning the type would be copied into a new variable which was then deleted at the end of the function. Now types can be passed by reference meaning that the variable being passed in will be modified by any changes in the function. For example

function func1( a as point ) // pass by value, variable is copied
    a.x = 5 // this change is local and lost at the end of the function
endfunction 
function func2( a ref as point ) // pass by reference, variable is the original
    a.x = 7 // this change modifies the original variable
endfunction 
type point 
    x as float
    y as float
endtype
myVar as point
myVar.x = 1
myVar.y = 2
func1( myVar ) // doesn't change the variable
print( myVar.x ) // will print "1"
func2( myVar ) // does change the variable
print( myVar.x ) // will print "7"

The same can also now be done with arrays

function func1( a as integer[] ) // pass by value, variable is copied
    a[0] = 5 // this change is local and lost at the end of the function
endfunction 
function func2( a ref as integer[] ) // pass by reference, variable is the original
    a[0] = 7 // this change modifies the original variable
endfunction 
myArray as integer[3]
myArray[0] = 1
myArray[1] = 2
myArray[2] = 3
myArray[3] = 4
func1( myArray ) // doesn't change the variable
print( myArray[0] ) // will print "1"
func2( myArray ) // does change the variable
print( myArray[0] ) // will print "7"

The new compiler is also very flexible about what you can pass into a function that takes a type or array, for example

function func( a ref as integer[] ) // pass by reference
    a[0] = 7
endfunction 
type myType
    ID as integer
    subArray as integer[5]
endtype
myVar as myType
func( myVar.subArray ) // passes the array from the type into the function
print( myVar.subArray[0] ) // will print "7"

To pass multidimensional arrays to functions use additional square brackets in the function declaration

function func1( a ref as integer[] ) // accepts a single dimensional array
    a[0] = 6
endfunction
function func2( a ref as integer[][] ) // accepts a two dimensional array
    a[0,0] = 7
endfunction
function func3( a ref as integer[][][] ) // accepts a three dimensional array
    a[0,0,0] = 8
endfunction

and so on up to six dimensions. You can also pass sub arrays of multidimensional arrays into functions like so (using the functions above)

myArray as integer[5,10,15]
func1( myArray[0,0] ) // passes the array at [0,0] to the function as a single dimensional array
print( myArray[0,0,0] ) // will display "6"

Also note that in these cases "myArray.length" would return 5, "myArray[0].length" would return 10 and myArray[0,0].length" would return 15, and each dimension can be resized individually

myArray[0,0].length = 20
myArray[0,1].length = 30

Assignment

Arrays can be assigned values using the square bracket notation like so

myArray as integer[3]
myArray = [1,2,3,4]
myArray2 as integer[2,5]
myArray2[0] = [1,2,3,4]
myArray2[1] = [5,6,7,8]
myArray2[2] = [9,10,11,12]

If the number of elements being assigned is greater than the current array length then the array will be expanded to accommodate the new elements. If the number of elements is less than the current array length then the array length will remain the same and new elements will overwrite the beginning of the array, whilst the rest of the array remains unchanged

Arrays and types can also now be assigned directly to each other, doing so will copy the contents of the array or type to the other variable

myArray as integer[3]
myArray2 as integer[5]
myArray = [1,2,3,4]
myArray2 = [11,12,13,14,15,16]
myArray = myArray2 // myArray is now length 5 with copies of the values in myArray2
print( myArray[4] ) // will display 15

Any sub arrays or sub types are also copied in their entirety, nothing will be shared with the new variable

type myType
    ID as integer
    subArray as integer[5]
endtype
var1 as myType
var2 as myType
var1.subArray[1] = 5
var2.subArray[1] = 10
var1 = var2 // the type is copied along with the array it contains
print( var1.subArray[1] ) // will print 10
print( var2.subArray[1] ) // will print 10
var2.subArray[1] = 15 // the array was copied so changes to one do not affect the other
print( var1.subArray[1] ) // will print 10
print( var2.subArray[1] ) // will print 15

JSON

Arrays can be converted to and from JSON strings by using the .toJSON() and .fromJSON(string) commands like so

MyArray as integer[5]
MyArray = [10, 53, 2, 678, 3, 2]
var1 as string
var1 = MyArray.toJSON()
var2 as string
var2 = "[1,2,3,4,5]"
MyArray.fromJSON( var2 )

When populating an array from a JSON string the array length will be adjusted to match the length of the array in the JSON string. You can also save and load the array to a JSON file by using the .save(filename) and .load(filename) commands like so

MyArray.save( "MyArray.json" )
MyArray.load( "MyArray.json" )

Types can also be converted to and from JSON strings like so

type spritetype
	ID as integer
	x as float
	y as float
	width as float
	height as float
endtype
MyType as spritetype
MyType.fromJSON( '{"ID": 4, "x": 10.5, "y": 20, "width": 50.1, "height": 20.45}' )
var1 as string
var1 = MyType.toJSON()

Types can contain other types or arrays, and will produce correctly nested JSON objects and arrays to match. There is no limitation on the contents of a type or array when converting it to JSON. When converting a JSON array to an AGK array then the array must only contain one type of variable, for example all integers, all floats, or all strings. There is no limit on a JSON object when converting it to an AGK type.

If a JSON object contains fields that are not in the AGK type being used to load it, then those fields will be ignored. If the AGK type contains fields that are not specified in the JSON object then those fields will be set to zero.

If a JSON object contains field names that are keywords in AGK then they cannot be directly loaded into an AGK type, since it won't be able to use those keywords as variable names. To overcome this you can use an underscore in front of the variable name in the AGK type and it will match JSON fields that don't have the underscore. For example

type spritetype
	ID as integer
	x as float
	y as float
	_type as integer
endtype
MyType as spritetype
MyType.fromJSON( '{"ID": 4, "x": 10.5, "y": 20, "type": 5}' )
Print( MyType._type ) // will print the number 5

When using .toJSON() on a type, any variables that start with an underscore will have that underscore removed in the JSON string, so in the example above the JSON string produced by .toJSON() will contain the field "type" rather than the field "_type". Only the first underscore is removed, so if you actually wanted the JSON field name to begin with an underscore then add two underscores to the AGK type variable instead, i.e "__type" in AGK would be converted to "_type" in JSON.