Map Collection - The most useful collection...

Discussion in 'Teaching Programming with B4X' started by Erel, Nov 12, 2015.

  1. Erel

    Erel Administrator Staff Member Licensed 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.
    Code:
    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:
    Code:
    Dim days As Map = CreateMap(1"Sunday"2"Monday")
    Log(days.Get(1))
    Iterating over the items is done with the For Each iterator:
    Code:
    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:
    Code:
    If days.ContainsKey(8Then ...
    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:
    Code:
    Dim map1 As Map = CreateMap("A"12"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.
     
    HuZz, Phayao, Kwame Twum and 13 others like this.
  2. ilan

    ilan Expert Licensed 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

    Code:
    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: Nov 15, 2015
  3. LucaMs

    LucaMs Expert Licensed User

  4. Peter Simpson

    Peter Simpson Expert Licensed User

    This is excellent @Erel :)
     
  5. pesquera

    pesquera Active Member Licensed User

    Can I use an image as a key? How?
     
  6. Erel

    Erel Administrator Staff Member Licensed User

    Yes. You can pass the Bitmap as the key.

    Note that if you load the same image multiple times then each instance will be considered a completely different object.
     
    pesquera likes this.
  7. ac9ts

    ac9ts Active Member Licensed User

    Is there a limit on the number of keys that can be held at one time?
     
  8. Erel

    Erel Administrator Staff Member Licensed User

    No.
     
  9. LucaMs

    LucaMs Expert Licensed User

    Yes: free memory :)

    SORRY, I'm tired :D
     
  10. Erel

    Erel Administrator Staff Member Licensed User

    That's always true...
     
  11. ilan

    ilan Expert Licensed User

    Is it possible to save a map and then load it like we save/load lists?
     
  12. Erel

    Erel Administrator Staff Member Licensed User

    File.WriteMap / ReadMap will work if the map only holds strings and numbers.

    For other types you can use B4XSerializator.
     
    fredo, pesquera and ilan like this.
  13. Amin Ismail

    Amin Ismail Member Licensed 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:

    Code:
    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, x=30, y=40], 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, x=10, y=20], 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 ?
     
  14. LucaMs

    LucaMs Expert Licensed 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.

    Code:
    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)
     
    Amin Ismail and Erel like this.
  15. Amin Ismail

    Amin Ismail Member Licensed User

    Yes... that makes sense.... Thanks!
     
    LucaMs likes this.
  16. Amin Ismail

    Amin Ismail Member Licensed 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?
     
  17. Erel

    Erel Administrator Staff Member Licensed User

    Use whichever keys that make more "sense" for your requirements. You will not see any difference in the performance.
     
Loading...