Android Question Can Maps use a custom type as key?

Jeffrey Cameron

Well-Known Member
Licensed User
I've created a simple (Int, Int) type to use a key into a map, I've discovered that if I add an item to the map with a key value of (1,1) and at a future time I make a new key with the same value, it doesn't find it in the map, for example:
B4X:
Sub Activity_Resume
    ' Point type is defined elsewhere as: Type Point(X As Int, Y As Int)
    Dim poPt1 As Point
    Dim poPt2 As Point
    Dim poMap As Map
    Dim poMap2 As Map
    Dim psVal As String
    Dim piVal1 As Int
    Dim piVal2 As Int
  
    poMap.Initialize
    poPt1.Initialize
    poPt1.X = 10
    poPt1.Y = 14
    poMap.Put(poPt1, "testing 1, 2, 3")

    poPt2.Initialize
    poPt2.X = 10
    poPt2.Y = 14
    psVal = poMap.Get(poPt2)
    Log(psVal)  'outputs ->null

    piVal1 = 5
    poMap2.Initialize
    poMap2.Put(piVal1, "Testing 4, 5, 6")
    piVal2 = 5
    psVal = poMap2.Get(piVal2)
    Log(psVal) 'outputs ->Testing 4, 5, 6
End Sub
Am I missing something, or will I need to make the map(Int,value) and create a lookup function to translate the ordered pair?
 

emexes

Well-Known Member
Licensed User
The key stored in the map will likely be a pointer to (memory address of) the first Point (poPt1) that you created. When you later create another Point (poPt2) it will be at a different address, and thus you can't use that address as a key to find the first one.

But you could just combine the X and Y into one key value as a string rather than a type, eg this should work:

B4X:
Sub Activity_Resume
    ' Point type is defined elsewhere as: Type Point(X As Int, Y As Int)

    Dim poPt1 As String
    Dim poPt2 As String
    Dim poMap As Map
    Dim poMap2 As Map
    Dim psVal As String
    Dim piVal1 As Int
    Dim piVal2 As Int
 
    poMap.Initialize

    'poPt1.Initialize
    'poPt1.X = 10
    'poPt1.Y = 14
    poPt1 =10 & "," & 14    'or even just "10,14"
    poMap.Put(poPt1, "testing 1, 2, 3")

    'poPt2.Initialize
    'poPt2.X = 10
    'poPt2.Y = 14
    poPt2 = 10 & "," & 14    'or even just "10,14"
    psVal = poMap.Get(poPt2)

    Log(psVal)  'outputs ->null BUT HOPEFULLY, NOT ANY MORE

End Sub
Now that I think about it, your type may well be convertible to a string, in which case this might work, using your Point type:
B4X:
poMap.Put("" & poPt1, "testing 1, 2, 3")
...
psVal = poMap.Get("" & poPt2)
or if that doesn't work but you're keen to keep the Point type, this will work:
B4X:
Sub PointToString(P as Point) As String    'or PointToMapKey or PointToKeyString or whatever you like best
    Dim S As String = P.X & "," & P.Y
    'Log("MapKey = """ & S & """")
    Return S
End Sub

'and then use with Map keys eg:

poMap.Put(PointToString(poPt1), "testing 1, 2, 3")

psVal = poMap.Get(PointToString(poPt2))
What could possibly go wrong?!?!
 

Jorge M A

Active Member
Licensed User
The same approach with jSON string in the key, and deconstruction to Point type, preserving original declarations:

B4X:
Sub Process_Globals
    Type Point(X As Int, Y As Int)
End Sub

Sub Activity_Resume
    ' Point type is defined elsewhere as: Type Point(X As Int, Y As Int)
    Dim poPt1 As Point
    Dim poPt2 As Point
    Dim poMap As Map
    Dim poMap2 As Map
    Dim psVal As String
    Dim piVal1 As Int
    Dim piVal2 As Int
    
    
    poMap.Initialize
    poPt1.Initialize
    poPt1.X = 10
    poPt1.Y = 14
    
    'Put the key of Point struct in jSON format
    poMap.Put( pointToJSONString (poPt1) , "testing 1, 2, 3")
    
    poPt2.Initialize
    poPt2.X = 10
    poPt2.Y = 14

    psVal = poMap.Get( pointToJSONString (poPt2) )
    
    Log(psVal)  'outputs -> testing 1, 2, 3

    piVal1 = 5
    poMap2.Initialize
    poMap2.Put(piVal1, "Testing 4, 5, 6")
    piVal2 = 5
    psVal = poMap2.Get(piVal2)
    
    Log(psVal) 'outputs ->Testing 4, 5, 6
    
    '************************
    'Retrieve key as Custom Type Point in jSon format
    Log("Key: " & poMap.GetKeyAt(0))
    
    'Convert jSON to Point
    Dim pt As Point = jSonToPoint( poMap.GetKeyAt(0) )
    
    Log(pt.X)
    Log(pt.Y)
    '************************
    
End Sub

Sub pointToJSONString(pt As Point) As String
    Return "{X:" & pt.X & ",Y:" & pt.Y & "}"
End Sub

Sub jSonToPoint(key As String) As Point
    Dim JSON As JSONParser
    JSON.Initialize(key)
    Dim m As Map
    Dim p As Point
    m = JSON.NextObject
    p.X=m.Get("X")
    P.Y=m.Get("Y")
    Return p
End Sub
 

Jeffrey Cameron

Well-Known Member
Licensed User

Thanks to you all, and I did see the "should be a string or number" but was hoping it would behave more as .NET does.
But you could just combine the X and Y into one key value as a string rather than a type, eg this should work:
If I'm going to have to wrap the get/put I could just as easily compute the X/Y into an index offset similar to single-dimension array. I don't need every X*Y entry, just some and was hoping to keep the storage and retrieval simple.
 

emexes

Well-Known Member
Licensed User
... an index offset similar to single-dimension array. I don't need every X*Y entry, just some ...
I agree: if the array is not too sparse, that is probably a better (and faster) solution than using a Map.

One thing I particularly miss from other BASIC dialects is the the ability to specify the lower bound of an array, eg:

Dim Angle(-1 to +1, -1 to +1)

or

Dim FirstDayOfMonth(1970 to 2030, 1 to 12)

but if you're willing to calculate an offset, then you're freed from Java's arrays-start-at-zero constraint :)
 
Top