Map Collection - The most useful collection...

Status
Not open for further replies.

Erel

B4X founder
Staff member
Licensed User
Longtime User
Map is a collection that maps keys to values.
It can also be named hashtable, dictionary or associative array.

Each item is made of a key and value. Access to the value is always done through the key.
Retrieving the key is a fast operation. The size of the map doesn't affect the performance (unlike searching for an item in a large list which requires iterating over all items).

The two main methods are Put and Get. Put adds a key / value pair to the map and Get returns the value based on the key.
The keys are unique. If you put an item with the same key as an existing item then the previous item will be replaced.
B4X:
Dim days As Map
days.Initialize
days.Put(1, "Sunday")
days.Put(2, "Monday")
Log(days.Get(1))
You can also use CreateMap as a shorthand:
B4X:
Dim days As Map = CreateMap(1: "Sunday", 2: "Monday")
Log(days.Get(1))

Iterating over the items is done with the For Each iterator:
B4X:
Dim days As Map = CreateMap(1: "Sunday", 2: "Monday", 3: "Tuesday", _
   4: "Wednesday", 5:"Thursday", 6: "Friday", 7: "Saturday")
   
'iterate over the values
For Each day As String In days.Values
   Log(day)     
Next
'iterate over both keys and values
For Each dayNumber As Int In days.Keys
   Dim day As String = days.Get(dayNumber)
   Log($"Day number: ${dayNumber}, value: ${day}"$)
Next
Note that in most implementations of maps the order is not preserved. In most use cases the order is not important.
The order in B4A and B4J maps is preserved. This is not the case in B4i.
B4A and B4J Maps include two methods named GetValueAt and GetKeyAt. These methods are only available because of historic reasons (before the For Each block was available). You shouldn't use these methods.

The ContainsKey method is used to quickly test whether there is a key / value item with the given key.

For example:
B4X:
If days.ContainsKey(8) Then ...
Note that if you try to get a non-existent item then Null will be returned.

The key type and case (for strings) are important.
For example:
B4X:
Dim map1 As Map = CreateMap("A": 1, 2: "B")
Log(map1.Get("a")) 'null
Dim d As Double = 2
Log(map1.Get(d)) 'null
Dim i As Int = 2
Log(map1.Get(i)) 'B

Note that the keys are not restricted to strings and numbers. Any object can be used as a key.
This is very useful for mapping an arbitrary object to another object (similar to the Tag property).

There are two ways to save and load maps.

File.WriteMap / ReadMap - Writes or reads the map from text file. This will only work properly with strings and numeric values.
RandomAccessFile.WriteB4XObject / ReadB4XObject - Can write or read maps with custom items.
 

ilan

Expert
Licensed User
Longtime User
really great, i learned something new today, thank you erel :)

i did something similar to this in a different way but only with int's & string's
if i knew that map exists at that time i would have used map (it was one of my first apps in 2012)

this is how i did it

B4X:
Sub txt_TextChanged (old As String, New As String)

Dim Result, ch, i As Int
Dim old As String
Dim c As Char

If txt.Text.Length > 0 Then Label1.Text = "" Else Label1.Text = "-"
If txt.Text.Length > 0 Then Label5.Text = "" Else Label5.Text = "-"

    Result = 0
    For i = 0 To txt.Text.Length - 1
    c =  txt.Text.CharAt(i)

    Select Case c
        Case "ื" : ch = 1 : Label5.Text = Label5.Text & "ื‘" : Label1.Text = Label1.Text & "ืช"
        Case "ื‘" : ch = 2 : Label5.Text = Label5.Text & "ื’" : Label1.Text = Label1.Text & "ืฉ"
        Case "ื’" : ch = 3 : Label5.Text = Label5.Text & "ื“" : Label1.Text = Label1.Text & "ืจ"
        Case "ื“" : ch = 4 : Label5.Text = Label5.Text & "ื”" : Label1.Text = Label1.Text & "ืง"
        Case "ื”" : ch = 5 : Label5.Text = Label5.Text & "ื•" : Label1.Text = Label1.Text & "ืฆ"
        Case "ื•" : ch = 6 : Label5.Text = Label5.Text & "ื–" : Label1.Text = Label1.Text & "ืค"
        Case "ื–" : ch = 7 : Label5.Text = Label5.Text & "ื—" : Label1.Text = Label1.Text & "ืข"
        Case "ื—" : ch = 8 : Label5.Text = Label5.Text & "ื˜" : Label1.Text = Label1.Text & "ืก"
        Case "ื˜" : ch = 9 : Label5.Text = Label5.Text & "ื™" : Label1.Text = Label1.Text & "ื "
        Case "ื™" : ch = 10 : Label5.Text = Label5.Text & "ื›" : Label1.Text = Label1.Text & "ืž"
        Case "ื›", "ืš" : ch = 20 : Label5.Text = Label5.Text & "ืœ" : Label1.Text = Label1.Text & "ืœ"
        Case "ืœ" : ch = 30 : Label5.Text = Label5.Text & "ืž" : Label1.Text = Label1.Text & "ื›"
        Case "ืž", "ื" : ch = 40 : Label5.Text = Label5.Text & "ื " : Label1.Text = Label1.Text & "ื™"
        Case "ื ", "ืŸ" : ch = 50 : Label5.Text = Label5.Text & "ืก" : Label1.Text = Label1.Text & "ื˜"
        Case "ืก" : ch = 60 : Label5.Text = Label5.Text & "ืข" : Label1.Text = Label1.Text & "ื—"
        Case "ืข" : ch = 70 : Label5.Text = Label5.Text & "ืค" : Label1.Text = Label1.Text & "ื–"
        Case "ืค", "ืฃ" : ch = 80 : Label5.Text = Label5.Text & "ืฆ" : Label1.Text = Label1.Text & "ื•"
        Case "ืฆ", "ืฅ" : ch = 90 : Label5.Text = Label5.Text & "ืง" : Label1.Text = Label1.Text & "ื”"
        Case "ืง" : ch = 100 : Label5.Text = Label5.Text & "ืจ" : Label1.Text = Label1.Text & "ื“"
        Case "ืจ" : ch = 200 : Label5.Text = Label5.Text & "ืฉ" : Label1.Text = Label1.Text & "ื’"
        Case "ืฉ" : ch = 300 : Label5.Text = Label5.Text & "ืช" : Label1.Text = Label1.Text & "ื‘"
        Case "ืช" : ch = 400 : Label5.Text = Label5.Text & "ื" : Label1.Text = Label1.Text & "ื"
        Case " " : ch = 0 : Label5.Text = Label5.Text & " " : Label1.Text = Label1.Text & " "

        Case Else
           ch = 0
           txt.Text = ""
           ToastMessageShow ("ื ื ืœื”ืงืœื™ื“ ืื•ืชื™ื•ืช ืจืง ื‘ืขื‘ืจื™ืช",False)
        End Select
          Result =  Result + ch
       Next
     
        gimcount.Text = Result
        old = txt.Text
     
        If txt.Text.Length > 0 Then Button1.SetBackgroundImage(LoadBitmap(File.DirAssets ,"full.png")) _
        Else Button1.SetBackgroundImage(LoadBitmap(File.DirAssets ,"empty.png"))

End Sub

this is a gematria calculator, it takes the hebrew letter and turn it to a number
 
Last edited:

ac9ts

Active Member
Licensed User
Longtime User
Is there a limit on the number of keys that can be held at one time?
 

aminoacid

Active Member
Licensed User
Longtime User
The Map Collection is very interesting. I went through this Tutorial and also Erel's video Tutorial and have been experimenting with some code snippets in order to understand it thoroughly. Everything seems pretty clear except for one thing ...... see code snippet below:

I have the following "test" code to simply create a map with two items:

B4X:
Type point (x As Int, y As Int)

   
    Dim p As point
   
    Dim m1 As Map
    m1.Initialize
   
    p.x=10
    p.y=20
    m1.Put("1",p)
    Log(m1)
   
    p.x=30
    p.y=40
    m1.Put("2",p)
    Log(m1)

Running the above code I do not get what was expected:

(MyMap) {1=[IsInitialized=false, x=10, y=20]}
(MyMap) {1=[IsInitialized=false, [COLOR=#ff0000]x=30, y=40[/COLOR]], 2=[IsInitialized=false, x=30, y=40]}

Notice that x and y in the first item in the map has changed to the same values as the second item ... which is not what I expected.

This is what I expected:

(MyMap) {1=[IsInitialized=false, x=10, y=20]}
(MyMap) {1=[IsInitialized=false, [COLOR=#ff0000]x=10, y=20[/COLOR]], 2=[IsInitialized=false, x=30, y=40]}

I understand that "p" is linked to the map "m1". But how would one add items to the map in a similar manner using variables instead of constants?

Also, why does the log say "IsInitialized=false" when m1 has been initialized ?
 

LucaMs

Expert
Licensed User
Longtime User
a) you have to initialize p (it is an object of your type point).

b) to have two distinct objects in the map, you have to use two distinct variables of type point OR you have to dim and inizialize p twice.

B4X:
Type point (x As Int, y As Int)

    Dim m1 As Map
    m1.Initialize
 
    Dim p As point
    p.Initialize
    p.x=10
    p.y=20
    m1.Put("1",p)
    Log(m1)
 
    Dim p As point
    p.Initialize
    p.x=30
    p.y=40
    m1.Put("2",p)
    Log(m1)
 

aminoacid

Active Member
Licensed User
Longtime User
One more question:
Is it more efficient to use numeric keys (int for example) rather than strings for the keys? It would seem like numeric keys would be more efficient for maps with a large number of items. But I guess this depends on how the map is internally processed?
 
Status
Not open for further replies.
Top