As promised in previous post, here is another set of functions to operate with arrays. This time it is to do with Associative Arrays. In a nutshell, you have a key and a value pair you can store and then retrieve or modify it by key. As there is no dedicated associative array handling in Arma, it can be emulated. One way is to keep it in [[key1,val1],[key2,val2]…] format. However such approach, although visually pleasing, is pretty awkward to work with due to SQF array commands, not to mention multi-dimensional arrays are slowing down performance. So I’ve created a set of functions that handle 2 parallel arrays, one with keys [key1,key2…], one with respectful values [val1,val2…].

KK_fnc_assocArrayCreate

KK_fnc_assocArrayCreate = { //_ref = call KK_fnc_assocArrayCreate; private "_ref"; _ref = str ("WeaponHolderSimulated" createVehicleLocal [0,0,0]); missionNamespace setVariable [_ref + "_KEYS", []]; missionNamespace setVariable [_ref + "_VALS", []]; _ref };

This will create an instance of associative array and return a string with reference to this instance. I wanted to be able to create thousands of arrays in split second if needed, so the reference needs to be 100% unique. Well, well, well, debug name for an object that is returned when you hint str _obj  is pretty unique and safe. Creating a WeaponHolderSimulated will make a unique instance of an object and then delete the object instantly, perfect for a unique ID!

_myAArray = call KK_fnc_assocArrayCreate;

KK_fnc_assocArrayAddKeyVal

KK_fnc_assocArrayAddKeyVal = { //_ref = [_ref, _key, _val] call KK_fnc_assocArrayAddKeyVal; private ["_ref","_keysRef","_valsRef","_keys","_vals","_key","_indx"]; _ref = _this select 0; _keysRef = format ["%1_KEYS", _ref]; _valsRef = format ["%1_VALS", _ref]; if (isNil _keysRef || isNil _valsRef) then { missionNamespace setVariable [_keysRef, []]; missionNamespace setVariable [_valsRef, []]; }; _keys = missionNamespace getVariable _keysRef; _vals = missionNamespace getVariable _valsRef; _key = _this select 1; _indx = _keys find _key; if (_indx < 0) then { _indx = count _keys; _keys set [_indx, _key]; }; _vals set [_indx, _this select 2]; _ref };

This function will add a (key, val) pair to an existing array indicated by array reference obtained from KK_fnc_assocArrayCreate function:

[_myAArray, "Name", "KK"] call KK_fnc_assocArrayAddKeyVal;

If you do not have array already created, you can create it on the fly with this function, by passing something to it as a reference. For example I can create an associative array linked to my player by passing netId of my player (netId is unique enough but MP only):

_newAArray = [netId player, "Name", name player] call KK_fnc_assocArrayAddKeyVal;

The function also returns array reference for convenience. It is worth mentioning that there could only be 1 unique key (case sensitive) in associative array, so if you try to add (key, val) pair and the key exists, you will simply overwrite the existing key value. If you do not want this to happen, you can always check for the existence of a key.

KK_fnc_assocArrayKeyExists

KK_fnc_assocArrayKeyExists = { //_bool = [_ref, _key] call KK_fnc_assocArrayKeyExists (_this select 1 in (missionNamespace getVariable [format [ "%1_KEYS", _this select 0 ], []])) };

What is says on the tin. If key exists you will get true, or false if it doesn’t exist.

if ([_myAArray, "Name"] call KK_fnc_assocArrayKeyExists) then { hint "Key 'Name' exists!"; };

KK_fnc_assocArrayGetVal

KK_fnc_assocArrayGetVal = { //_val = [_ref, _key] call KK_fnc_assocArrayGetVal; private ["_ref","_keys","_indx"]; _ref = _this select 0; _keys = missionNamespace getVariable [format ["%1_KEYS", _ref], []]; _indx = _keys find (_this select 1); if (_indx < 0) exitWith {}; (missionNamespace getVariable [format ["%1_VALS", _ref], []]) select _indx };

You can use this function to get the value of a key.

_value = [_myAArray, "Name"] call KK_fnc_assocArrayGetVal;

If you know for sure the key exists, then no problem. If not sure, you can either check for key existence with the KK_fnc_assocArrayKeyExists, or check if the returned result is not nil:

if (!isNil {_value}) then { hint _value; } else { hint "'Name' is either empty or doesn't exist!"; };

KK_fnc_assocArrayDeleteKey

KK_fnc_assocArrayDeleteKey = { //_bool = [_ref, _key] call KK_fnc_assocArrayDeleteKey; private ["_ref","_keys","_vals","_indx"]; _ref = _this select 0; _keys = missionNamespace getVariable [format ["%1_KEYS", _ref], []]; _vals = missionNamespace getVariable [format ["%1_VALS", _ref], []]; _indx = _keys find (_this select 1); if (_indx < 0) exitWith {false}; _keys set [_indx, nil]; _vals set [_indx, nil]; true };

This will clear memory of (key, val) pair but will not resize the array. On successful operation it will return true, but if key does not exist it will return false.

if ([_myAArray, "Name"] call KK_fnc_assocArrayDeleteKey) then { hint "Existing key 'Name' is successfully deleted"; };

KK_fnc_assocArrayDestroy

KK_fnc_assocArrayDestroy = { //_ref call KK_fnc_assocArrayDestroy; missionNamespace setVariable [format ["%1_KEYS", _this], nil]; missionNamespace setVariable [format ["%1_VALS", _this], nil]; };

Will clear memory occupied by array.

_myAArray call KK_fnc_assocArrayDestroy;

KK_fnc_assocArrayAllKeys

KK_fnc_assocArrayAllKeys = { //_keys = _ref call KK_fnc_assocArrayAllKeys missionNamespace getVariable [format ["%1_KEYS", _this], []] };

This will return all the keys of the array, including placeholders for deleted keys. To calculate the length of array, you can simply count the keys.

_size = count (_myAArray call KK_fnc_assocArrayAllKeys);

One thing I’d change would be the removal and not deletion of array elements. Might do it later if I can find an elegant way of doing it.

Enjoy,
KK

EDIT: KK_fnc_assocArrayAddKeyVal tweaked.
EDIT2: KK_fnc_assocArraySize changed to more useful KK_fnc_assocArrayAllKeys.
EDIT3: KK_fnc_assocArrayKeyExists optimised.