Why does an array not find variant values a dictionary finds?

I am working on a database solution making SQLite a bit more oop.
Currently, I am refining controls that automate database access. For the listbox subclass, I want it to redraw when the selection has changed without deselecting rows if they still appear after redraw.
I put the SQLite table’s primaryField value as a variant into the rowtag. Before redraw I capture the selected rows:

Function SelectedIndexes() As Variant() dim count as integer = me.ListCount-1 dim values() as variant for q as integer = 0 to count if me.Selected(q) then values.Append rowtag(q) next return values End Function

And after the table is redrawn, I tried to reselect them this way:

Sub SelectIndexes(Indexes() as variant) dim count as integer = me.ListCount-1 for q as integer = 0 to count if indexes.IndexOf( RowTag(q)) > -1 then // <- never returns true! me.Selected(q) = true end if next End Sub

But the IndexOf clause is never true.
The variants are of the same Type, and I checked manually that the variant array contains values that should return a true for this line.

I then tried with a new framework dictionary:

Function SelectedIndexesDict() As xojo.Core.Dictionary dim count as integer = me.ListCount-1 dim values as new xojo.core.dictionary for q as integer = 0 to count if me.Selected(q) then values.Value( rowtag(q)) = nil // only need the key next return values End Function

And if I pass the result to this method:

Sub SelectIndexes(Indexes as xojo.Core.Dictionary) dim count as integer = me.ListCount-1 for q as integer = 0 to count if indexes.HasKey( RowTag(q)) then me.Selected(q) = true end if next End Sub

It all works and the rows stay selected. But why? Shouldn’t the array IndexOf do the same?

Shouldn’t the array IndexOf do the same?

Yes.
Try Appending “XXX” to the start of the primary key to force it to be a string.
If the index is numeric, just maybe you are losing leading zeroes or something similar

Thanks, Jeff!
The index in this case was an int64. There shouldn’t be any leading 0s on an integer value, only on its string representation, and I checked the variants are all of type 3 = Int64.
The dictionary method works, and I can easily remove an entry if it matched and have a shortcut to skip the loop if the dictionary is empty. That’s nice and fast enough, so I’ll go with this solution. I know from time to time I tend to make stupid mistakes. If you agree the first method should work too (in other words I didn’t oversee some silly basic stuff), I guess I should file a feedback entry.

Variants are like Objects.

ObjectArray.IndexOf(obj) searches for a reference to obj. It won’t find an object containing the same property values, it’ll only match to the same instance. While Variants aren’t objects they sort of are, like a class with Operator_Converts.


//want to find integer 7
dim targetValue As integer = 7

//build array of variants containing integers
dim varr() As Variant
for i As integer = 0 to 20
  varr.Append(i)
next

dim idx As integer

//search array with integer, not found
idx = varr.IndexOf(targetValue) //-1 
log "integer: " + Str(idx)

//add a variant and search for _that_ variant, found
dim v As Variant = targetValue
varr.Append(v)
idx = varr.IndexOf(v) //21
log "v: " + Str(idx)

//search with a reference copy of _that_ variant, found
dim vcopy As Variant = v
idx = varr.IndexOf(vcopy) //21
log "vcopy: " + Str(idx)

//search with a 'new' variant, not found
dim vother As Variant = targetValue
idx = varr.IndexOf(vother) //-1
log "vother: " + Str(idx)

The reason a dictionary works is it unpacks the variant and hashes it’s value. That hash is what’s being searched for. A variant array just searches ‘references’ to variants.

But to use an array give it a type that matches what the RowTags contain. The following should work (with the same type change in calling code)

[code]Function SelectedIndexes() As INT64()
//…
dim values() as INT64
//…
End Function

Sub SelectIndexes(Indexes() as INT64)
//…
End Sub[/code]

Thanks a lot, Will! I wasn’t aware of the difference in variant handling. I had the feeling it could be connected to a ptr instead of a value compare, but didn’t know it is the case.
Good to know a dictionary is the solution. I’d rather keep it then. While the array might probably be faster, my classes allow primary fields as Text fields too so you can handle UUIDs. I’d rather have not two different handlings all over the project, except for when the dictionary should be too slow. Which doesn’t seem so currently.

Dictionary should never be "slow ".

Dictionary.Value – with distinct keys as in this case – are probably even faster than IndexOf on an array.

You’re right. I guess building a dictionary takes a bit longer than filling an array – Xojo arrays are really fast –, but I haven’t checked it. And yes, even if it should be so the drawback is very probably compensated by the fast dictionary find methods.