Android Question Define and use multidimensional array as matrix

Jeanc161

Member
Licensed User
Longtime User
Hi all
It's been a while since i posted something in the community. But here i'm stuck with some understanding of how to use a multidimensional array as a matix.

Let me explain.. I have a multidimensional array like that Dim nTotals = Array as int(2,13,53,8)
What the dimension represent firts element is 0 to 2 is the values of counter for a year 0 = 2024 and 1 = 2025
the second Dimension is the month 0 to 12 where 0 is january,1 is february and so on
The third Dimension is the week number of the same year from 0 to 52 weeks
the last Dimension is the day number from 0 to 7 where 0=sunday,1=monday and so on

In the code i define an array like this Dim nTotals(2,13,53,8) the number of element in each dimension is equal the number of elements plus 1 as the array system work like that as it need one more element for not having error when you access the last element since it is all 0 base declaration for array

IN javascript you can assign values for each dimension using a standard defenition per example
B4X:
nTotals[1,3,30,1] = 10
nTotals[1,3,30,2] = 20
nTotals[0,1,10,3]= 12

' In my code in javascript i use a function to assign values to elements like
[ getYearValue(currentYear), getMonthValue(CurrentMonth), getWeekValue(currentWeek),getDayValue(CurrentDay) ]
        ' currentYear, currentMonth, currentWeek, currentDay
        ' refers to the values as integer for the dimension i want to set or read from
        ' 0, 4, 31, 1   Some return values for each function in the dimensions elements

so if i want to access values for year 0, month 7, week 31, day 1 i simply tell wich dimension element number i want to access and it give me a value for let say month 7 got 12
week number 32 got value of 20, day number 3 got value of 10, and i do that for each element in each dimension, and that what i call a matrix
So far it only return the value for the last dimension wich is the day value not the month. so there is something i don't understand about array in B4X.
Do you think it is possible to do that using array only. ??

The functions return an integer value for each elements and the currentxxx is the current index of the array
So if i define an array in B4X, it seems like i can't assign value in the dimension and the values returned when i iterate thrue the dimensions and elemnts, always return the values that are stored in getDayValue(currentDay) it dos not store the values for each dimension separately.

I use this in javascript to create a matrix of value for each year, month, week, and day as for example if i want to get the values for year 0, month 7, week 30, and day 1 it return the value of day not the month, week

The idea behind this is to store values for each month, week and day separately withing a specific year

So if someone have an idea on how to create an array or maybe a map or a type that can store and access the different values store in each dimension for specific elements, i welcome some example or explanation on how to do this in B4X for android.

Thanks guys..
 

Daestrum

Expert
Licensed User
Longtime User
B4X:
    Dim MyArray(2,12,52,7) As Int
    MyArray(1,3,30,1) = 10
    Dim yr,month,week,day As Int
   
    yr = 1
    month=3
    week = 30
    day = 1
    Log(MyArray(yr,month,week,day))

Personally I would just use a database with fields yr,month,week and day and query that.
 
Last edited:
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
You are over-specifying date indexes (use either weeks or months) - use the DateTime object to simplify things and make you code more transparent

B4X:
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    
    DateTime.DateFormat = "yyyy-MM-dd"

    'Private baseDate as string = "23-01-01" in Globals
    Dim n As Int = 2 * 366  'for 2 years of data from: (baseDate) to (baseDate + n - 1)
    
    Dim nTotals(n) As Float   'to use NaN you need to float - could use minimum value -2147483648 for int
    For i = 0 To n - 1
        nTotals(i) = 0 / 0        'preset to missing - defined as NaN
    Next
    
    'to store a date specific values = >
    nTotals(DateToIndex("2024-10-02")) = 78                'my birthday this year
    
    'to get a date specific value
    Log(nTotals(DateToIndex("2024-10-02")))        '78
    
    'to test if there is missing value
    Log(nTotals(DateToIndex("2023-02-29")))        'NaN
    
    'to test for NaN
    Log(isMissing(nTotals(DateToIndex("2023-02-29"))))    'true
    
End Sub

Private Sub DateToIndex(date As String) As Int
    Try
        DateTime.DateParse(date)  'for february 29 in non-leap years - or invalid dates
    Catch
        Return 0 / 0
    End Try
    Return (DateTime.DateParse(date) - DateTime.DateParse(baseDate)) / DateTime.TicksPerDay
End Sub

Private Sub isMissing(number As Float) As Boolean
    Return Not(number = number)        'NaN is the only number that is not equal to itself!   
End Sub
 
Last edited:
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
Just for fun, I tested the timing using a Map object that only grows in size when needed - no need for a preset array.
Also no need to use date conversions (except to define the range of random dates for testing.

You'll be surprised at the performance!

B4X:
Private Sub alternative
    Dim nTotals As Map
    nTotals.Initialize

    'to store a date specific values = >
    nTotals.Put("2024-10-02", 78)                'my birthday this year
    
    'to get a date specific value
    Log(nTotals.Get("2024-10-02"))            '78
    
    'to test if there is missing value
    Log(nTotals.ContainsKey("2023-02-29"))        'False
    
    'test timing
    Dim markTime As Long = DateTime.now
    Dim strDate As Int = DateTime.DateParse("2020-01-01") / 10000
    Dim endDate As Int = DateTime.DateParse("2025-01-01") / 10000
    For i = 1 To 10000
        Dim rndDate As String = DateTime.Date(10000 * Rnd(strDate, endDate))
        nTotals.Put(rndDate, i)
    Next
    Log(DateTime.now - markTime)        '21 msec for loading 10000 dates - including computing random dates


    Dim markTime As Long = DateTime.now
    Dim strDate As Int = DateTime.DateParse("2020-01-01") / 10000
    Dim endDate As Int = DateTime.DateParse("2025-01-01") / 10000
    For i = 1 To 10000
        Dim rndDate As String = DateTime.Date(10000 * Rnd(strDate, endDate))
        Dim value As Int = nTotals.Get(rndDate)
    Next
    Log(DateTime.now - markTime)        '10 msec for getting 10000 values - including computing random dates
    
End Sub
 
Upvote 0

Jeanc161

Member
Licensed User
Longtime User
Just for fun, I tested the timing using a Map object that only grows in size when needed - no need for a preset array.
Also no need to use date conversions (except to define the range of random dates for testing.

You'll be surprised at the performance!

B4X:
Private Sub alternative
    Dim nTotals As Map
    nTotals.Initialize

    'to store a date specific values = >
    nTotals.Put("2024-10-02", 78)                'my birthday this year
   
    'to get a date specific value
    Log(nTotals.Get("2024-10-02"))            '78
   
    'to test if there is missing value
    Log(nTotals.ContainsKey("2023-02-29"))        'False
   
    'test timing
    Dim markTime As Long = DateTime.now
    Dim strDate As Int = DateTime.DateParse("2020-01-01") / 10000
    Dim endDate As Int = DateTime.DateParse("2025-01-01") / 10000
    For i = 1 To 10000
        Dim rndDate As String = DateTime.Date(10000 * Rnd(strDate, endDate))
        nTotals.Put(rndDate, i)
    Next
    Log(DateTime.now - markTime)        '21 msec for loading 10000 dates - including computing random dates


    Dim markTime As Long = DateTime.now
    Dim strDate As Int = DateTime.DateParse("2020-01-01") / 10000
    Dim endDate As Int = DateTime.DateParse("2025-01-01") / 10000
    For i = 1 To 10000
        Dim rndDate As String = DateTime.Date(10000 * Rnd(strDate, endDate))
        Dim value As Int = nTotals.Get(rndDate)
    Next
    Log(DateTime.now - markTime)        '10 msec for getting 10000 values - including computing random dates
   
End Sub
Seem good if i want to store values for a specific date, but this is not what i'm looking for but...
You give me an idea if i use a map with a custom type like this

B4X:
Type mData(datelast as long, year as int, month as int, week as int, day as int)
' in global i declare a Map like Dim nTotals as map
' then in app resume or start i initialize the map like
          nTotals.initialize

' To save the values to the map i need an index like
Dim index as string
        index="y" & curYear & "m" & curMonth & "w" & curweek & "d" & curday
        ' wich should equal something like this
        Dim mObj as mData
                mObj.initialize
                mObj.datelast = datetime.now
                mObj.year = curyear               ' values can be 2024
                mObj.month = curMonth       ' values can be 7 for july
                mObj.week = cutWeek           ' values can be 31 for week 31
                mObj.day = curDay                ' values can be 3 for day thursday
         nTotals.put(Index,mObj)                ' save the map object

' Note that the values for the curValues are incrementation by one for each of the year,month,week,day
' so i can totalize the values per month, year, week, day etc

' To read the values i gos the other way
Dim index as string
       index="y" & curYear & "m" & curMonth & "w" & curWeek & "d" & curDay
Dim mObj as mData
       mObj.initialize
mObj = nTotals.get(index)
' then assign the values to local vars like
nYear = mObj.year
nMonth = mobj.month
nWeek = mobj.week
nDay = mObj.day

' the values for that specific record are stored in local variables that i use for other stuff

So i don't know yet if this will work correctly but i look in the B4A questions a long time ago and i thought i find something like this that user type can be used to store and get values from a custom type inside a map, maybe the syntax is not 100% ok but i seem to something like this

BTW I use the RandomAccessFile to store the map as an object and use the B4XWrite and b4XRead for this

If you have seen something like this tell me where so i can get the wright syntax because if this works i resolve many problems in my Code

Thank for your response Realy appreciate.
 
Upvote 0

William Lancee

Well-Known Member
Licensed User
Longtime User
You're still over-specifying dates. Months and day of month. OR Week of year and day of week, BUT not both.
It will lead to conflicts.
 
Upvote 0

Jeanc161

Member
Licensed User
Longtime User
You're still over-specifying dates. Months and day of month. OR Week of year and day of week, BUT not both.
It will lead to conflicts.
The thing is i'm not interrested in the date itself but by the month number, week number and day number so i can increment values for each month week and day but not the date itself i use the date of current time to add a value + 1 on each month,week and day.
The idea behing this is to get how many occurences appears for the day that are cumulate on the week and month so i can find how many occurence appears during the month and week and day. the date itself have no particular meaning, i use the current date to extract the current day of the week, the currennt week of the year and the current month.
So i think that if i would want to know how many occurences for the date i would use something like you define.

So thanks for your answer but it is not for me right now.
 
Upvote 0

Jeanc161

Member
Licensed User
Longtime User
** UPDATE **
Guys i think i found the perfect solutions to get my accumulators correctly
I use some of the ideas from post #4 and use only Map to store the values for each year,month,week and day by setting an index specific for each part of the accumulators i want to have

Here is the idea in case someone want to create some data and use it as a matix using map

B4X:
Dim nTotals as map
       nTotals.initialize

' To add an entry and cumulate each part i define an index like this
' and use a function to get or put values in the collection (Map) and read from it
Dim indexe as string = ""
Dim total as int = 0
            nYear = 2024 
            totals = putOrGet("y" & nYear,0,false)              ' get the value for year
            totals = totals + 1
            totals = putOrGet("y" & nYear,totals,true)         ' put the value based on indexe for year
            nMonth = dateTime.getMonthOfYear(DateTime.now)
            totals = putOrGet("y" & nYear & "m" & nMonth,0,false)
            totals = totals + 1
            totals = putOrGet("y" & nYear & "m" & nMonth,totals,True)
            nWeek = dateUtils.getWeekOfYear(dateTime.now)
            totals = putOrGet("y" & cYear & "m" & nMonth & "w" & nWeek,0,False)
            totals = totals + 1
            totals = putOrGet("y" & nYear & "m" & nMonth & "w" & nWeek,totals,True)
            nDay = dateTime.GetDayOfWeek(dateTime.now)
            totals = putOrGet("y" & nYear & "m" & nMonth & "w" & nWeek & "d" & nDay,0,False)
            totals = totals + 1
            totals = getOrPut("y" & nYear & "m" & nMonth & "w" & nWeek & "d" & nDay,totals,False
' This is for putting values into dimensions of the map and it is base on the index value
' since the index is a string value i put some letters in it to keep track of the elements in the map

' For reading the values i use the same part as for reading i set the index accordingly
' if i want to get for year, month, week or day

' Like
Dim elementValue as int = getOrPut("MY indexe",0,False)     ' do a read only
' to set it
Dim elementValue as int = 100
       elementValue = putOrGet("MY indexe",elementValue,True)  
' do a write with the same indexe as you get
' The index part define the dimensions
' you can use a different type of index like
' indexe = "ad1" & n & "ad2" & y & "ad3" & x & "ad4" & z etc...
' n y x and z represent variables values for the dimension that you want to access
' have fun with this like i have to make it work

Dim Sub putOrGet(indexe as string, value as int, mode as boolean) as int
  Dim getValue as int
  if mode = true then
     ' put the values into the map
    nTotals.put(Indexe,value)
    return value
else
   if nTotals.ContainsKey(Indexe) Then
      getValue = nTotals.get(Indexe)
      return getValue
   else
      return 0
   end if
end if
End Sub

So it takes me a couple days to rewrite my code and get rig of all the arrays that i use
in the application and replace it with the map code and it works perfectly.

The map in android or B4X is about the same as collections in VB6

So if you want to use a myltidimensional array to store values you can try using a variable index to access multi dimension
like in my example
-- indexe "y" & nYear access the first dimension
-- indexe "m" & nMonth access the second dimension
-- indexe "w" & nWeek access the third dimension
-- indexe "d" & nDay access the fourth dimension
And the thing it is very fast and save a lot of memory since the map will grow as you need it
also i use the RandomAccessFile to save the object using Random.writeB4xObject to store the map and not the
save function to save the map as to when reading the map this way you need a method to parse the string elements
that it return from the reading back. The save as object is much faster and much easier to use since it put the map as it was when you save it
so you don't need to do a parsing sub to read it back.

So thanks you guys to have put me on the right direction and with a little bit of experimentations i was able to get multi type of solutions to do what i wanted and to do this without the use of arrays.

NOTE: You can also use a user type and save it to the map but it need a little bit more of coding to do so.

In the nTotals if you debug it and display in the debug window you will see the elements like this
nTotals
size = 5
y2024 = 30
y2024m8 = 20
y2024m8w32 = 10
y2024m8w32d5 = 5
y2024m8w32d6 = 4
y2024m8w32d7 =10
 
Upvote 0
Top