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 tables 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? Shouldnt 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 shouldnt 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. Thats nice and fast enough, so Ill 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 didnt oversee some silly basic stuff), I guess I should file a feedback entry.
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 wasnt aware of the difference in variant handling. I had the feeling it could be connected to a ptr instead of a value compare, but didnt know it is the case.
Good to know a dictionary is the solution. Id 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. Id rather have not two different handlings all over the project, except for when the dictionary should be too slow. Which doesnt seem so currently.
Youre right. I guess building a dictionary takes a bit longer than filling an array Xojo arrays are really fast , but I havent checked it. And yes, even if it should be so the drawback is very probably compensated by the fast dictionary find methods.