Array: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
No edit summary
m (Fix)
 
(142 intermediate revisions by 36 users not shown)
Line 1: Line 1:
==Description==
{{TOC|side}}
'''Description:'''
An '''Array''' is a list of items of varying [[:Category:Data Types|variable types]] (including other arrays). Different types can coexist within the same array.
An array is a list of items. Each item may be any the [[:Category:Types|variable types]]. The items in an array are referred to as its '''elements'''.


==Declaring Arrays==
See also: [[:Category:Arrays|Arrays]]
Arrays are declared like this:<br>
{{Feature|warning|Since {{arma3}} v1.56, arrays are limited to maximum of '''9,999,999''' (sometimes 10,000,000) elements.}}
[elementOne, elementTwo, ..., elementItem]


Arrays have an order. That is, any element in the array comes either before, or after, any other element in the array.


==Accessing elements==
== Working With Arrays ==
Every element in the array has an '''index'''. The index says what position the element is in the array. Indices are how elements are accessed. Indices begin at zero, and continue up to (size of array - 1).


The command for accessing elements is [[Select]]. For example, suppose an array is <br>
=== Array Properties ===
_array = [soldier1, soldier2, soldier3]


Then, (_array select 0) is _soldier1, and (_array select 2) is soldier3.
An array variable is a '''reference''' to the array (see {{Link|https://en.wikipedia.org/wiki/Reference_(computer_science)|Wikipedia reference page}});
this means that if the array is edited, all the scripts/functions using a reference to this array will see the edition.


=Old section=
<sqf>
private _myArray = ["a", "b", "c"];
private _myNewArray = _myArray;
_myArray set [1, "z"];
_myNewArray select 1; // will be "z"
</sqf>


Arrays are somewhat different from other variable types.<br>
An array set through [[setVariable]] does not need to be assigned again if you modify it by reference:
Arrays are returned by reference.
<sqf>
player setVariable ["myArray", ["a", "b", "c"]];
private _myArray = player getVariable "myArray";
_myArray set [1, "z"];
player getVariable "myArray"; // is ["a", "z", "c"]
</sqf>


=== Array Creation ===


What does this mean?
<sqf>
// Example of an empty array
private _myArray = [];
count _myArray; // returns 0


// Example of a filled array
private _myFilledArray = ["abc", "def"];
count _myFilledArray; // returns 2
</sqf>


It means that the variable name you give to an array (for example ''myArray'', or ''_units'') references an array of values in the computer's memory.
An array can hold another array within it, that can hold another array itself, etc:
<sqf>
private _myArray = [["my", "subArray", 1], ["mySubArray2"], [["my", "sub", "sub", "array"]]];
count  _myArray; // returns 3
count  (_myArray select 0); // returns 3
count  (_myArray select 1); // returns 1
count  (_myArray select 2); // returns 1
count ((_myArray select 2) select 0); // returns 4
</sqf>


=== Getting an element ===


Suppose you have two numbers, ''_num1'' and ''_num2''.
An array uses a zero-based index for its elements:
You set ''_num1'' to equal 5, and ''_num2'' equal to ''_num1''.
<sqf>
private _myArray = ["first item", "second item", "third item"];
_myArray select 0; // returns "first item"
_myArray # 2; // returns "third item" - Arma 3 only
</sqf>


=== Setting an Element ===


What do you have?<br>
<sqf>
You have two completely separate variables, which store a number each, and which are both currently storing the number 5.
private _myArray = ["first item", "second item", "third item"];
_myArray select 1; // returns "second item"
_myArray set [1, "hello there"]; // _myArray is ["first item", "hello there", "third item"]
</sqf>


{{Feature | important | If the index given to the [[set]] command is out of bounds, the array will [[resize]] to incorporate the index ''as its last value''.
All the "empty spaces" between the last valid element and the new [[set]] element will be filled with [[nil]]}}


But what happens if you do a similar thing with a pair of arrays?
=== Counting elements ===


For Example:
<sqf>
private _myArray = ["first item", ["second item's subitem 1", "second item's subitem 2"], "third item"];
count _myArray; // returns 3 - arrays are not counted recursively
</sqf>


''_array1'' = [5]<br>
=== Changing array size ===
''_array2'' = ''_array1''


What do you have now?
The [[resize]] command is made to reduce or expand an array:
<sqf>
private _myArray = ["a", "b", "c", "d", "e"];
_myArray resize 3; // _myArray is ["a", "b", "c"]
</sqf>
<sqf>
private _myArray = ["a", "b", "c"];
_myArray resize 5; // _myArray is ["a", "b", "c", nil, nil]
</sqf>


{{Feature|important|You do '''not''' need to extend an array before adding any elements.}}


You have two variables, but each one points to the same data, in this case an array containing a single variable of type number, with the value of 5.
=== Array Copy ===
You can understand the difference when you try to change the two variable types around.


<sqf>
private _myArray = ["a", "b", "c"];
private _myNewArray = _myArray;
_myArray set [1, "z"];
_myNewArray select 1; // will be "z"
</sqf>
<sqf>
private _myArray = [["a", "b", "c"], ["d", "e", "f"]];
private _subArray1 = _myArray select 0;
_subArray1 set [1, "z"];
// _subArray1 is now ["a", "z", "c"]
// _myArray is now [["a", "z", "c"], ["d", "e", "f"]]
</sqf>


Taking our number type variables:
In order to avoid this behaviour, '''copy''' the array with [[+|+ (plus)]]:
<sqf>
// making copy
private _myArray = ["a", "b", "c"];
private _myNewArray = +_myArray;
_myArray set [1, "z"];
_myNewArray select 1; // still "b"
</sqf>


Suppose we set ''_num2'' to zero.<br>
Sub-arrays are also deep-copied; {{hl|_myNewArray}} will not point at the same sub-array instances.
Now we have ''_num2'' equal to zero, but ''_num1'' still equal to 5.


=== Adding (Appending) Elements ===


Now take the two arrays.<br>
In {{arma3}} use [[append]] and [[pushBack]] commands:
Supposing we set ''_array2'' to [ [[player]]].
<sqf>
private _myArray = ["a", "b", "c"];
_myArray pushBack "d"; // _myArray is ["a", "b", "c", "d"] - pushback = add the element at the end
_myArray append ["e", "f"]; // _myArray is ["a", "b", "c", "d", "e", "f"] - append = pushback for each provided items
</sqf>


What does ''_array1'' equal?<br>
You could also use the [[+|plus (+)]] operator to add arrays.
''_array1'' also equals [ [[player]]].
The difference is that addition returns a copy of array and thus [[Code Optimisation#Adding elements |a little slower]] than [[append]] and [[pushBack]], which modify the target array.
<sqf>
private _myArray = ["a", "b", "c"];
_myArray = _myArray + ["d"]; // _myArray is ["a", "b", "c", "d"]
_myArray = _myArray + ["e", "f"]; // _myArray is ["a", "b", "c", "d", "e", "f"]
</sqf>


=== Removing (Deleting) Elements ===


We have modified the array that both ''_array1'' and ''_array2'' refer to.<br>
In {{arma3}} the [[deleteAt]] and [[deleteRange]] commands are available:
Exactly the same thing would happen if we set ''_array1'' to [ [[player]]] instead of ''_array2''.<br>
<sqf>
Both our variables, ''_array1'' and ''_array2'' are two labels for the same thing.<br>
private _myArray = ["a", "b", "c", "d", "e"];
_myArray deleteAt 0; // _myArray is ["b", "c", "d", "e"]
</sqf>
<sqf>
private _myArray = ["a", "b", "c", "d", "e"];
_myArray deleteRange [1, 2]; // _myArray is ["a", "d", "e"]
</sqf>


You can also use the [[-|minus (-)]] operator to subtract arrays.
The subtraction returns array copy, just like addition, and is [[Code Optimisation#Removing elements|not as fast]] as [[deleteAt]] and [[deleteRange]] which modify target arrays.
<sqf>
private _myArray = ["a", "b", "c", "d", "e"];
_myArray = _myArray - ["a"]; // _myArray is ["b", "c", "d", "e"]
</sqf>


'''The difference between '[[ cond_set | =]]' and '[[set]]'.'''
In {{arma3}} it became possible to also subtract nested arrays:
<sqf>
private _myArray = [["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]];
_myArray = _myArray - [["d", "e", "f"]]; // _myArray is [["a", "b", "c"], ["g", "h", "i"]]
</sqf>


It is important at this point to recognise the difference between two commands, '''[[set]]''' and '''[[ cond_set | =]]''' .
The subtraction will remove ''all'' elements of the second array from the first one:
If you think about it, there are two ways we could set ''_array2'' to [ [[player]]] from its previous value of [5]. We could say ''_array2'' '''[[ cond_set | =]]''' [ [[player]]] or we could say ''_array2'' '''[[set]]''' [0, [[player]]].
<sqf>_myArray = ["a", "b", "c", "a", "b", "c"] - ["a", "b"]; // _myArray is ["c", "c"]</sqf>


The difference is very important.
The solution to this issue is the combined use of [[set]] and an item that you know is '''not''' present in the array:
<sqf>
private _myArray = ["a", "b", "c", "a", "b", "c"];
_myArray set [2, objNull]; // _myArray is ["a", "b", objNull, "a", "b", "c"]
_myArray = _myArray - [objNull]; // _myArray is ["a", "b", "a", "b", "c"]
</sqf>


If you use '''[[ cond_set | =]]''' to change ''_array2'', you will find that ''_array1'' and ''_array2'' now have different values completely; ''_array1'' is still [5] but ''_array2'' is [ [[player]]].
Using this technique, it is possible to mimic [[deleteRange]] behaviour this way:
<sqf>
private _myArray = ["a", "b", "c", "d", "e"];
{ _myArray set [_x, objNull] } forEach [1, 2]; // _myArray is ["a", objNull, objNull, "d", "e"]
_array = _array - [objNull]; // _myArray is ["a", "d", "e"]
</sqf>


If you use '''[[set]]''', then both ''_array1'' and ''_array2'' store [ [[player]]].
=== Going Through the Array ===


The reason for this lies in what you're doing with the equals.
The simplest way to iterate through an array is the [[forEach]] command:
<sqf>
private _myArray = ["a", "b", "c", "d", "e"];
{ systemChat _x } forEach _myArray;
</sqf>


When you say ''_array2'' '''[[ cond_set | =]]''' [ [[player]]], you are creating an entirely new array, and assigning ''_array2'' as the variable that points to it.
A combination of [[for]], [[count]] and [[select]] can also be used:
<sqf>
private _myArray = ["a", "b", "c", "d", "e"];
for "_i" from 0 to (count _myArray) -1 do { // count returns 5, but it is a zero-based index
systemChat (_myArray select _i);
};
</sqf>


''_array2'' is stripped away from the array that it was originally pointing to (but which ''_array1'' still points to).


== Advanced Usage ==


The equals command has a slighly different meaning - here is a summary:
=== apply ===


''_a1'' [[ cond_set | =]] [1,2,3]
Similar to the {{Link|https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map|"map" function in Javascript}},
The variable _a1 points to a new array which is created at the end of this statement
[[apply]] allows to apply code to every elements in an array and return a copy:
<sqf>
private _myArray = [1, 2, 3, 4, 5];
_myArray = _myArray apply { _x * 2 }; // _myArray is [2, 4, 6, 8, 10]


''_a2'' [[ cond_set | =]] ''_a1''
// same as (but faster than)
No new array is created, but the variable ''_a2'' now refers to the same array that ''_a1'' refers to.
_myArray = + _myArray;
for "_i" from 0 to count _myArray -1 do
{
private _element = _myArray select _i;
_myArray set [_i, _element * 2];
};
</sqf>


''_a2'' [[ cond_set | =]]  ''+_a1''
=== select ===
A new array is created which is an identical clone of the array that ''_a1'' points to.
''_a2'' points to this new array.
''_a1'' points to the old one


''_a2'' [[ cond_set | =]] ''_a1'' + [2]
A simple way to filter an array (and obtain a new one) is using [[select]]'s alternative syntax:
A new array is created which is the same as array ''_a1'' plus an extra element.
<sqf>
private _myArray = [1, 2, 3, 4, 5];
private _filteredArray = _myArray select { _x > 3 }; // _filteredArray is [4, 5]


''_a2'' '''[[set]]''' [0, [[player]]]
// same as
The array which ''_a2'' references is modified.
private _filteredArray = [];
Its zero element is set to '[[player]]'.
{ if (_x > 3) then { _filteredArray pushBack _x } } forEach _myArray;
All variables referencing this array will return the changed array.
</sqf>


=== findIf ===


Because arrays are 'returned by reference' (not by value), the equality ([[ cond_equal | ==]]) operator behaves differently.
The [[findIf]] command was introduced in {{arma3}} and allows you to go through the whole list and stop '''as soon as the condition is met''', returning the condition-meeting element's array index:
Testing our two numbers, ''_num1'' and ''_num2'', we know that ''_num1'' [[ cond_equal | ==]] ''_num2'' is true when ''_num1'' and ''_num2'' are storing the same value.
<sqf>
private _myArray = [1, 2, 3, 4, 5];
_myArray findIf { _x == 3 } > -1; // returns true, meaning there is an element that equals 3
_myArray findIf { _x == 6 } > -1; // returns false, meaning there is no element that is equal to 6
</sqf>


The same is '''not''' true of arrays.
You could use [[count]] to achieve the same result, however [[count]] won't stop until it iterated through the whole array, so it might take [[Code Optimisation#findIf|longer]].
<sqf>
private _myArray = [1, 2, 3, 4, 5];
{ _x == 3 } count _myArray > 0; // returns true, meaning there is an element that equals 3
{ _x == 6 } count _myArray > 0; // returns false, meaning there is no element that is equal to 6
</sqf>


If we had two separate arrays, and both had identical values e.g. [3,4,2].
=== arrayIntersect ===


''_firstArray'' [[ cond_equal | ==]] ''_secondArray'' would return false, even though the values are identical.
The [[arrayIntersect]] command returns a new array filled with the items found in both provided lists:
When used with arrays, the equality ([[ cond_equal | ==]]) operator compares two references and checks if they point to the same thing.
<sqf>
private _array1 = [1, 2, 3, 4];
private _array2 = [3, 4, 5, 6];
private _result = _array1 arrayIntersect _array2; // _result is [3, 4]
</sqf>


From our example above, ''_array1'' [[ cond_equal | ==]] ''_array2'' would return true, because both ''_array1'' and ''_array2'' point to the same array.
'''You can remove duplicates (get unique items) with this command:'''
<sqf>
private _myArray = [1, 2, 2, 3, 4];
private _result = _myArray arrayIntersect _myArray; // _result is [1, 2, 3, 4]
</sqf>


Another interesting thing is what happens when we delete a variable pointing to an array.
Be wary that [[nil]] elements get removed by this method:
<sqf>
private _myArray = [1, 2, nil, 3, 4];
private _result = _myArray arrayIntersect _myArray; // _result is [1, 2, 3, 4]
</sqf>


Returning to our two arrays, supposing we do:
=== Selective Removal ===
''_array2'' [[ cond_set | =]] [[nil]]


The array is not deleted.
Selectively filter out single elements from {{hl|_arrayA}} based on {{hl|_arrayB}}.<br>
It helps to think of ''_array1'' and ''_array2'' as labels or arrows, pointing to a chunk of data (the array).
This is useful in the case where ''some'' duplicates must be removed.
Deleting ''_array2'' removes one of the arrows pointing to the array.
''_array1'', however, still points to the array as per usual.


(An array is normally destroyed, and its memory reclaimed, when there are no more labels pointing to it).
<sqf>
private _arrayA = [1, 2, 3, 2, 4, 5, 4];
private _arrayB = [2, 2, 4];


[[Category: Types]]
{
private _index = _arrayA find _x;
if (_index != -1) then
{
_arrayA deleteAt _index;
};
} forEach _arrayB;
 
_arrayA; // is now [1, 3, 5, 4]
</sqf>
 
=== Sorting ===
 
==== sort ====
The [[sort]] command allows for sorting an array of [[String]], [[Number]] or sub-[[Array]]s of string/number. It modifies the original array and '''does not return anything''':
<sqf>
private _myArray = ["zzz", "aaa", "ccc"];
_myArray sort true; // _myArray is ["aaa", "ccc", "zzz"]
</sqf>
<sqf>
private _myArray = [666, 57, 1024, 42];
_myArray sort false; // _myArray is [1024, 666, 57, 42]
</sqf>
<sqf>
private _myArray = [["zzz", 0], ["aaa", 42], ["ccc", 33]];
_myArray sort true; // _myArray is [["aaa", 42], ["ccc", 33], ["zzz", 0]]
</sqf>
 
==== reverse ====
The [[reverse]] command simply reverses the array order:
<sqf>
private _myArray = [99, 33, 17, 24, "a", [3,2,1], 7777];
reverse _myArray; // _myArray is [7777, [3,2,1], "a", 24, 17, 33, 99]
</sqf>
 
==== BIS_fnc_sortBy ====
The function [[BIS_fnc_sortBy]] has been created for more complex sorting. Its algorithm input must return a number:
<sqf>private _closestHelicopters = [[_heli1, _heli2, _heli3], [], { player distance _x }, "ASCEND"] call BIS_fnc_sortBy;</sqf>
 
 
== Common Errors ==
 
=== Index Rounding ===
 
In {{Link|Real Virtuality}} scripts, indices are rounded to the nearest whole number.
A boundary case (X.5, where X is any whole number) rounds to the nearest '''even''' whole number.
 
; Boundary cases:
* -0.5 <= index <= 0.5 rounds to 0
* &nbsp;0.5 <&nbsp; index <&nbsp; 1.5 rounds to 1
* &nbsp;1.5 <= index <= 2.5 rounds to 2
* &nbsp;2.5 <&nbsp; index <&nbsp; 3.5 rounds to 3
 
; In short:
* -0.5 rounds '''up''' to 0
* &nbsp;0.5 rounds '''down''' to 0
* &nbsp;1.5 rounds '''up''' to 2
* &nbsp;2.5 rounds '''down''' to 2
* &nbsp;3.5 rounds '''up''' to 4
etc.
{{Feature|informative|This behaviour is different from {{Link|random}}'s behaviour.}}
 
=== Index out of Range ===
 
The following code lists {{arma3}} behaviour on wrong indices:
<sqf>
private _myArray = ["element0"];
_myArray select -1; // throws a Error Zero Divisor error message
_myArray select  0; // returns "element0"
_myArray select  1; // returns nil
_myArray select  2; // throws a Error Zero Divisor error message
</sqf>
 
==== param ====
Use the [[param]] command in order to avoid out of range error:
<sqf>
private _myArray = [0, 1, 2];
_myArray select 5; // error: out of range
_myArray param [5]; // returns nil
_myArray param [5, "abc"]; // returns default value "abc" on invalid index
</sqf>
{{Feature|informative|See also [[params]] to define multiple items at once.}}
 
==== set ====
If the index given to the [[set]] command is out of bounds:
* if the index rounded to a negative number, then an [[Error Zero Divisor]] message will be displayed in game.
* if the index rounded to a positive number, then the array will [[resize]] to incorporate the index ''as its last value''. Each element between the last valid element, and the new [[set]] element, will be the [[Nothing|null type]]
 
=== Zero Divisor ===
 
See above - a used array index may be negative.
 
=== Unexpected "," ===
 
This error originates from a bad syntax:
<sqf>
private _myErroneousArray = ["Weapon1", "Weapon2", "Weapon3",]; // The last element in an array must not end by ","
 
// this mostly happens in vertical arrays
private _myErroneousArray = [
"element1",
"element2",
"element3", // this one comma is wrong
];
</sqf>
 
=== Reserved variable in expression ===
 
This issue arises when trying to modify a read-only array (such as {{Link|select}} arrays that are returned from an addon, or eventually a trigger's {{Link|Magic Variables#thisList|thisList}}).
 
To combat this, ''copy'' said array with the {{Link|+|plus (+)}} command before modifying:
<sqf>
private _readOnlyArray = thisList; // assuming the code is called from within a trigger
_readOnlyArray set [1, "test"]; // Error: Reserved variable in expression - thisList return value is read-only
 
// No Error
private _normalArray = +_readOnlyArray; // shallow-copy the array instead of using a reference
_normalArray set [1, "test"]; // perfectly fine
</sqf>
 
 
== See Also ==
 
* [[:Category:Arrays|Arrays]]
 
 
[[Category: Data Types]]

Latest revision as of 19:49, 26 May 2024

An Array is a list of items of varying variable types (including other arrays). Different types can coexist within the same array.

See also: Arrays

Since Arma 3 v1.56, arrays are limited to maximum of 9,999,999 (sometimes 10,000,000) elements.


Working With Arrays

Array Properties

An array variable is a reference to the array (see Wikipedia reference page); this means that if the array is edited, all the scripts/functions using a reference to this array will see the edition.

private _myArray = ["a", "b", "c"]; private _myNewArray = _myArray; _myArray set [1, "z"]; _myNewArray select 1; // will be "z"

An array set through setVariable does not need to be assigned again if you modify it by reference:

player setVariable ["myArray", ["a", "b", "c"]]; private _myArray = player getVariable "myArray"; _myArray set [1, "z"]; player getVariable "myArray"; // is ["a", "z", "c"]

Array Creation

// Example of an empty array private _myArray = []; count _myArray; // returns 0 // Example of a filled array private _myFilledArray = ["abc", "def"]; count _myFilledArray; // returns 2

An array can hold another array within it, that can hold another array itself, etc:

private _myArray = [["my", "subArray", 1], ["mySubArray2"], [["my", "sub", "sub", "array"]]]; count _myArray; // returns 3 count (_myArray select 0); // returns 3 count (_myArray select 1); // returns 1 count (_myArray select 2); // returns 1 count ((_myArray select 2) select 0); // returns 4

Getting an element

An array uses a zero-based index for its elements:

private _myArray = ["first item", "second item", "third item"]; _myArray select 0; // returns "first item" _myArray # 2; // returns "third item" - Arma 3 only

Setting an Element

private _myArray = ["first item", "second item", "third item"]; _myArray select 1; // returns "second item" _myArray set [1, "hello there"]; // _myArray is ["first item", "hello there", "third item"]

If the index given to the set command is out of bounds, the array will resize to incorporate the index as its last value. All the "empty spaces" between the last valid element and the new set element will be filled with nil

Counting elements

private _myArray = ["first item", ["second item's subitem 1", "second item's subitem 2"], "third item"]; count _myArray; // returns 3 - arrays are not counted recursively

Changing array size

The resize command is made to reduce or expand an array:

private _myArray = ["a", "b", "c", "d", "e"]; _myArray resize 3; // _myArray is ["a", "b", "c"]
private _myArray = ["a", "b", "c"]; _myArray resize 5; // _myArray is ["a", "b", "c", nil, nil]

You do not need to extend an array before adding any elements.

Array Copy

private _myArray = ["a", "b", "c"]; private _myNewArray = _myArray; _myArray set [1, "z"]; _myNewArray select 1; // will be "z"
private _myArray = [["a", "b", "c"], ["d", "e", "f"]]; private _subArray1 = _myArray select 0; _subArray1 set [1, "z"]; // _subArray1 is now ["a", "z", "c"] // _myArray is now [["a", "z", "c"], ["d", "e", "f"]]

In order to avoid this behaviour, copy the array with + (plus):

// making copy private _myArray = ["a", "b", "c"]; private _myNewArray = +_myArray; _myArray set [1, "z"]; _myNewArray select 1; // still "b"

Sub-arrays are also deep-copied; _myNewArray will not point at the same sub-array instances.

Adding (Appending) Elements

In Arma 3 use append and pushBack commands:

private _myArray = ["a", "b", "c"]; _myArray pushBack "d"; // _myArray is ["a", "b", "c", "d"] - pushback = add the element at the end _myArray append ["e", "f"]; // _myArray is ["a", "b", "c", "d", "e", "f"] - append = pushback for each provided items

You could also use the plus (+) operator to add arrays. The difference is that addition returns a copy of array and thus a little slower than append and pushBack, which modify the target array.

private _myArray = ["a", "b", "c"]; _myArray = _myArray + ["d"]; // _myArray is ["a", "b", "c", "d"] _myArray = _myArray + ["e", "f"]; // _myArray is ["a", "b", "c", "d", "e", "f"]

Removing (Deleting) Elements

In Arma 3 the deleteAt and deleteRange commands are available:

private _myArray = ["a", "b", "c", "d", "e"]; _myArray deleteAt 0; // _myArray is ["b", "c", "d", "e"]
private _myArray = ["a", "b", "c", "d", "e"]; _myArray deleteRange [1, 2]; // _myArray is ["a", "d", "e"]

You can also use the minus (-) operator to subtract arrays. The subtraction returns array copy, just like addition, and is not as fast as deleteAt and deleteRange which modify target arrays.

private _myArray = ["a", "b", "c", "d", "e"]; _myArray = _myArray - ["a"]; // _myArray is ["b", "c", "d", "e"]

In Arma 3 it became possible to also subtract nested arrays:

private _myArray = [["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]]; _myArray = _myArray - [["d", "e", "f"]]; // _myArray is [["a", "b", "c"], ["g", "h", "i"]]

The subtraction will remove all elements of the second array from the first one:

_myArray = ["a", "b", "c", "a", "b", "c"] - ["a", "b"]; // _myArray is ["c", "c"]

The solution to this issue is the combined use of set and an item that you know is not present in the array:

private _myArray = ["a", "b", "c", "a", "b", "c"]; _myArray set [2, objNull]; // _myArray is ["a", "b", objNull, "a", "b", "c"] _myArray = _myArray - [objNull]; // _myArray is ["a", "b", "a", "b", "c"]

Using this technique, it is possible to mimic deleteRange behaviour this way:

private _myArray = ["a", "b", "c", "d", "e"]; { _myArray set [_x, objNull] } forEach [1, 2]; // _myArray is ["a", objNull, objNull, "d", "e"] _array = _array - [objNull]; // _myArray is ["a", "d", "e"]

Going Through the Array

The simplest way to iterate through an array is the forEach command:

private _myArray = ["a", "b", "c", "d", "e"]; { systemChat _x } forEach _myArray;

A combination of for, count and select can also be used:

private _myArray = ["a", "b", "c", "d", "e"]; for "_i" from 0 to (count _myArray) -1 do { // count returns 5, but it is a zero-based index systemChat (_myArray select _i); };


Advanced Usage

apply

Similar to the "map" function in Javascript, apply allows to apply code to every elements in an array and return a copy:

private _myArray = [1, 2, 3, 4, 5]; _myArray = _myArray apply { _x * 2 }; // _myArray is [2, 4, 6, 8, 10] // same as (but faster than) _myArray = + _myArray; for "_i" from 0 to count _myArray -1 do { private _element = _myArray select _i; _myArray set [_i, _element * 2]; };

select

A simple way to filter an array (and obtain a new one) is using select's alternative syntax:

private _myArray = [1, 2, 3, 4, 5]; private _filteredArray = _myArray select { _x > 3 }; // _filteredArray is [4, 5] // same as private _filteredArray = []; { if (_x > 3) then { _filteredArray pushBack _x } } forEach _myArray;

findIf

The findIf command was introduced in Arma 3 and allows you to go through the whole list and stop as soon as the condition is met, returning the condition-meeting element's array index:

private _myArray = [1, 2, 3, 4, 5]; _myArray findIf { _x == 3 } > -1; // returns true, meaning there is an element that equals 3 _myArray findIf { _x == 6 } > -1; // returns false, meaning there is no element that is equal to 6

You could use count to achieve the same result, however count won't stop until it iterated through the whole array, so it might take longer.

private _myArray = [1, 2, 3, 4, 5]; { _x == 3 } count _myArray > 0; // returns true, meaning there is an element that equals 3 { _x == 6 } count _myArray > 0; // returns false, meaning there is no element that is equal to 6

arrayIntersect

The arrayIntersect command returns a new array filled with the items found in both provided lists:

private _array1 = [1, 2, 3, 4]; private _array2 = [3, 4, 5, 6]; private _result = _array1 arrayIntersect _array2; // _result is [3, 4]

You can remove duplicates (get unique items) with this command:

private _myArray = [1, 2, 2, 3, 4]; private _result = _myArray arrayIntersect _myArray; // _result is [1, 2, 3, 4]

Be wary that nil elements get removed by this method:

private _myArray = [1, 2, nil, 3, 4]; private _result = _myArray arrayIntersect _myArray; // _result is [1, 2, 3, 4]

Selective Removal

Selectively filter out single elements from _arrayA based on _arrayB.
This is useful in the case where some duplicates must be removed.

private _arrayA = [1, 2, 3, 2, 4, 5, 4]; private _arrayB = [2, 2, 4]; { private _index = _arrayA find _x; if (_index != -1) then { _arrayA deleteAt _index; }; } forEach _arrayB; _arrayA; // is now [1, 3, 5, 4]

Sorting

sort

The sort command allows for sorting an array of String, Number or sub-Arrays of string/number. It modifies the original array and does not return anything:

private _myArray = ["zzz", "aaa", "ccc"]; _myArray sort true; // _myArray is ["aaa", "ccc", "zzz"]
private _myArray = [666, 57, 1024, 42]; _myArray sort false; // _myArray is [1024, 666, 57, 42]
private _myArray = [["zzz", 0], ["aaa", 42], ["ccc", 33]]; _myArray sort true; // _myArray is [["aaa", 42], ["ccc", 33], ["zzz", 0]]

reverse

The reverse command simply reverses the array order:

private _myArray = [99, 33, 17, 24, "a", [3,2,1], 7777]; reverse _myArray; // _myArray is [7777, [3,2,1], "a", 24, 17, 33, 99]

BIS_fnc_sortBy

The function BIS_fnc_sortBy has been created for more complex sorting. Its algorithm input must return a number:

private _closestHelicopters = [[_heli1, _heli2, _heli3], [], { player distance _x }, "ASCEND"] call BIS_fnc_sortBy;


Common Errors

Index Rounding

In Real Virtuality scripts, indices are rounded to the nearest whole number. A boundary case (X.5, where X is any whole number) rounds to the nearest even whole number.

Boundary cases
  • -0.5 <= index <= 0.5 rounds to 0
  •  0.5 <  index <  1.5 rounds to 1
  •  1.5 <= index <= 2.5 rounds to 2
  •  2.5 <  index <  3.5 rounds to 3
In short
  • -0.5 rounds up to 0
  •  0.5 rounds down to 0
  •  1.5 rounds up to 2
  •  2.5 rounds down to 2
  •  3.5 rounds up to 4

etc.

This behaviour is different from random's behaviour.

Index out of Range

The following code lists Arma 3 behaviour on wrong indices:

private _myArray = ["element0"]; _myArray select -1; // throws a Error Zero Divisor error message _myArray select 0; // returns "element0" _myArray select 1; // returns nil _myArray select 2; // throws a Error Zero Divisor error message

param

Use the param command in order to avoid out of range error:

private _myArray = [0, 1, 2]; _myArray select 5; // error: out of range _myArray param [5]; // returns nil _myArray param [5, "abc"]; // returns default value "abc" on invalid index

See also params to define multiple items at once.

set

If the index given to the set command is out of bounds:

  • if the index rounded to a negative number, then an Error Zero Divisor message will be displayed in game.
  • if the index rounded to a positive number, then the array will resize to incorporate the index as its last value. Each element between the last valid element, and the new set element, will be the null type

Zero Divisor

See above - a used array index may be negative.

Unexpected ","

This error originates from a bad syntax:

private _myErroneousArray = ["Weapon1", "Weapon2", "Weapon3",]; // The last element in an array must not end by "," // this mostly happens in vertical arrays private _myErroneousArray = [ "element1", "element2", "element3", // this one comma is wrong ];

Reserved variable in expression

This issue arises when trying to modify a read-only array (such as select arrays that are returned from an addon, or eventually a trigger's thisList).

To combat this, copy said array with the plus (+) command before modifying:

private _readOnlyArray = thisList; // assuming the code is called from within a trigger _readOnlyArray set [1, "test"]; // Error: Reserved variable in expression - thisList return value is read-only // No Error private _normalArray = +_readOnlyArray; // shallow-copy the array instead of using a reference _normalArray set [1, "test"]; // perfectly fine


See Also