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

Discussion in 'B4J Questions' started by OliverA, Sep 9, 2019.

  1. OliverA

    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)
    Code:
    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:
    Now I'm going to try to find the map that contains the smallest x amount:
    Code:
    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:
    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?
    Code:
    '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:
    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?
    Code:
    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:
    What am I not seeing/understanding? I've lost over 1/2 of a day on this and it's bugging me.
     

    Attached Files:

  2. Harris

    Harris Well-Known Member Licensed User

  3. OliverA

    OliverA Expert Licensed User

    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.
     
  4. Enrique Gonzalez R

    Enrique Gonzalez R Well-Known Member Licensed User

    i think i get it...

    it is related to memory pointers....

    Code:
    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:

    Code:
    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:

    Code:
    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 likes this.
  5. OliverA

    OliverA Expert Licensed User

    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.
    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.
     
  6. Enrique Gonzalez R

    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 likes this.
  7. OliverA

    OliverA Expert Licensed User

    Interesting.
    Code:
    Sub returnMap(aMap As MapAs Map
       
    Dim secondMap As Map
       secondMap = aMap
       
    Return secondMap
    End Sub

    Sub returnMap2(aMap As MapAs 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:
    Code:
    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:
    Code:
    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.
     
    Harris and Enrique Gonzalez R like this.
  8. Enrique Gonzalez R

    Enrique Gonzalez R Well-Known Member Licensed User

    glad to be of help!
     
    Harris and OliverA like this.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice