Page 1 of 1

[Solved] ForEach loop with Maps

Posted: Sun Aug 04, 2024 7:25 am
by BarryG
I'm still getting used to maps, so the code below doesn't make sense to me. If I set the value of "icon" to 1, then ForEach/Next goes into an endless loop, but NOT if I set "icon" to 2 or 3. Why? What's so special about "1" that it doesn't stop the loop?

Reason for asking: I have a Map of all icons loaded by my app, but sometimes I'll load an icon on demand that ISN'T in the map (like "three=" below). So I need a way to work out if any given icon is in the map or not. I thought ForEach/Next could do this, but with an endless loop I can't use it. :(

Code: Select all

NewMap IconMap()

IconMap("1")=1
IconMap("2")=2
three=LoadIcon_(0,#IDI_APPLICATION) ; Purposely not in the map.

icon=1 ; Endless loop, but NOT if set to 2 or 3. Why?

ForEach IconMap()
  Debug IconMap(Str(icon))
Next

Re: ForEach loop with Maps

Posted: Sun Aug 04, 2024 7:37 am
by DarkDragon
By accessing a different key you change the current element. Store the old current element and restore it afterwards with PushMapPosition(Map()) and PopMapPosition(Map()):

Code: Select all

ForEach IconMap()
  PushMapPosition(IconMap())
  Debug IconMap(Str(icon))
  PopMapPosition(IconMap())
Next
However checking whether a key is inside a map is done via FindMapElement(Map(), Key$), you don't iterate all elements.

Re: ForEach loop with Maps

Posted: Sun Aug 04, 2024 7:57 am
by BarryG
Thanks, that helped me work out my problem! :) This was my goal:

Code: Select all

NewMap IconMap()

IconMap("1")=1
IconMap("2")=2
three=LoadIcon_(0,#IDI_APPLICATION) ; Purposely not in the map.

Debug FindMapElement(IconMap(),Str(1)) ; Non-zero means in the map.
Debug FindMapElement(IconMap(),Str(2)) ; Non-zero means in the map.
Debug FindMapElement(IconMap(),Str(three)) ; 0 means NOT in the map.

Re: ForEach loop with Maps

Posted: Sun Aug 04, 2024 9:16 am
by Bisonte

Code: Select all

Procedure.i IsInIconMap(Number)
  
  Protected Result = #False
  
  PushMapPosition(IconMap())
  
  If FindMapElement(IconMap(), Str(Number))
    Result = #True
  EndIf
  
  PopMapPosition(IconMap())
  
  ProcedureReturn Result
  
EndProcedure
Where IconMap() is global....

else

Code: Select all

Procedure.i IsInIconMap(Map IconMap(), Number)

Re: [Solved] ForEach loop with Maps

Posted: Sun Aug 04, 2024 9:34 am
by BarryG
Thank you, Bisonte. :)

Re: [Solved] ForEach loop with Maps

Posted: Sun Aug 04, 2024 12:30 pm
by boddhi
Hello,

For further information (just in case),
We need to be careful, as some operations can implicitly create an element without an assignment:

Code: Select all

NewMap IconMap()

IconMap("1")=1
IconMap("2")=2

Debug FindMapElement(IconMap(),"3")

If IconMap("3"):EndIf

Debug FindMapElement(IconMap(),"3")
 
ALWAYS use FindMapElement() to check if a key really exists.

Re: [Solved] ForEach loop with Maps

Posted: Tue Aug 06, 2024 9:08 am
by Mesa
It's a bug, you should inform fred on the appropriate forum.

M.

Re: [Solved] ForEach loop with Maps

Posted: Tue Aug 06, 2024 10:17 am
by mk-soft
Mesa wrote: Tue Aug 06, 2024 9:08 am It's a bug, you should inform fred on the appropriate forum.

M.
The behaviour was changed in this point because it led to other problems in other points.
FindMapElement must be used explicitly.

Re: [Solved] ForEach loop with Maps

Posted: Tue Aug 06, 2024 11:17 am
by boddhi
mk-soft wrote: FindMapElement must be used explicitly.
As it's not explicitly explained in the (French, anyway) documentation, knowing this would have saved me, some time ago, the trouble of trying to understand why one of my codes wasn't working properly! :mrgreen:

Re: [Solved] ForEach loop with Maps

Posted: Tue Aug 06, 2024 10:12 pm
by AZJIO
I always use AddMapElement() and have never encountered a problem yet. I think you are trying to shorten the entry using simplified assignment.

You list the map elements

Code: Select all

ForEach IconMap()
  Debug IconMap()
Next
When you set IconMap("1") in a loop, you probably reset the current position of the element listing, you have "1" every time, and always strive for "2".

Code: Select all

Dim Arr(2)

Arr(0) = 1
Arr(1) = 2
Arr(2) = 3

For i = 0 To 2
	Debug Arr(i)
Next

; Now guess why you always have the same number infinitely
For i = 0 To 2
	i=1
	Debug Arr(i)
Next

Code: Select all

NewList IconList()
AddElement(IconList())
IconList() = 1
AddElement(IconList())
IconList() = 2

ForEach IconList()
	Debug IconList()
Next

; Now guess why you always have the same number infinitely
ForEach IconList()
	SelectElement(IconList() , 0)
	Debug IconList()
Next