MapElementExists(Map(), Key$)

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
benubi
Enthusiast
Enthusiast
Posts: 215
Joined: Tue Mar 29, 2005 4:01 pm

MapElementExists(Map(), Key$)

Post by benubi »

Hello

please don't get a burn-out from reading my growing wishlist :lol:

Code: Select all

MapElementExists(Map(), Key$)
This procedure returns the *Element pointer or #Null, but does not change the current element.

Useful especially when using a shared map for reading over multiple threads :mrgreen:
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: MapElementExists(Map(), Key$)

Post by jacdelad »

Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
benubi
Enthusiast
Enthusiast
Posts: 215
Joined: Tue Mar 29, 2005 4:01 pm

Re: MapElementExists(Map(), Key$)

Post by benubi »

Thanks for the tip but nope. :wink:

This function DOES change the current element. What I want is one that checks (thread safely) for the element's existence without changing the current element (which causes bad read/writes in multi-threaded, problems during ForEach etc.).
benubi
Enthusiast
Enthusiast
Posts: 215
Joined: Tue Mar 29, 2005 4:01 pm

Re: MapElementExists(Map(), Key$)

Post by benubi »

I know you can also use the Threaded keyword, but it won't work in an structure-embedded Map(), like I did in my failed web server experiment (ca. 900kB of source-codes).
User avatar
jacdelad
Addict
Addict
Posts: 1993
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: MapElementExists(Map(), Key$)

Post by jacdelad »

Oh, I never thought of that especially. I thought you missed the Find-function. Sorry.
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
benubi
Enthusiast
Enthusiast
Posts: 215
Joined: Tue Mar 29, 2005 4:01 pm

Re: MapElementExists(Map(), Key$)

Post by benubi »

No worries we are all fine gentlemen :^)
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: MapElementExists(Map(), Key$)

Post by Demivec »

benubi wrote: Fri Aug 25, 2023 1:21 pm Thanks for the tip but nope. :wink:

This function DOES change the current element. What I want is one that checks (thread safely) for the element's existence without changing the current element (which causes bad read/writes in multi-threaded, problems during ForEach etc.).
As a proof of concept, here is a macro that can do the job and an unproductive demo to test it:

Code: Select all

Macro mcr_MapElementExists(_Map_, _KeyStr_, _ResultInt_)
  PushMapPosition(_Map_)
  _ResultInt_ = FindMapElement(_Map_, _KeyStr_)
  PopMapPosition(_Map_)
EndMacro

NewMap myMap.s()

myMap("one") = "first"
myMap("two") = "second"
myMap("three") = "third"
myMap("four") = "fourth"
Define order$ = "zero,one,two,three,four,five,six", k$, exists

Debug "Note: map elements are not in a sorted order"
Debug "---------------------"
ForEach myMap()
  Debug MapKey(mymap()) + " = " + mymap()
  k$ = StringField(order$, Random(CountString(order$, ",")) + 1, ",")
  mcr_MapElementExists(myMap(), k$, exists)
  If exists
    Debug "Element " + k$ + " exists."
  Else
    Debug "Element " + k$ + " does not exist."
  EndIf
Next
Sample output:

Code: Select all

Note: map elements are not in a sorted order
---------------------
two = second
Element five does not exist.
four = fourth
Element six does not exist.
one = first
Element four exists.
three = third
Element five does not exist.
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 670
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Re: MapElementExists(Map(), Key$)

Post by Kurzer »

Or as a Procedure...

Code: Select all

NewMap Country.s()

Procedure.i MapElementExists(Map myMap.s(), sKey.s)
	Protected.i iResult
	PushMapPosition(myMap())
	iResult = Bool(FindMapElement(myMap(), sKey)<>0)
	PopMapPosition(myMap())
	ProcedureReturn iResult
EndProcedure

Country("US") = "United States"
Country("FR") = "France"
Country("GE") = "Germany"

ResetMap(Country())
FindMapElement(Country(), "FR")
Debug Country()

Debug "GE? " + Str(MapElementExists(Country(), "GE"))
Debug "US? " + Str(MapElementExists(Country(), "US"))
Debug "XY? " + Str(MapElementExists(Country(), "XY"))

Debug Country()
[15:24:48] Warte auf den Start des Executable...
[15:24:48] Executable-Typ: Windows - x64 (64bit, Unicode)
[15:24:48] Executable gestartet.
[15:24:48] [Debug] France
[15:24:48] [Debug] GE? 1
[15:24:48] [Debug] US? 1
[15:24:48] [Debug] XY? 0
[15:24:48] [Debug] France
[15:24:48] Die Programmausführung ist abgeschlossen.
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: MapElementExists(Map(), Key$)

Post by skywalk »

Can you explain what problem this solves?
Still not completely reliable without mutex or semaphores.
If 2,3,10 or more threads run findmapelement() on same map?
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Kurzer
Enthusiast
Enthusiast
Posts: 670
Joined: Sun Jun 11, 2006 12:07 am
Location: Near Hamburg

Re: MapElementExists(Map(), Key$)

Post by Kurzer »

This procedure (and also Demivec's macro) return the *element pointer or 0, but does not change the current element of the map() as FindMapelement() does. It is a kind of workaround for the feature request of the thread creator.
And yes, in a multithreaded environment you still have to secure this using mutex or semaphores.
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520, User age in 2024: 56y
"Happiness is a pet." | "Never run a changing system!"
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: MapElementExists(Map(), Key$)

Post by Demivec »

Kurzer wrote: Fri Aug 25, 2023 2:26 pm Or as a Procedure...
There is a drawback to a procedure and that it is usable with only one type of map because of the parameter declaration. It does have the plus of not needing additional variables in the calling scope. A variation using a macro to define a procedure with the specific map element type as a kind of template would solve both those situations. I'll leave that as an exercise to the reader. :wink: It wouldn't be needed as often as the original solution, which already may be a uncommon occurrence.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: MapElementExists(Map(), Key$)

Post by netmaestro »

Demivec's macro looks good to me. Am I missing something here? The macro makes no changes, just taking a peek and saying yes or no if an element is there or not. So why the need for thread safety and mutexes?
BERESHEIT
User avatar
spikey
Enthusiast
Enthusiast
Posts: 750
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: MapElementExists(Map(), Key$)

Post by spikey »

netmaestro wrote: Sat Aug 26, 2023 5:40 pm Demivec's macro looks good to me. Am I missing something here? The macro makes no changes, just taking a peek and saying yes or no if an element is there or not. So why the need for thread safety and mutexes?
Suppose two threads are running concurrently but slightly out of step, the map is currently at item 'X' before the threads act.

Code: Select all

Thread A                    Thread B
--------                    --------
PushMapPosition (at 'X')
FindMapElement (for 'Y')    PushMapPosition(at 'X', 'Y' or invalid if 'Y' is not an element?)
PopMapPosition (at 'X')     FindMapElement('Z')
                            PopMapPosition(Possibly X or Y?)
The code is fine in a single thread but in concurrent threads, because FindMapElement changes the current map pointer and the operation overall isn't performed atomically, the result in thread B and the final position are indeterminate, depending upon the difference in step between the two threads. The more threads that were running concurrently the worse this problem would get.

Threadsafe code is definitely required and a mutex could well be necessary to ensure atomicity.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: MapElementExists(Map(), Key$)

Post by netmaestro »

Right, it changes the map pointer and pops it back, making threadsafety necessary.
BERESHEIT
Little John
Addict
Addict
Posts: 4779
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: MapElementExists(Map(), Key$)

Post by Little John »

+1 for benubi's request.
Post Reply