Android Question Order String

imgsimonebiliato

Well-Known Member
Licensed User
Longtime User
Hello,
please I need some help ordering a string.

This string: C1, P5, S3, C5, S9, C3, S1, P1
To this: P1, P3, S3, S9, C1, C3, C5

So how to order? The strings with letter "P" must be the first, then "S" and "C" finally
 

Roycefer

Well-Known Member
Licensed User
Longtime User
You'll need to implement an IsLessThan method that takes two parameters (two of your Strings) and returns a Boolean. Then, once you have that, you can use any generic sorting algorithm like a bubble sort.

You'll probably want something along these lines:
B4X:
'Returns True if if a<b
Sub IsLessThan(a As String, b As String) as Boolean
     Dim aPrefix as String = a.GetCharAt(0)
     Dim aNum as Int = a.GetCharAt(1)
     Dim bPrefix as String = b.GetCharAt(0)
     Dim bNum as Int = b.GetCharAt(1)
     If aPrefix=="P" Then
          If bPrefix<>"P" Then 
             Return True
          Else
             Return aNum<bNum
     Else
          If aPrefix==bPrefix Then Return aNum<bNum
          Return aPrefix=="S"
     End If
End Sub

This code isn't tested so make sure you test it to see that it does what you want.

Now you can implement any generic sorting algorithm. A bubble sort is simple (though not the fastest): https://en.wikipedia.org/wiki/Bubble_sort
 
Upvote 0

James Chamblin

Active Member
Licensed User
Longtime User
Here is another method. Just replace "P" with "A", and "S" with "B". Then split the string into a list to be sorted, then recombine into a single string, Replace the original letters.
B4X:
    Dim s As String = "C1, P5, S3, C5, S9, C3, S1, P1"
    Dim a As List
    a.Initialize
    s = s.Replace("P","A")
    s = s.Replace("S","B")
    a.AddAll(Regex.split(", ",s))
    a.Sort(True)
    s = ""
    For Each c As String In a
        s = s & c & ", "
    Next
    s = s.SubString2(0,s.Length - 2)
    s = s.Replace("A", "P")
    s = s.Replace("B", "S")
    Log(s)
 
Upvote 0

imgsimonebiliato

Well-Known Member
Licensed User
Longtime User
Here is another method. Just replace "P" with "A", and "S" with "B". Then split the string into a list to be sorted, then recombine into a single string, Replace the original letters.
B4X:
    Dim s As String = "C1, P5, S3, C5, S9, C3, S1, P1"
    Dim a As List
    a.Initialize
    s = s.Replace("P","A")
    s = s.Replace("S","B")
    a.AddAll(Regex.split(", ",s))
    a.Sort(True)
    s = ""
    For Each c As String In a
        s = s & c & ", "
    Next
    s = s.SubString2(0,s.Length - 2)
    s = s.Replace("A", "P")
    s = s.Replace("B", "S")
    Log(s)

Thank you, this is useful.

But If I get : "C1, P50, S3, C5, S10, C3, S1, P1"
The result is "P1, P50, S1, S10, S3, C1, C3, C5"

If you see S10 if before S3 and must be contrary.
How do i solve?
 
Upvote 0

Roycefer

Well-Known Member
Licensed User
Longtime User
Another way is to create three separate Lists, a PList, an SList and a CList, filled accordingly. Strip the letters from each element in the List so you just have numbers, remaining (make sure they are actually of an Int type, not just a String representation of an integer). Then sort each list. Then you can put the sequence back together, with the PList first, then the SList, then the CList.
 
Upvote 0

imgsimonebiliato

Well-Known Member
Licensed User
Longtime User
Another way is to create three separate Lists, a PList, an SList and a CList, filled accordingly. Strip the letters from each element in the List so you just have numbers, remaining (make sure they are actually of an Int type, not just a String representation of an integer). Then sort each list. Then you can put the sequence back together, with the PList first, then the SList, then the CList.

I tried to do that but without success.. If you can help me.
Also I prefer the method of @James Chamblin , but there is the other error

:(:(:(:(
 
Upvote 0

imgsimonebiliato

Well-Known Member
Licensed User
Longtime User
Yes! I solved!!

Simply replace the number 10 with Z, and then the sort work!
I know that it is a silly solution, but for my little program, it works fine..

Thank all!
 
Upvote 0

udg

Expert
Licensed User
Longtime User
If you know beforehand that the number following the leading letter is made up of "x" digits at maximum, just "normalize" the numeric part of each element pre-pending enough 0s.
So P1 becomes P01 when you know that a letter is followed by at most 2 digits, or that same P1 becomes P001 if the digits are three.
You can generalize the concept with a discovering function (to discover the number of digits) and an eventual clean-up function to stripe the extra zeros when done.

udg
 
Upvote 0

James Chamblin

Active Member
Licensed User
Longtime User
numbers in strings are compared differently than in ints. My example worked because the numbers are all single digits. If you don't mind having leading 0's on all your numbers, then you could use a string like "C01, P50, S03, C05, S10, C03, S01, P01" and it will work. If you can't do that, then you will need to treat numbers and letters separately, similar to what Roycefer has done.

Modified Roycefer's IsLessThan() Sub and added a bubble sort here.
B4X:
Sub ProcessString
    Dim s As String = "C1, P50, S3, C5, S10, C3, S1, P1"
    Dim a As List
    a.Initialize

    a.AddAll(Regex.split(", ",s))
    SortComponents(a)
    s = ""
    For Each c As String In a
        s = s & c & ", "
    Next
    s = s.SubString2(0,s.Length - 2)

    Log(s)   
End Sub

Sub SortComponents(l As List)
    Dim index As Int = 0
    Dim Top As Int
    For Top = l.Size - 2 To 0 Step - 1
        For index = 0 To Top
            If IsLessThan(l.Get(index+1),l.Get(index)) Then
                Dim temp As String = l.Get(index)
                l.Set(index,l.Get(index+1))
                l.Set(index+1,temp)
               
               
            End If
        Next
    Next
End Sub

Sub IsLessThan(a As String, b As String) As Boolean
     Dim aPrefix As String = a.charat(0)
     Dim aNum As Int = a.SubString(1)
     Dim bPrefix As String = b.CharAt(0)
     Dim bNum As Int = b.SubString(1)
    If aPrefix = bPrefix Then Return aNum<bNum
    Select aPrefix
        Case "P"
            Return True
        Case "S"
            Select bPrefix
                Case "P"
                    Return False
                Case Else
                    Return True
            End Select
        Case Else
            Return False
                   
    End Select
End Sub
 
Upvote 0

wonder

Expert
Licensed User
Longtime User
Hello,
please I need some help ordering a string.

This string: C1, P5, S3, C5, S9, C3, S1, P1
To this: P1, P3, S3, S9, C1, C3, C5

So how to order? The strings with letter "P" must be the first, then "S" and "C" finally
Hello hello!

Here's my approach [fixed]:

1. Add this function to your code.
B4X:
Sub SetCustomOrder(originalString As String, customOrder As String, ascending As Boolean) As String 
    Dim tempString, finalString   As String
    Dim tempList(2)               As List
    For co = 0 To (customOrder.Length - 1)
        tempList(0).Initialize
        tempList(1).Initialize
        For os = 0 To (originalString.Length - 1)
            If originalString.CharAt(os) = customOrder.CharAt(co) Then
                tempList(0).Add(originalString.CharAt(os))
                For n = Min(os + 1, (originalString.Length - 1)) To (originalString.Length - 1)
                    If Asc(originalString.CharAt(n)) >= Asc("0") And Asc(originalString.CharAt(n)) <= Asc("9") Then
                        tempString = tempString & originalString.CharAt(n)
                    Else
                        Exit
                    End If
                Next
                Dim num = tempString As Long
                tempList(1).Add(num)
                tempString = ""
            End If
        Next
        tempList(1).Sort(ascending)
        For l = 0 To (tempList(0).Size - 1)
            finalString = finalString & tempList(0).Get(l) & tempList(1).Get(l) & ", "
        Next
    Next
    Return finalString.SubString2(0, Max(0, finalString.Length - 2))
End Sub


2. Use it like this:
B4X:
myString = SetCustomOrder(myString, "PSC", True)

B4J Screenshot:
Untitled.png
 
Last edited:
Upvote 0

Mahares

Expert
Licensed User
Longtime User
@wonder:
Your function returns for this string: C1, P50, S3, C5, S10, C3, S1, P1
a new string like this: €P1, €P50, S1, S10, S3, ‚C1, ‚C3, ‚C5
But I think the old boy wants S3 to be ahead of S10.
 
Upvote 0

wonder

Expert
Licensed User
Longtime User
@wonder:
Your function returns for this string: C1, P50, S3, C5, S10, C3, S1, P1
a new string like this: €P1, €P50, S1, S10, S3, ‚C1, ‚C3, ‚C5
But I think the old boy wants S3 to be ahead of S10.
I see, I'll try to fix it tomorrow. Thanks for noticing! :)
 
Upvote 0

Roycefer

Well-Known Member
Licensed User
Longtime User
This is the code for my second solution:
B4X:
Dim input as String = "S3, C1024, etc....."
Dim PList, SList, CList as List
PList.Initialize: SList.Initialize: CList.Initialize
Dim stArr() as String = Regex.Split(",", input)
For j = 0 to stArr.Length-1
   Dim prefix as String = stArr(j).GetCharAt(0)
   Dim num as Int = stArr(j).Trim.SubString(1)
   Select prefix
      Case "P"
         PList.Add(num)
      Case "S"
         SList.Add(num)
      Case "C"
         CList.Add(num)
   End Select
Next

PList.Sort(True)
CList.Sort(True)
SList.Sort(True)

Dim result as StringBuilder
result.Initialize
For j = 0 to PList.Size-1
   result.Append("P").Append(PList.Get(j)).Append(" ,")
Next
For j = 0 to SList.Size-1
   result.Append("S").Append(SList.Get(j)).Append(" ,")
Next
For j = 0 to CList.Size-1
   result.Append("C").Append(CList.Get(j))
      If j<CList.Size-1 Then result.Append(" ,")
Next

'print out the answer:
Log(result.ToString)

Again, this is untested.
 
Upvote 0

Mahares

Expert
Licensed User
Longtime User
@Roycefer
Your solution will work if you add the below line under the Dim Input line:
B4X:
input=input.Replace(" ","")
And if you replace this line:
B4X:
Dim prefix as String = stArr(j).GetCharAt(0)
with:
B4X:
Dim prefix as String = stArr(j).CharAt(0)
Not bad for someone who has not had a chance to test it. Where have you been hiding all these years. You are a powerhouse.
 
Last edited:
Upvote 0

Roycefer

Well-Known Member
Licensed User
Longtime User
Ha, thanks. I knew there had to be some errors in there. That's what you get when you enter code directly into the forum's text editor. Coding without syntax highlighting, auto-complete and error catching is like driving without a seat belt, airbag and windshield. Erel should implement the full IDE in the forum's text editor.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
I'm either not getting the point or all given example are too complex to how I interpret it.

here's how I interpret it

B4X:
Dim data As String = "C1, P5, S3, C5, S9, C3, S1, P1"
Dim temp,output,item,items() As String
Dim l As List
l.Initialize
items=Regex.Split(", ",data)
For x=0 To items.Length-1
If items(x).StartsWith("P") Then l.Add(1 & items(x))
If items(x).StartsWith("S") Then l.Add(2 & items(x))
If items(x).StartsWith("C") Then l.Add(3 & items(x))
Next
l.Sort(True)
For x=0 To l.Size-1
item=l.Get(x)
output=output & item.SubString(1)
If x<l.Size-1 Then output=output&", "
Next
Log(output)

which outputs

** Activity (main) Create, isFirst = true **
P1, P5, S1, S3, S9, C1, C3, C5
** Activity (main) Resume **
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
ok, ignore my post I just read that he want nummerical sort for numbers with multiple digits aswell
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
this works for any numerical data aswell

B4X:
Sub Globals
Type mydata (sorttext As String,text As String)
End Sub

Sub Activity_Create(FirstTime As Boolean)
Dim data As String = "C1, P5, S3, C5, S9, C3, S1, P1, S10, S939"
Dim output,items(),prefix,num As String
Dim l As List
l.Initialize
items=Regex.Split(", ",data)
For x=0 To items.Length-1
Dim md As mydata
md.Initialize
num=("000000"&items(x).SubString(1))
If items(x).StartsWith("P") Then prefix="1"
If items(x).StartsWith("S") Then prefix="2"
If items(x).StartsWith("C") Then prefix="3"
md.sorttext=prefix &"-"&  num.SubString(num.Length-5)
md.text=items(x)
l.Add(md)
Next
l.SortType("sorttext",True)
For x=0 To l.Size-1
md=l.Get(x)
output=output & md.text
If x<l.Size-1 Then output=output&", "
Next
Log(output)

output: P1, P5, S1, S3, S9, S10, S939, C1, C3, C5

the trick is to create a sorting value so the data is stored as (prefix order, value order)

2-00010
1-00001
1-00005
2-00001
2-00003
2-00009
2-00010
2-00939
...

which makes the sort work correct
 
Last edited:
Upvote 0

udg

Expert
Licensed User
Longtime User
Hi sorex,
I see you solved the problem in a way similar to what I suggested in post #8 above, but smartly avoided the clean-up function using your mydata data structure.
Well done.
This reminds me that too many years spent programming on scarce-RAM devices (64-128KB) lead to think more in terms of using as few resources as possible than on CPU cycles and time to complete. Not a winning point of view these days of monstrous GB RAM devices! eheh

udg
 
Upvote 0
Top