B4J Question Maps - Creating a Ranking List

Mmilo1971

Member
Hello everybody!

I want to read a non sorted list from an Excel file, sort it in B4J and create a sorted text/doc output.
My thoughs are:
1) I will use the JPOI library to read the Excel file using a For Loop cycle
2) Sort in B4J the results
3) Print out a doc or txt file.

I already did this in python and there it very easy, you need to just create a collection and sort it using the item you want (example: the name or the points).

Here come my questions (I am reading the booklet B4X Basic Language):
1) Is it correct to use Maps for a ranking list? Considering that the name of runners are unique (like: Omar Simpson)
2) I will use a For Loop cycle to "scroll" the Map position and collect data like:

B4X:
Private RANK As Map
RANK.Initialize
and
B4X:
RANK.Put(RACER,POINTS)

where RACER is name and surname and POINTS are the points,
at the end I will have a Map like

Luca,5
Michele,7
Marco,1
...

for this point my question is can I order the list using the POINTS item? So I can display a ranking list on the monitor and after print it in a doc file?

3) the Map index start from 0 (zero)?
In my example the "Key" Luca will be in index 0?

Thank you
Massimiliano
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
1) Is it correct to use Maps for a ranking list? Considering that the name of runners are unique (like: Omar Simpson)
No.

Use a List instead.
Create a custom type with the fields that you need to store. Each item in the list should be an instance of this custom type. Sort the list with List.SortType. It allows you to sort lists based on a given field.
 
Upvote 0

KMatle

Expert
Licensed User
Longtime User
3) the Map index start from 0 (zero)?
In my example the "Key" Luca will be in index 0?

By definition maps are always unsorted and the keys will "never" be in sequence. You can use SQLite to store the results. With it, you can sort very fast and do other things, too.
 
Upvote 0

emexes

Expert
Licensed User
where RACER is name and surname and POINTS are the points, at the end I will have a Map like

Luca,5
Michele,7
Marco,1
...
Another reason for not using a Map here, is that the key has to be unique, ie, items will overwrite earlier items that have same key.

Perhaps the "RACER is name and surname" will be unique, but it is not guaranteed.

Case in point: I found a lost Seniors Card a couple of months ago, and there were 6 people with the same name and surname listed for the surrounding suburbs. Turned out none of them were the actual person, and so I ended up mailing the card back to the government department that issued it.
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Here a Example as suggested by @Erel
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Type Rank(name As String, points As Int)
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    'MainForm.RootPane.LoadLayout("Layout1") 'Load the layout file.
    'MainForm.Show
    
    Dim members As List
    members.Initialize
    For i = 0 To 10
        Dim u As Rank
        u.Initialize
        u.name = "User"&Rnd(1,25000)
        u.points=Rnd(1000,50001)
        members.Add(u)
    Next
    Log($"============= unsorted =============="$)
    For i=0 To members.Size-1
        Dim m As Rank = members.Get(i)
        Log($"User: ${m.name} (${m.points} points)"$)
    Next
    members.SortType("points",False)
    Log($"============== sorted ==============="$)
    For i=0 To members.Size-1
        Dim m As Rank = members.Get(i)
        Log($"User: ${m.name} (${m.points} points)"$)
    Next
    
End Sub

============= unsorted ==============
User: User329 (23362 points)
User: User910 (47544 points)
User: User11261 (45797 points)
User: User9270 (11831 points)
User: User7857 (7722 points)
User: User18301 (48060 points)
User: User1827 (25043 points)
User: User4379 (37549 points)
User: User17240 (42763 points)
User: User12017 (29169 points)
User: User240 (12836 points)
============== sorted ===============
User: User18301 (48060 points)
User: User910 (47544 points)
User: User11261 (45797 points)
User: User17240 (42763 points)
User: User4379 (37549 points)
User: User12017 (29169 points)
User: User1827 (25043 points)
User: User329 (23362 points)
User: User240 (12836 points)
User: User9270 (11831 points)
User: User7857 (7722 points)
 
Upvote 0

Mmilo1971

Member
Hi Manfred,
thank you I was already doing something similar (I was making practice reading a file with State and Capital names).

But I have to say you showed me the .Size instruction that I was searching :) and the reason was that I would extract a random State from the List, and the .Size help me to have the upper limit -1 to use the Rnd instruction.

I have a question for who want to help me more or for you if online:

is there an instruction that permit to extract only the State value of a certain index in the list?

Because if I use the .Get I will receive in return the State and the Capital value.


Thank you.
 
Upvote 0

emexes

Expert
Licensed User
is there an instruction that permit to extract only the State value of a certain index in the list?

Because if I use the .Get I will receive in return the State and the Capital value.
Not sure whether you're using a List or a Map at this point, but no worries:

upload_2019-10-20_17-38-28.png


upload_2019-10-20_17-38-50.png
 
Upvote 0

emexes

Expert
Licensed User
But I have to say you showed me the .Size instruction that I was searching :) and the reason was that I would extract a random State from the List, and the .Size help me to have the upper limit -1 to use the Rnd instruction.
Btw you probably don't need to do the "- 1" bit because the upper limit ("Max") value is not returned ("exclusive"):

upload_2019-10-20_17-42-21.png
 
Upvote 0

emexes

Expert
Licensed User
Btw you probably don't need to do the "- 1" bit because the upper limit ("Max") value is not returned ("exclusive")
so code like this might get you to an acceptable state (puns intended ;-)
B4X:
Dim StateCapital As Map
Dim UserAnswer As EditText
...
Dim RandomState As Int = Rnd(0, StateCapital.Size)    'returns 0..(StateCapital.Size - 1)
Dim Question As String = "What is the capital of " & StateCapital.GetKeyAt(RandomState) & "?"
Dim Answer As String = StateCapital.GetValueAt(RandomState)
...
Dim Result As String
If UserAnswer.Text.Trim.ToUpper = Answer.Trim.ToUpper Then
    Select Rnd(1, 5)    'returns 1..4
        Case    1: Result = "Correct!!!"
        Case    2: Result = "Brilliant!!!"
        Case    3: Result = "You're right (naturally :-)"
        Case else: Result = "Yes! How did you know that?!?!"
    End Select
Else
    Result = "Close, but no cigar ;-)"
End If
 
Last edited:
Upvote 0

Mmilo1971

Member
thank you!
I will check your solution too, it looks nice coded (but if you will do it on world capitals and states you will have an issue with the Georgia States as there are two of them in the world [Georgia U.S. and Georgia ex USSR])

But the .GetValueAt works only on Maps ….. I was doing the exercise on the Lists …. is there a command to get the the first element in a List. look at my code so you can understand better what I want to do:

B4X:
Sub Process_Globals
    Type paese (Stato As String, Capitale As String)
    Dim numerocoppie As Int
    Dim paesescelto As Int
    Dim PaeseperDomanda As String
        
End Sub

Sub AppStart (Args() As String)
    'storing a list from a file
    Dim su As StringUtils
    Dim headers As List
    headers.Initialize
    Dim data As List = su.LoadCSV2(File.DirAssets, "StatieCapitali.CSV",",",headers)
    
    'add items to the list paesi that include two fields Stato and Capitale
    Dim paesi As List
    paesi.Initialize
    For Each row () As String In data
        Dim p As paese
        p.Initialize
        p.Stato = row (0)
        Log($"***********************"$)
        Log(p.Stato) 'I am using this only for my learning purpose and see if it reads each State
        p.Capitale = row(1)
        Log(p.Capitale) ' same as above comment
        paesi.Add(p)
    Next
    ' other useful commands for Lists: sort in asc/desc order the list
    ' paesi.SortType("Stato", True)
    ' paesi.SortType("Capitale",False)
    numerocoppie = paesi.Size 'collect the information on how big is the List
    Log($"***********************"$)
    Log($"Nella lista ci sono ${numerocoppie} coppie"$)
    
    '???how to extract a single value???
    paesescelto = Rnd(0,numerocoppie) 'choose a random position to be used later
    PaeseperDomanda = paesi.Get(paesescelto)
    Log($"*********************"$)
    Log(PaeseperDomanda)
    'for exemple IT PRINTS: [IsInitialized=true, Stato=Finlandia, Capitale=Helsinki]
    'HOW CAN I EXTRACT only the Stato=value ??? in this case Finlandia....
    
End Sub
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
is there an instruction that permit to extract only the State value of a certain index in the list?

Because if I use the .Get I will receive in return the State and the Capital value.
No. You are the coder. The user does not have access to the list.

It is up to you to get the RANK Type from the list using Get and only use the name or points. It is up to you to decide what to use.
 
Upvote 0

emexes

Expert
Licensed User
is there an instruction that permit to extract only the State value of a certain index in the list?

Because if I use the .Get I will receive in return the State and the Capital value.
No. ... It is up to you to get the RANK Type from the list using Get and only use the name or points.
It always warms my heart when @DonManfred makes even the tiniest mistake, shows that there is a real person behind all that German precision ;-)

From "I will receive in return the State and the Capital value" it sounds like you are using a List of custom Type eg:
B4X:
Type StateCapitalType(State As String, Capital As String, Flower As String, LandlockedFlag As Boolean)

Dim StateCapitalList As List
StateCapitalList.Initialize

Dim StateCapital As StateCapitalType
StateCapital.State = "Virginia"
StateCapital.Capital = "Richmond"
StateCapital.Flower = "American Dogwood"
StateCapital.LandlockedFlag = False
StateCapitalList.Add(StateCapital)

'repeat 49 times to add the other states
in which case the gist of the previous post is correct, except for the type and field names... eg, you'd do something like this:
B4X:
'choose a random state
Dim RandomStateIndex As Int = Rnd(0, StateCapitalList.Size)    'returns 0-based index 0..49 (assuming 50 states)

'get the state's information
Dim TempStateCapital As StateCapitalType = StateCapitalList.Get(RandomStateIndex)

'and use it
Log("The state is " & TempStateCapital.State)
Log("The capital is " & TempStateCapital.Capital)
Log("The state flower is the " & TempStateCapital.Flower)

If TempStateCapital.LandlockedFlag Then
    Log(TempStateCapital.State & " is an excellent place for thalassophobes.")
Else
    Log(TempStateCapital.State & " might be a good place for thalassophile.")
End If
 
Last edited:
Upvote 0
Top