Jump to content
Sign in to follow this  
Fiddi

deleteAt and _forEachIndex clarification needed.

Recommended Posts

Hi, I'm working on optimizing my code. I have an array which needs to remove elements on a regular basis.
 
I use an forEach to test each element for a condition and if it fills that it gets removed from the array.
 
For now I use a simple " _array - [_x]" but I wanted to use deleteAt with _forEachIndex like so:
 
_array deleteAt _forEachIndex;
 
 
But read it could be unreliable, exactly how reliable is it? Is there a threshold where it becomes unstable or is it always unstable?
 
Exact quote from Wiki:

Deleting from an array with foreach and _foreachIndex variable is tricky. The array is being altered, the _foreachIndex won't keep up and other elements in the array will be skipped and in worst case not being deleted. If you delete elements from an array in descending order (using while or for) it will work.

 

He speaks of backwards. Is it possible to run a forEach backwards?
Thanks.

Share this post


Link to post
Share on other sites


private _src = [1,5,7,3,2,9,6];

_src = _src select {_x > 5}; // [7,9,6]

  • Like 1

Share this post


Link to post
Share on other sites

Or (if you don't like easy ways):

Fn_FilterArrayElements = {
	private _arr = _this select 0;
	private _fnc = _this select 1;
	private _sze = 0;
	
	{
		if (_x call _fnc) then {
			if (_sze < _forEachIndex) then {
				_arr set [_sze, _x]};
			_sze = _sze + 1}
	} forEach _arr;
	_arr resize _sze;
	_sze
};

private _src = [1,5,7,3,2,9,6];
private _sze = [_src, {_x > 5}] call Fn_FilterArrayElements; //Result: _sze = 3; _src = [7,9,6];

Share this post


Link to post
Share on other sites

And are either of those faster than: "_array - [_x]"?

Share this post


Link to post
Share on other sites

Test in game :)  Type the code into the debug console and click the little speedometer button to see.

  • Like 1

Share this post


Link to post
Share on other sites
Fn_FilterArrayElements = {
	private _arr = _this select 0;
	private _fnc = _this select 1;
	private _sze = 0;
	
	{
		if (_x call _fnc) then {
			if (_sze < _forEachIndex) then {
				_arr set [_sze, _x]};
			_sze = _sze + 1}
	} forEach _arr;
	_arr resize _sze;
	_sze
};


Fn_MeasuredExec = {
	private _arg = _this select 0;
	private _fnc = _this select 1;
	private _stt = diag_tickTime;
	_arg call _fnc;
	diag_tickTime - _stt
};


Fn_RunTests = {
	// prepare source data
	private _fnc = {_x > 5};
	private _src = [];
	for "_x" from 0 to 100000 do {
		_src pushBack (round random 10)};
		
	// test of original method (source_array - excluded_elements_array)
	private _et0 = [[_src, _fnc], {
		params ["_src", "_fnc"];
		private _exc = _src select {not (_x call _fnc)};
		private _res = _src - _exc;
		_res}] call Fn_MeasuredExec;
	
	// test of method using select command
	private _et1 = [[_src, _fnc], {
		params ["_src", "_fnc"];
		private _res = _src select {_x call _fnc};
		_res}] call Fn_MeasuredExec;
	
	
	// test of method using Fn_FilterArrayElements
	private _et2 = [[_src, _fnc], {
		params ["_src", "_fnc"];
		private _res = [_src, _fnc] call Fn_FilterArrayElements;
		_src}] call Fn_MeasuredExec;
		
	[_et0, _et1, _et2]
};

And results: [85.7969,0.490234,1.04102]

 

85.7ms - original method with array substraction

0.49ms - method using select command

1.04ms - method using Fn_FilterArrayElements algorithm

Share this post


Link to post
Share on other sites

I think this could be used for deleting but idk how fast it is:

_elements = _your_array select { your_condition};
 _index = _your_array find (_elements select 0);
 _your_Array deleteAt _index;

or this if select will find more than one element:
 

_elements = _your_array select { your_condition};

{
 _index = _your_array find _x;
 _your_Array deleteAt _index;
 true
} count _elements;
Edited by sarogahtyp

Share this post


Link to post
Share on other sites

Wow, didn't think it would make that much of a difference! Thanks for testing for me :)

 

So, how do I adapt the select command to choose the current forEach element for removal?

 

 

Though I tested using, "_array deleteAt _forEachIndex" and could not notice any problems, atleast not yet...

Share this post


Link to post
Share on other sites

Test in game :)  Type the code into the debug console and click the little speedometer button to see.

 

It is not so easy to test using the debug console as it seems at first glance. Because at least the second method modifies the original array, and honest measurements you want to exclude the costs of preparing the initial data

Share this post


Link to post
Share on other sites

Thanks for testing for me :)

 

The highest reward for me if my examples will help not just to feed the hungry, but teach them to fish on their own

 

 

Though I tested using, "_array deleteAt _forEachIndex" and could not notice any problems, atleast not yet...

 

Modification structure of source array inside of iteration cycle leads to unpredictable results

  • Like 1

Share this post


Link to post
Share on other sites

Modification of source array inside of iteration cycle leads to unpredictable results

 

But then this whole thread is pointless, because that's exactly what I'm hoping to do...

Share this post


Link to post
Share on other sites

But then this whole thread is pointless, because that's exactly what I'm hoping to do...

 

my last post showed a method to delete selected element from the array without any  unpredictable results ...

 

EDIT:   ... because the method is not looping the array in which elements are deleted

Share this post


Link to post
Share on other sites

But then this whole thread is pointless, because that's exactly what I'm hoping to do...

 

Pointless is only idea delete array elements inside forEach loop. My examples works fine

Share this post


Link to post
Share on other sites

If it is just 1 element you want to delete there is no problem with using deleteAt in forEach loop:
I have also added note here to explain the mechanics https://community.bistudio.com/wiki/forEach
 

_arr = [1,2,3,4,5,6,7,8,9];
{
	if (_x == 5) exitWith 
	{
		_arr deleteAt _forEachIndex;
	};
} 
forEach _arr; 
hint str _arr; //[1,2,3,4,6,7,8,9]
  • Like 1

Share this post


Link to post
Share on other sites

Cant test it right now but is it possible to manipulate _forEachIndex this way?

 

_arr = [1,2,3,4,5,6,7,8,9];
{
	if (_x == 5) exitWith 
	{
		_arr deleteAt _forEachIndex;
                _forEachIndex = _forEachIndex - 1;
	};
} 
forEach _arr; 

if its possible then that could be the way how elements of an array can be deleted while looping through the array.

Share this post


Link to post
Share on other sites

What are you trying to do with that?  The original code already would just remove 5 from that array.  I also feel that forEachIndex would be reset to the proper next loop count value for each loop, or else it kind of loses it's purpose right?  How can you have a "current index" if you're able to adjust it all willy nilly. :)

Share this post


Link to post
Share on other sites

What are you trying to do with that? The original code already would just remove 5 from that array. I also feel that forEachIndex would be reset to the proper next loop count value for each loop, or else it kind of loses it's purpose right? How can you have a "current index" if you're able to adjust it all willy nilly. :)

it was just an example. the if condition could be anything.

i think it would be nice if u could adjust the index because it would solve ops problem... also it would be possible to do wild jumps inside of such loops. i d like the idea. i think u would not say that a fast forward or backward button is senseless at a video recorder?

EDIT: okay ... I did a mistake in the above code which I just copied from

KK and modified then.

thats what I meant:

_arr = [1,2,3,4,5,6,7,8,9];
{
	if (any_condition) then
	{
		_arr deleteAt _forEachIndex;
                _forEachIndex = _forEachIndex - 1;
	};
} 
forEach _arr; 
Edited by sarogahtyp

Share this post


Link to post
Share on other sites

Algorithm with elements removal one by one is pointless for the simple reason - it has a huge cost in performance. Each time you remove element from the beginning of array requires moving all the following elements up. This operation is hidden behind the magic of deleteAt command, which creates a false sense of cheapness. In posts 2 and 3 of this topic are shown working and performance/memory-effective algorithms

Share this post


Link to post
Share on other sites

Why not use the new code syntax of select:

 

_arr = [1,2,3,4,5,6,7,8,9];

_arr = _arr select { !any_condition };

Edit:

We also had issues with this in ACE and I believe we're currently using

_arr deleteAt (_arr find _x)
Not sure if there's been a performance test against the new select syntax yet

Share this post


Link to post
Share on other sites

Totally missed that serena covered my first point already as I only read the most recent posts  :lol:

 

My bad!  :ph34r:

Share this post


Link to post
Share on other sites

Why not use the new code syntax of select

You mean why not use what serena already suggested in the very first reply?

Share this post


Link to post
Share on other sites

You mean why not use what serena already suggested in the very first reply?

 

Precisely! It does sound better when you repeat what I already said!  ;)

  • Like 1

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  

×