Most efficient way to filter an array?

I am porting some Javascript code and am looking for the most efficient way to filter (not sort) the elements of an array.

Here’s the Javascript code:

originalArray = originalArray.filter(function(item) { return item.isStatic; });

Each element in originalArray has a boolean property (isStatic) that, if True, should be preserved. Essentially, this line of code removes every element from originalArray whose isStatic property is False.

In Xojo, given an array originalArray containing a custom class Item which has a single boolean property isStatic, how can I keep every element in the array where the element’s isStatic property is True?

Here’s my attempt. Is there a faster / better way?

// Assume originalArray contains the following: // item1.isStatic = True // item2.isStatic = True // item3.isStatic = False // item4.isStatic = False // [ item1, item2, item3, item4 ] // We want to keep items 1 and 2 in the original array. Var tmp() As Item = originalArray originalArray.ResizeTo(-1) For i As Integer = tmp.LastRowIndex DownTo 0 If tmp(i).IsStatic = True Then originalArray.AddRowAt(0, tmp(i)) // The order must be preserved. End If Next i

If you want or remove from the original array all the items that not match the condition you can:
For i as Integer= originalArray.LastRowIndex DownTo 0
if not originalArray.isStatic then originalArray.RemoveRowAt(i)
Next

Now your originalArray is filtered

otherwise if you want a new array (eventually to assign to the same as in you javascript code)
you can create a function in a module like

function filter(extends items() as Item) as item() var tmp() as item for i as integer=0 to items.lastRowIndex if items(i).isStatic then tmp.addRow items(i) next return tmp end function

You could also define a delegate function itemFilter(obj as item) as boolean
declare one or more shared function in your class like for example:

function checkIsStatic(obj as item) as boolean return obj.isStatic end function
and you filter function could be:

function filter(extends items() as Item, filterFunction as itemFilter) as item() var tmp() as item for i as integer=0 to items.lastRowIndex if filterFunction.invoke(items(i) then tmp.addRow items(i) next return tmp end function

and call with something like:
originalArray = originalArray.filter(AddressOf item.checkIsStatic)

then you code is more like what you have in JS and, more relevant, you can declare more filter function and choose which function you need.

I think that Rinaldi’s suggestion works. But for the sake of discussion, I think that there are problems with the poster’s tentative code.

tmp = originalArray

does not create an actual second array. Rather you just get a second name for the first array (originalArray in this case.)

From documentation:

So the line,

originalArray.ResizeTo(-1)

Results in the tmp array also being empty and you have lost everything.

I am also not sure why you would write:

For i As Integer = tmp.LastRowIndex DownTo 0 If tmp(i).IsStatic = True Then originalArray.AddRowAt(0, tmp(i)) // The order must be preserved. End If Next i

it seems a little awkward to be counting down to zero and adding a row at index zero when it is not clear to me why you would not just write

For i As Integer = 0 To tmp.LastRowIndex If tmp(i).IsStatic = True Then originalArray.AddRow( tmp(i)) // The order must be preserved. End If Next i

Doesn’t that also preserve the order?

By the way, I am only an intermediate programmer and am happy to be corrected.

[quote=470196:@Robert Livingston]

tmp = originalArray

does not create an actual second array. Rather you just get a second name for the first array (originalArray in this case.)

So the line,

originalArray.ResizeTo(-1)

Results in the tmp array also being empty and you have lost everything.[/quote]

Yep you’re right. I didn’t realise that was the case. I thought that tmp would be a new array referencing the elements in originalArray that could be separately modified. That’s frustrating and not what javascript does.

[quote=470193:@Antonio Rinaldi]If you want or remove from the original array all the items that not match the condition you can:
For i as Integer= originalArray.LastRowIndex DownTo 0
if not originalArray.isStatic then originalArray.RemoveRowAt(i)
Next[/quote]

I think this is probably the simplest solution since I want to modify the original array. There’s a typo in your code so here’s the working version:

For i as Integer = originalArray.LastRowIndex DownTo 0 if not originalArray(i).isStatic then originalArray.RemoveRowAt(i) Next i

Kudos for providing a filter function delegate.

Keep in mind that if the elements of the arrays are objects and you simply append them to a second array, you are appending a reference to the same object, not a copy of the object. If that’s what you need, I suggest creating a method on the object for creating a new instance and truly cloning the data.

If you want speed for large arrays and you don’t care about throwing the old data away then I would hope that adjusting the size of the array once would be quicker than removing entries multiple times. So…

  1. Allocate a new array that is the size of the original array (worse case if we don’t have anything to remove)
  2. Iterate the original array copying over only the entries you want into an incrementing position in the new array
  3. Resize the new array to the new length using the incremental number you ended on 2)
  4. Assign the new array back to the original array causing all references to the unwanted data to be lost

Here’s a dirty piece of test code:

[code]Const arraySize As Integer = 300

'set up the original array for the test
Dim originalArray() As Class1
originalArray.ResizeTo(arraySize)
For i As Integer = 0 To originalArray.LastRowIndex
originalArray(i) = New Class1
originalArray(i).isStatic = (i Mod 2 = 0)
Next

'do the work
Dim newArray() As class1

'resize our new array to the size of the original array
newArray().ResizeTo(originalArray.LastRowIndex)

Dim index As Integer = 0
For i As Integer = 0 To originalArray.LastRowIndex
If originalArray(i).isStatic = True Then
'found an entry we want to keep
newArray(index) = originalArray(i)
index = index + 1
End If
Next
'resize our new array once so its the correct size
newArray.ResizeTo(index - 1)

'assign the new array back to the old and all the data we don’t have a reference to goes poof
originalarray = newArray[/code]

I’m still trying to adjust to someone’s idea that one-dimensional arrays have “rows” :confused: