B4J Question For Each/Next List of Maps and references. What am I misunderstanding?

OliverA

Expert
Licensed User
B4J Version: 7.51
OS Platfrom: Win10 Pro (1903)
JDK: 11.0.1 downloaded from B4X's site

Here's the setup. I'm creating a list of maps that contain x, y values (note: in my case, the map contains way more info, I'm just simplifying it here to show the issue I'm running into)
B4X:
    Dim largestX As Int = 4
   Dim mapList As List
   mapList.Initialize
   For x = 0 To largestX
       Dim aMap As Map
       aMap.Initialize
       aMap.Put("x", x)
       aMap.Put("y", x+1)
       mapList.Add(aMap)
   Next
   Log("Dump of mapList:")
   For Each tempMap As Map In mapList
       Log($"(x,y) = (${tempMap.Get("x")},${tempMap.Get("y")})"$)
   Next
Output:
Dump of mapList:
(x,y) = (0,1)
(x,y) = (1,2)
(x,y) = (2,3)
(x,y) = (3,4)
(x,y) = (4,5)
Now I'm going to try to find the map that contains the smallest x amount:
B4X:
    Log("Let's find the smallest x value. It should be 0")
   Dim smallestXmap As Map
   Dim smallestX As Int = largestX
   For Each temp2Map As Map In mapList
       If temp2Map.Get("x") < smallestX Then
           Log("Found a smaller x!")
           smallestX = temp2Map.Get("x")
           smallestXmap = temp2Map
       End If
   Next
   Log($"Smallest x value is ${smallestX}"$)
   Log("The log entry below should show content of (0,1), but it does not. Why?")
   Log($"Map containing smallest x value has following content: (${smallestXmap.Get("x")},${smallestXmap.Get("y")})"$)
And the ouput is:
Let's find the smallest x value. It should be 0
Found a smaller x!
Smallest x value is 0
The log entry below should show content of (0,1), but it does not. Why?
Map containing smallest x value has following content: (4,5)
Why? Why are the contents of smallestXmap 4,5? If temp2Map is just a reference and stays the same, then why does the following code work?
B4X:
    'https://www.b4x.com/android/forum/threads/maps-for-each-but-backwarts-like-step-1.64131/#post-405821
   Log("The list below is produced properly. What is the difference (besides code)?")
   Dim itemsToRemove As List
   itemsToRemove.Initialize
   For Each temp3Map As Map In mapList
       If temp3Map.Get("x") > 0 And temp3Map.Get("x") < largestX Then
           itemsToRemove.Add(temp3Map)
       End If
   Next
   For Each temp4Map As Map In itemsToRemove
       Log($"Map with content (${temp4Map.Get("x")}, ${temp4Map.Get("y")}) flagged for removal"$)
   Next
Output:
The list below is produced properly. What is the difference (besides code)?
Map with content (1, 2) flagged for removal
Map with content (2, 3) flagged for removal
Map with content (3, 4) flagged for removal
Would I not be adding the same temp3Map reference to the list and get (3,4) for all three logs?
And why does this modified version produce a correct output if an intermediate list is used?
B4X:
    Log("Let's find the smallest x value, try#2. It should be 0")
   Dim smallestXmap2 As Map
   Dim smallestX2 As Int = largestX
   Dim smallestXList As List
   smallestXList.Initialize
   smallestXList.Add("Filler")
   smallestXList.Add("Filler")
   For Each temp5Map As Map In mapList
       If temp5Map.Get("x") < smallestX2 Then
           Log("Found a smaller x!")
           smallestX2 = temp5Map.Get("x")
           smallestXmap2 = temp5Map
           smallestXList.Set(0,temp5Map)
           'Just for kicks
           smallestXList.Set(1,smallestXmap2)
       End If
   Next
   Log($"Smallest x value is ${smallestX2}"$)
   Log("The log entry below should show content of (0,1), but it does not. Why?")
   Log($"Map containing smallest x value has following content: (${smallestXmap2.Get("x")},${smallestXmap2.Get("y")})"$)
   Log("Extracting map from smallestXList list")
   Dim lastTempMap As Map
   lastTempMap = smallestXList.Get(0)
   Log($"Map containing smallest x value has following content: (${lastTempMap.Get("x")},${lastTempMap.Get("y")})"$)
   Log("Why did this work?")
   Dim justForFun As Map
   justForFun = smallestXList.Get(1)
   Log("Retrieving smallestXmap2 from list")
   Log($"Map containing smallest x value has following content: (${justForFun.Get("x")},${justForFun.Get("y")})"$)
   Log("This works too!!! I'm confused (Note: It should, but then why does the previous smallestXmap2 log produce it's result?).")
Output:
Let's find the smallest x value, try#2. It should be 0
Found a smaller x!
Smallest x value is 0
The log entry below should show content of (0,1), but it does not. Why?
Map containing smallest x value has following content: (4,5)
Extracting map from smallestXList list
Map containing smallest x value has following content: (0,1)
Why did this work?
Retrieving smallestXmap2 from list
Map containing smallest x value has following content: (0,1)
This works too!!! I'm confused (Note: It should, but then why does the previous smallestXmap2 log produce it's result?).
Program terminated (StartMessageLoop was not called).
What am I not seeing/understanding? I've lost over 1/2 of a day on this and it's bugging me.
 

Attachments

OliverA

Expert
Licensed User
There is a hint in there.... (init)
You're going to need to use a sledgehammer on me, I'm currently that dense. I just don't see it. I need my face rubbed into it. It's pretty sad and frustrating that I just don't get it.
 

Enrique Gonzalez R

Well-Known Member
Licensed User
i think i get it...

it is related to memory pointers....

B4X:
For Each temp2Map As Map In mapList
this one is is iterating over each temp2map and asigning different pointers to temp2map.

when you do this:

B4X:
smallestXmap = temp2Map
you are saying that smallestXmap is pointing to the same memory address as temp2map.

with primitives you dont get that issue, primitives are compared for their value but complex strucutures are compared to their memory address....

change it like this:

B4X:
    For i = 0 To mapList.Size -1
       Dim temp2Map As Map = mapList.Get(i)
you will get your expected value.

why? easy.. now each temp2map is now getting different map pointers each time, not reasigning the pointer to the variable.
 

OliverA

Expert
Licensed User
you are saying that smallestXmap is pointing to the same memory address as temp2map.
smallestXList.Set(0,temp5Map)
Then why is this working? Technically I'm saying that List item #0 is temp5Map, a pointer. Therefore at the end it should also produce the incorrect result, but it does not.

If temp3Map.Get("x") > 0 And temp3Map.Get("x") < largestX Then
itemsToRemove.Add(temp3Map)
End If
How does this work then? I'm not doing a Dim temp3Map as Map = mapList.Get(i) here, yet each Add adds a different map, instead of a pointer that in the end points to the same map.
 

Enrique Gonzalez R

Well-Known Member
Licensed User
I do not see how the 2 examples are related. On the items to remove you are deleting that specific pointer. If later the pointer is reassigned doesn't mean it's going to be deleted automatically.

Maybe the difference of the 3 examples is that on the first you are not calling any function. On the second you are calling to functions. Remove and set.

Functions can handle pointers but only the one you are passing not every time you reassign a pointer.
 

OliverA

Expert
Licensed User
Functions can handle pointers but only the one you are passing not every time you reassign a pointer.
Interesting.
B4X:
Sub returnMap(aMap As Map) As Map
   Dim secondMap As Map
   secondMap = aMap
   Return secondMap
End Sub

Sub returnMap2(aMap As Map) As Map
   Dim aList As List
   aList.Initialize
   aList.Add(aMap)
   Return(aList.Get(aList.IndexOf(aMap)))
End Sub

Sub returnMap3(aObject As Object) As Map
   Dim secondMap As Map
   secondMap = aObject
   Return secondMap
End Sub

Sub returnMap4(aObject As Object) As Map
   Return aObject
End Sub
Usage example: smallestMap = returnMap(temp2Map)

returnMap produces the same erroneous result, but returnMap2, returnMap3, and returnMap4 work.

And yes, for each/next is just like doing a for x = without redimming the temp map:
B4X:
    Dim temp6Map As Map
   Dim smallestXmap3 As Map
   Dim smallestX3 As Int = largestX
   For x = 0 To largestX
       temp6Map = mapList.Get(x)
       If temp6Map.Get("x") < smallestX3 Then
           Log("Found a smaller x!")
           smallestX3 = temp6Map.Get("x")
           smallestXmap3 = temp6Map
       End If
   Next
It's interesting that passing the map as an object to a method and returning it as a map correctly de-references the map. I guess I've never used a for each next loop without assigning the object retrieved with the each clause to a list, and therefore never ran into this problem. BTW, this works when using For Each/Next:
B4X:
    Log("Let's find the smallest x value. It should be 0")
   Dim tempMap98 As Map
   Dim smallestXmap99 As Map
   Dim smallestX99 As Int = largestX
   For Each tempObject As Object In mapList       ' Retrieve items from list as Object
       tempMap98 = tempObject                          ' Cast Object to Map
       If tempMap98.Get("x") < smallestX99 Then
           Log("Found a smaller x!")
           smallestX99 = tempMap98.Get("x")
           smallestXmap99 = tempObject                ' Here assign the Object, not the Map
       End If
   Next
   Log($"Smallest x value is ${smallestX99}"$)
   Log("The log entry below should show content of (0,1), but it does not. Why?")
   Log($"Map containing smallest x value has following content: (${smallestXmap99.Get("x")},${smallestXmap99.Get("y")})"$)
Thanks for being a sounding board. It's finally clear as a bell and makes perfect sense (and somehow I have the feeling I've seen this before).

BTW, an alternative to re-dimming temp6Map within the For x = 0 / Next loop above is to assign mapList.Get(x) to smallestXmap3 instead of temp6Map. Again, it makes sense now.
 
Top