Jump to content
Sign in to follow this  
_Mofo_

Arrays.sqf (howto)?

Recommended Posts

Hey all-

Have found this script while searching for a way to remove duplicates from an array and I'm not quite sure how to call / use this.

1. Is this part of the functions module?

2. If not 1, then how does one include this file? (compile?)

(Source)

http://code.google.com/p/arma2-sqf-library-communitys-shared-scripts/source/browse/trunk/css/lib/arrays.sqf?spec=svn1fabcfbda6efc7527aa02e9f29b533ab4f053918&r=1fabcfbda6efc7527aa02e9f29b533ab4f053918

(Download)

http://code.google.com/p/arma2-sqf-library-communitys-shared-scripts/downloads/detail?name=SQF-Library%20Community%60s%20Shared%20Scripts%20%28revision%2024105a186b4c46aedb46a90666dd7867b022c47b%29.7z&can=2&q=

----

Everything I've tried (execVM, compile {crashes the game}, simply leaving it out all together) throws the same "Error: mission ;" on the invoking line;

_test = ["test","test","test"];

_test invoke(GetUnduplicatedArray);

hint format["%1",_test];

Note* this is is only a test array... the array I will use this on is a nested array with chords in which I'll check if the chords are already within the array.

I.e. array = [[x,y,z],[x,y,z]];

Edited by _Mofo_

Share this post


Link to post
Share on other sites

It is not part of the functions module. Looking at the function, http://code.google.com/p/arma2-sqf-library-communitys-shared-scripts/source/browse/trunk/css/lib/arrays.sqf#87, you want to call it doesn't look like it will handle nested arrays, since you have to check if every element is the same. We can solve this by creating a function that checks if two (nested)arrays are equal and then another to check the entire array for duplicates:

Checks if two objects are equal. They are if they are of the same type and value. For array they are if they have the same length and same content:

FancyIsEqual = {
   private ["_objectA","_objectB","_result","_elemB","_elemB"];
_objectA = _this select 0;
_objectB = _this select 1;
//Do they have the same type?
if (typeName _objectA != typeName _objectB) exitWith {false};
//Are they values?
if (typeName _objectA != typeName []) exitWith {_objectA == _objectB};
//It must be arrays. Do they have same length?
if (count _objectA != count _objectB) exitWith {false};
//Process each element; assume true
_result = true;
for "_i" from 0 to (count _arrayA - 1) do {
	_elemA = _objectA select _i;
	_elemB = _objectB select _i;
	//Process recursively
	if (not ([_elemA, _elemB] call FancyIsEqual)) exitWith {_result = false;};
};
_result
};

Now we can remove duplicates. Keep adding to a new array and check if we already added it to the new array. If we did then it is a duplicate and should not be added again.

RemoveDuplicates = {
   private ["_array", "_unduplicated", "_original"];
_array = _this;
_unduplicated = [];
{
	_original = _x;
	{
		if (not ([_original, _x] call FancyIsEqual)) exitWith {
			_unduplicated set [count _unduplicated, _original];
		};
	} forEach _unduplicated;
} forEach _array;
};

I have not checked this for syntax or logic errors.

Share this post


Link to post
Share on other sites

Thank you for the time and I'll be more than happy to check for errors while testing and correct as needed. I really do appreciate it your time!!!

Will report back shortly.

------

Also, in trying to understand your logic and the way you are checking the arrays... when you say... "For array they are if they have the same length and same content:"... I'm assuming you mean that the entirety of the content must be the same... correct?

I.e.

_a1 = [[1,2,3],[2,3,4],[1,2,3]];

the Unduped array would be built using the entirely of the nested array content and not just the individual elements themselves

_unDupe = [[1,2,3],[2,3,4]];

Sorry to question... but I'm really struggling to wrap my head around checking nested arrays for dupes.

------

First test:

FancyIsEqual = {
   private ["_objectA","_objectB","_result","_elemB","_elemB"];
   _objectA = _this select 0;
   _objectB = _this select 1;
   //Do they have the same type?
   if (typeName _objectA != typeName _objectB) exitWith {false};
   //Are they values?
   if (typeName _objectA != typeName []) exitWith {_objectA == _objectB};
   //It must be arrays. Do they have same length?
   if (count _objectA != count _objectB) exitWith {false};
   //Process each element; assume true
   _result = true;
   for "_i" from 0 to (count _arrayA - 1) do {
       _elemA = _objectA select _i;
       _elemB = _objectB select _i;
       //Process recursively
       if (not ([_elemA, _elemB] call FancyIsEqual)) exitWith {_result = false;};
   };
   _result
};  


RemoveDuplicates = {
   private ["_array", "_unduplicated", "_original"];
   _array = _this;
   _unduplicated = [];
   {
       _original = _x;
       {
           if (not ([_original, _x] call FancyIsEqual)) exitWith {
               _unduplicated set [count _unduplicated, _original];
           };
       } forEach _unduplicated;
   } forEach _array;
hint format["%1",_unduplicated]; // hinting out the array after all runs
};  

// Setup test array
_test = [[1,2,3],[1,2,3],[4,5,6]];

// call remove duplicates
_test call RemoveDuplicates;

This returns a blank array in the hint.

Wouldn't the check fail on every run since "_unduplicated" will return "any" or "nil" for the first run?

RemoveDuplicates = {

private ["_array", "_unduplicated", "_original"];

_array = _this;

_unduplicated = [];

{

_original = _x;

{

if (not ([_original, _x] call FancyIsEqual)) exitWith {

_unduplicated set [count _unduplicated, _original];

};

} forEach _unduplicated;

} forEach _array;

};

Since the array is empty to begin with...

 if (typeName _objectA != typeName _objectB) exitWith {false};

Would always return false?

Maybe count _unduplicated array and if 0 store the first value no matter what since the array is empty?

Edited by _Mofo_

Share this post


Link to post
Share on other sites

The "problem" with arrays are that they don't compare equal.

[1,2,3] == [1,2,3]    (false)

So I define equality by the content of the arrays, eg. I want it to be the case that the above is true. I also want this to be true (it is equally simple to implement):

[ [1, 2], 3] == [ [1, 2], 3]

This is what FancyIsEqual checks for.

The plan to remove duplicates is then, given:

Original: [ [1,2,3], [2,3,4], [1,2,3] ]

New: []

Then we take the first item [1,2,3] and check if it already is in the new array using FancyIsEqual since == doesn't work. If it isn't so

New: [ [1,2,3] ]

Then we take the next item [2,3,4] and check. None of the current elements in New array matches so we add it:

New: [ [1,2,3], [2,3,4] ]

Then we check the last item [1,2,3]. Using == it would give false since it is in fact two different arrays. Just happens to have the same content. But FancyIsEqual idenfies the as equal so it is not added. Resulting in:

New: [ [1,2,3], [2,3,4] ]

Wouldn't the check fail on every run since "_unduplicated" will return "any" or "nil" for the first run?

Good catch. Hadn't noticed that. I've attempted to fix it:

RemoveDuplicates = {
   private ["_array", "_unduplicated", "_original","_exists"];
   _array = _this;
   _unduplicated = [];
   {
       _original = _x;
       _exists = false;
       {
           if (not ([_original, _x] call FancyIsEqual)) exitWith {
           	_exists = true;
           };
       } forEach _unduplicated;
       if (not _exists) then {
       	_unduplicated set [count _unduplicated, _original];
       };
   } forEach _array;
  _unduplicated
};  

I don't know if you are going to be using nil in your code. But to make FancyIsEqual more robust maybe add these lines as the first check:

   if (isNil "_objectA") exitWith {isNil "_objectB"};

Which properly checks that if one value is 'nil' then the other has to be too for them to be equal.

Edited by Muzzleflash

Share this post


Link to post
Share on other sites

I won't be using nil in the array at all... give me a few mins to process this new information and try again to implement it. BTW... your brain must hurt every night if this is the sort of stuff you think about during the day. ;)

Also... they "must" match "in order" according to position within the nest? So that...

[1,2,3] vs. [1,3,2] = false (not matched)

This code takes care of that correct?

for "_i" from 0 to (count _arrayA - 1) do {
       _elemA = _objectA select _i;
       _elemB = _objectB select _i;
       //Process recursively
       if (not ([_elemA, _elemB] call FancyIsEqual)) exitWith {_result = false;};
   };

----------

Further testing...

FancyIsEqual = {
   private ["_objectA","_objectB","_result","_elemB","_elemB"];
   _objectA = _this select 0;
   _objectB = _this select 1;
   //Do they have the same type?
   if (typeName _objectA != typeName _objectB) exitWith {false};
   //Are they values?
   if (typeName _objectA != typeName []) exitWith {_objectA == _objectB};
   //It must be arrays. Do they have same length?
   if (count _objectA != count _objectB) exitWith {false};
   //Process each element; assume true
   _result = true;
   for "_i" from 0 to (count _objectA - 1) do {
       _elemA = _objectA select _i;
       _elemB = _objectB select _i;
       //Process recursively
       if (not ([_elemA, _elemB] call FancyIsEqual)) exitWith {_result = false;};
   };
   _result
};  


RemoveDuplicates = {
   private ["_array", "_unduplicated", "_original","_exists"];
   _array = _this;
   _unduplicated = [];
   {
       _original = _x;
       _exists = false;
       {
           if (not ([_original, _x] call FancyIsEqual)) exitWith {
               _exists = true;
           };
       } forEach _unduplicated;
       if (not _exists) then {
           _unduplicated set [count _unduplicated, _original];
       };
   } forEach _array;
  _unduplicated
};

This code now outputs the array with MATCHING elements. Also, I changed one error in the code to what I believed it should be.

// Setup test array

_test = [[1,2,3],[1,2,3],[4,5,6]];

// call remove duplicates

_some = _test call RemoveDuplicates;

RETURNS:

[ [1,2,3], [1,2,3] ]

 for "_i" from 0 to (count _arrayA - 1) do {

should actually be...

for "_i" from 0 to (count _objectA - 1) do {

back to further debugging I go to try and trace the problem.

-----------

Final edit (for now)...

changing this line

if (not ([_original, _x] call FancyIsEqual)) exitWith {

to...

if ([_original, _x] call FancyIsEqual) exitWith {

Works for the first test... going to stress test it now to see what breaks it.

----------------------------------------------

Working code to remove duplicate entries in arrays by MuzzleFlash:

FancyIsEqual = {
   private ["_objectA","_objectB","_result","_elemB","_elemB"];
   _objectA = _this select 0;
   _objectB = _this select 1;
   //Do they have the same type?
   if (typeName _objectA != typeName _objectB) exitWith {false};
   //Are they values?
   if (typeName _objectA != typeName []) exitWith {_objectA == _objectB};
   //It must be arrays. Do they have same length?
   if (count _objectA != count _objectB) exitWith {false};
   //Process each element; assume true
   _result = true;
   for "_i" from 0 to (count _objectA - 1) do {
       _elemA = _objectA select _i;
       _elemB = _objectB select _i;
       //Process recursively
       if (not ([_elemA, _elemB] call FancyIsEqual)) exitWith {_result = false;};
   };
   _result
};  


RemoveDuplicates = {
   private ["_array", "_unduplicated", "_original","_exists"];
   _array = _this;
   _unduplicated = [];
   {
       _original = _x;
       _exists = false;

       {
           if ([_original, _x] call FancyIsEqual) exitWith {
               _exists = true;
           };
       } forEach _unduplicated;


       if (not _exists) then {
           _unduplicated set [count _unduplicated, _original];
       };
   } forEach _array;
  _unduplicated
};

// Test arrays used

_test = [[1,2,3],[1,2,3],[4,5,6],[7,8,9],[1,2,3],["a","b","c"],[4,5,6],["a","b","c"],[132.2,123.4,532.6],[132.2,123.4,532.6]];

_test = [1,1,2,2,"a","a","c","c",2.1,2.1];

// Used to output results

_unDupe = [];

_unDupe = _test call RemoveDuplicates;

hint format["%1",_unDupe];

------

Thank you MuzzleFlash for your efforts!!

Edited by _Mofo_

Share this post


Link to post
Share on other sites

Got around to attempting it myself. Good catch with _arrayA, just found that myself and also the 'not' in RemoveDuplicates should not be there.. With these codes I got it working:

FancyIsEqual = {
   private ["_objectA","_objectB","_result","_elemB","_elemB"];
   _objectA = _this select 0;
   _objectB = _this select 1;
   //Nils?
   if (isNil "_objectA") exitWith {isNil "_objectB"};  
   //Do they have the same type?
   if (typeName _objectA != typeName _objectB) exitWith {false};
   //Are they values?
   if (typeName _objectA != typeName []) exitWith {_objectA == _objectB};
   //It must be arrays. Do they have same length?
   if (count _objectA != count _objectB) exitWith {false};
   //Process each element; assume true
   _result = true;
   for "_i" from 0 to (count _objectA - 1) do {
       _elemA = _objectA select _i;
       _elemB = _objectB select _i;
       //Process recursively
       if (not ([_elemA, _elemB] call FancyIsEqual)) exitWith {_result = false;};
   };
   _result
};  


RemoveDuplicates = {
   private ["_array", "_unduplicated", "_original","_exists"];
   _array = _this;
   _unduplicated = [];
   {
       _original = _x;
       _exists = false;
       {
           if ([_original, _x] call FancyIsEqual) exitWith {
               _exists = true;
           };
       } forEach _unduplicated;
       if (not _exists) then {
           _unduplicated set [count _unduplicated, _original];
       };
   } forEach _array;
  _unduplicated
}; 

The hint'ed result are to the right:

hint str ( [ [1, [2,3]], [1, [2,3]] ] call FancyIsEqual);   true
hint str ( [ [1, [2,3]], [1, [2,4]] ] call FancyIsEqual);   false

hint str ( [ [1,2,3], [2,3,4], [1,2,3] ] call RemoveDuplicates);   [[1,2,3],[2,3,4]]
hint str ( [ [1, [2, 3]], [[1, 2],3], [1, [2, 3]] ] call RemoveDuplicates );   [[1,[2,3]],[[1,2],3]]

Yes order matters. [1,2] is not the same as [2, 1] using FancyEqual.

Share this post


Link to post
Share on other sites

Guess I shouldn't stay on the page editing as I go so I can see replys. ;-) Thank you again for your help with this... I'm sure it will be VERY useful for the community to have this function.

Share this post


Link to post
Share on other sites

Hi guys.

You forget about the other types, which can't be compared with "==" operator.

Such as "BOOL", "CODE", "DIARY_RECORD", "SCRIPT", "TASK"


#define def(name) private #name; name
#define arg(i)    (_this select (i))

func_isEqual = {

   def(_a) = arg(0);
   def(_b) = arg(1);

   if (isNil "_a") exitWith {
       isNil "_b"
   };

   if (typeName _a != typeName _b) exitWith {
       false
   };

   if (typeName _a in [
       "BOOL", "CODE", "DIARY_RECORD",
       "SCRIPT", "TASK"
   ]) exitWith {
       _a in [_b]
   };

   if (typeName _a in [
       "SCALAR", "STRING", "OBJECT", "SIDE", "GROUP",
       "TEXT", "CONFIG", "DISPLAY", "CONTROL", "TEAM_MEMBER"
   ]) exitWith {
       _a == _b
   };

   if (typeName _a != "ARRAY") exitWith {
       // namespace type and others
       false
   };

   if (count _a != count _b) exitWith {
       false
   };

   for "_i" from 0 to count _a - 1 do {
       if !([_a select _i, _b select _i] call func_isEqual) exitWith {
           false
       };
       true;
   };

};

// simplified version (and case sensitive string comparison)
func_isEqual_2 = {

   def(_a) = arg(0);
   def(_b) = arg(1);

   if (isNil "_a") exitWith {
       isNil "_b"
   };

   if (typeName _a != typeName _b) exitWith {
       false
   };

   if (typeName _a != "ARRAY") exitWith {
       _a in [_b]
   };

   if (count _a != count _b) exitWith {
       false
   };

   for "_i" from 0 to count _a - 1 do {
       if !([_a select _i, _b select _i] call func_isEqual_2) exitWith {
           false
       };
       true;
   };

};

// replace all \xA0 to \x20 after copy&paste (f..ng parser)

Edited by DenVdmj

Share this post


Link to post
Share on other sites

It was not meant to comphrensive and be able to process all caes, just the most common cases for where you might want to remove duplicates. However, yes good idea to add the incompatible types check, especially if someone is going to use the function elsewhere.

I've never had any trouble comparing BOOLs.

Another interesting case is null.. Should [objNull] == [objNull]? Might make sense from a certain viewpoint, however, may cause confusion since objNull != objNull is the 'vanilla' case.

Edited by Muzzleflash

Share this post


Link to post
Share on other sites
I've never had any trouble comparing BOOLs.

when i tried to execute "true == true" i got the error:

Error in expression <true == true>
 Error position: <== true>
 Error ==: Type Bool, expected Number,String,Object,Side,Group,Text,Config entry,Display (dialog),Control,Team member,Task,Location
Error in expression <true == true>
 Error position: <== true>
 Error Generic error in expression

Also, in other cases, for compare boolean values can be used xor:

#define __xor(a,b) (!((a)&&(b))&&((a)||(b)))

Should [objNull] == [objNull]?

hm.. yes, statement "objNull in [objNull]" be true, and this is different with "objNull == objNull". i think that this behavior is necessary to specify in the function description.

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×