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 thats 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…
- Allocate a new array that is the size of the original array (worse case if we don’t have anything to remove)
- Iterate the original array copying over only the entries you want into an incrementing position in the new array
- Resize the new array to the new length using the incremental number you ended on 2)
- 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”