Fiddi 68 Posted June 17, 2016 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
serena 151 Posted June 17, 2016 private _src = [1,5,7,3,2,9,6]; _src = _src select {_x > 5}; // [7,9,6] 1 Share this post Link to post Share on other sites
serena 151 Posted June 17, 2016 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
Fiddi 68 Posted June 17, 2016 And are either of those faster than: "_array - [_x]"? Share this post Link to post Share on other sites
kylania 568 Posted June 17, 2016 Test in game :) Type the code into the debug console and click the little speedometer button to see. 1 Share this post Link to post Share on other sites
serena 151 Posted June 17, 2016 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
sarogahtyp 1109 Posted June 17, 2016 (edited) 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 June 17, 2016 by sarogahtyp Share this post Link to post Share on other sites
Fiddi 68 Posted June 17, 2016 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
serena 151 Posted June 17, 2016 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
serena 151 Posted June 17, 2016 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 1 Share this post Link to post Share on other sites
Fiddi 68 Posted June 17, 2016 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
sarogahtyp 1109 Posted June 17, 2016 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
serena 151 Posted June 17, 2016 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
killzone_kid 1333 Posted June 17, 2016 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] 1 Share this post Link to post Share on other sites
sarogahtyp 1109 Posted June 18, 2016 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
kylania 568 Posted June 18, 2016 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
sarogahtyp 1109 Posted June 18, 2016 (edited) 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 June 18, 2016 by sarogahtyp Share this post Link to post Share on other sites
serena 151 Posted June 18, 2016 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
SilentSpike 84 Posted June 18, 2016 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
SilentSpike 84 Posted June 18, 2016 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
killzone_kid 1333 Posted June 18, 2016 Why not use the new code syntax of selectYou mean why not use what serena already suggested in the very first reply? Share this post Link to post Share on other sites
SilentSpike 84 Posted June 18, 2016 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! ;) 1 Share this post Link to post Share on other sites