B4J Question Map in Type

aaronk

Well-Known Member
Licensed User
Longtime User
Hi,

I am trying to use a 'Type' and then have a map in that type.

B4X:
Dim Customers As Map
Type Customer (Account As String, emails As Map)

Then I am trying to add an email to the emails map like:

B4X:
Customers.Initialize
    
    Dim account As String = "ABC123"
    Dim email_account As String = "test@test.com"
    
    If Customers.ContainsKey(account) Then
        Dim c As Customer = Customers.Get(email_account)
        c.emails.Put(email_account,email_account)
    Else
        Dim c As Customer
        c.emails.Put(account,account)  ' this is line 53
        Customers.Put(account,c)
    End If

When I run this I get an error:

Waiting for debugger to connect...
Program started.
Error occurred on line: 53 (Main)
java.lang.NullPointerException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.shell.Shell.runVoidMethod(Shell.java:673)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:240)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:111)
at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:100)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:98)
at b4j.example.main.main(main.java:29)

I can't work out how to use a map in a type.

Should I be able to use a map in the type, or am I doing something wrong ?
 

tchart

Well-Known Member
Licensed User
Longtime User
customer and emails needs to be initialized...

This works

B4X:
    Customers.Initialize
   
    Dim account As String = "ABC123"
    Dim email_account As String = "test@test.com"
   
    If Customers.ContainsKey(account) Then
        Dim c As Customer = Customers.Get(email_account)
        c.emails.Initialize
        c.emails.Put(email_account,email_account)
    Else
        Dim c As Customer
        c.Initialize
        c.emails.Initialize
        c.emails.Put(account,account)  ' this is line 53
        Customers.Put(account,c)
    End If
 
Last edited:
Upvote 0

tchart

Well-Known Member
Licensed User
Longtime User
This works fine

B4X:
Sub Process_Globals
    Dim Customers As Map
    Type Customer (Account As String, emails As Map)
End Sub

Sub AppStart (Args() As String)
    Customers.Initialize
    
    Dim account As String = "ABC123"
    Dim email_account As String = "test@test.com"
    
    If Customers.ContainsKey(account) Then
        Dim c As Customer = Customers.Get(email_account)
        c.emails.Initialize
        c.emails.Put(email_account,email_account)
    Else
        Dim c As Customer
        c.Initialize
        c.emails.Initialize
        c.emails.Put(account,account)  ' this is line 53
        Customers.Put(account,c)
    End If
    
    Log("Hello world!!!")
End Sub
 
Upvote 0

aaronk

Well-Known Member
Licensed User
Longtime User
If I re-Initialize the emails map again, wouldn't it delete the values in the emails map ?

I only need to add the initialize code if the value isn't in the customers map. If I ran it based on your code then it will delete the already loaded customers email since it would re-Initialize the email map again.

So, the following is most like the correct way in doing it ?

B4X:
Customers.Initialize
    
    Dim account As String = "ABC123"
    Dim email_account As String = "test@test.com"
    
    If Customers.ContainsKey(account) Then
        Dim c As Customer = Customers.Get(account)
        ' c.emails.Initialize ' < remove, since it was already Initialized
        c.emails.Put(email_account,email_account)
    Else
        Dim c As Customer
        c.Initialize
        c.emails.Initialize
        c.emails.Put(account,account)
        Customers.Put(account,c)
    End If
 
Upvote 0

tchart

Well-Known Member
Licensed User
Longtime User
If I re-Initialize the emails map again, wouldn't it delete the values in the emails map ?

I only need to add the initialize code if the value isn't in the customers map. If I ran it based on your code then it will delete the already loaded customers email since it would re-Initialize the email map again.

Yes but if there are no existing email you will need to initialise emails.

You should check if its initialized rather than assuming.

B4X:
if Not(c.IsInitialized) then c.Initialize
 
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
@tchart answer is correct but for simplicity and clarity ...

I would use the autogenerating feature of the IDE.
Hover over the word 'Customer' in the type statement.
Click on 'Generate Create Type Sub'.

Whenever you create a new customer you want to initialize a new email map, so modify that sub as below.

B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI

    Type Customer (Account As String, emails As Map)
    Private Customers As Map
End Sub

Public Sub Initialize
    Customers.Initialize
End Sub

Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
   
    Dim account As String = "ABC123"
    Dim email_account As String = "test@test.com"
    If Customers.ContainsKey(account) = False Then Customers.Put(account,CreateCustomer(account))
    Customers.Get(account).As(Customer).emails.Put(email_account, "SOME DATA")

    Dim c As Customer = Customers.Get("ABC123")
    For Each email As String In c.emails.Keys
        Log(email & TAB & c.emails.Get(email))
    Next
End Sub

Public Sub CreateCustomer (Account As String) As Customer
    Dim t1 As Customer
    t1.Initialize
    t1.Account = Account
    t1.emails.Initialize
    Return t1
End Sub
 
Upvote 0
Top