Finding multiple elements in an array

Maybe a dumb question, but what is the most efficient way of “querying” an array to find multiple matching elements and end up with an array of only the “selection” of matching elements found?
Thanks.

You should use Collections for this.

1 Like

Bob-

Perhaps something like this:

C_LONGINT($vlPosition)
C_TEXT($vtMyValue)

ARRAY TEXT($atMyArray;0)`of course, we expect this array to already be populated
ARRAY TEXT($atMyNewArray;0)

$vtMyValue:=“FindThisValue"

While (Find in sorted array($atMyArray;$vtMyValue;>;$vlPosition))
APPEND TO ARRAY($atMyNewArray;$atMyArray{$vlPosition})
End while

Mike

Thanks, Mike…

That’s exactly what I came up with, although yours is a little more elegant :blush:

Thanks for the reassurance.

Bob

1 Like

Why using collections? Without any details? How it can be a solution for that? Strange…
For me, Mike’s answer is the solution.

I think it is related the fact that the original post used the verb “query”.

Find in array or Find in sorted array is good enough for a simple match, but for a more complex query condition, collection methods such as filter() reduce() or even query() offers more flexibility.

2 Likes

If you can use collections -

C_Collection($Matched)
$Matched:=$Original.query(“Value = :1”;$myFindValue)

OR, if you have arrays and need to return arrays -
Array Text($MyNewArray;0)
C_Collection($Original;$Matched)
$Original:=new collection
Array to collection($Original;$Origin_Array;“Value”)
$Matched:=$Original.query(“Value = :1”;$myFindValue)
Collection to array($Matched;$MyNewArray;“Value”)

1 Like

Hi,
I did some testing… for speed.

4D v18 R2. Test on array, then on Collection.

  // Fill array
C_LONGINT($tt;$i)
$tt:=1000000
ARRAY LONGINT($T_source;$tt)  //of course, we expect this array to already be populated
For ($i;1;$tt)
	$T_source{$i}:=Random%100
End for 
SORT ARRAY($T_source;>)

  // With Array
C_LONGINT($myValue;$value)
$myValue:=3
C_LONGINT($progress_id)
$progress_id:=wog_progress_new ("Fill in source")
wog_progress_setTitleBold ($progress_id;"Find...")
ARRAY LONGINT($T_target;0)
C_LONGINT($start;$end;$tt_array;$tt_coll;$ticks_array;$ticks_coll)
$start:=Tickcount
For ($i;1;$tt)
	$value:=$T_source{$i}
	If ($value=$myValue)
		APPEND TO ARRAY($T_target;$value)
	End if 
End for 
$end:=Tickcount
$ticks_array:=$end-$start
wog_progress_quit ($progress_id)
$tt_array:=Size of array($T_target)

  // With Collection
C_LONGINT($myValue)
$myValue:=3
C_LONGINT($progress_id)
$progress_id:=wog_progress_new ("Fill in source")
wog_progress_setTitleBold ($progress_id;"Find...")
ARRAY LONGINT($T_target;0)

$start:=Tickcount
C_COLLECTION($Original;$Matched)
$Original:=New collection
ARRAY TO COLLECTION($Original;$T_source;"Value")
$Matched:=$Original.query("Value= :1";$myValue)
COLLECTION TO ARRAY($Matched;$T_target;"Value")
$end:=Tickcount
$ticks_coll:=$end-$start
wog_progress_quit ($progress_id)
$tt_coll:=Size of array($T_target)

If ($tt_array#$tt_coll)
	TRACE  // Pb on search !!!
End if 
wog_io_alert ("found "+String($tt_coll)+"/"+String($tt);"Array: "+String($ticks_array)+" ; Coll: "+String($ticks_coll))

Result in ticks.
Non compiled
image

I’ll check in compiled and’ll tell you.

Compiled !
image

So for sure Arrays are better for simple search.
If you need a complex search then Collection are good.

Since you have the test bed already done, how about timing this line only in compiled

$Matched:=$Original.query(“Value = :1”;$myFindValue)

This is working with collections only and without the conversion between arrays and collections, would be an interesting test…

I decided to test it myself, and I can second your finding. I also tried to see if looping with a FOR EACH through the collection will get closer to the array speed, but it appears to take about the same time as the query.

Given that results, I also wanted to see if it’s worth converting a given collection to an array to run this kind of simple searches and it still came up about the same.

So, if indeed your data is in an array, and your search is simple, stick with the looping through the array. If your data is in a collection, or your search is complex, the collection route is faster.

Thanks for pushing the issue :slight_smile:

This is consistent with my experiences too. For raw processing speed 4D classic almost always has an edge. It likely won’t last.

Collection.join() is already a step beyond…