Android Question Sort json

miling

Member
How can I sort the following JSON based on the 'amount' in ascending order? I need a function that takes the JSON, sorts it, and returns the sorted JSON as output.

Simple json:
[{"service":"1","count":"110","amount":99000,"id":"1413"},{"service":"1","count":"3","amount":53000,"id":"3"},{"service":"1","country":"5","operator":"1","count":"29702","amount":63000,"id":"9"},{"service":"1","count":"605","amount":87000,"id":"95"},{"service":"1","count":"102","amount":46000,"id":"8"}]
 
Solution
B4X:
Sub Process_Globals
    Type ItemType (Service As String, Count As Int, Amount As Int, Id As Int)
    Dim msg As String = $"[{"service":"1","count":"110","amount":99000,"id":"1413"},{"service":"1","count":"3","amount":53000,"id":"3"},{"service":"1","country":"5","operator":"1","count":"29702","amount":63000,"id":"9"},{"service":"1","count":"605","amount":87000,"id":"95"},{"service":"1","count":"102","amount":46000,"id":"8"}]"$
End Sub

Sub AppStart (Args() As String)
    Dim res As List = Sort(msg.As(JSON).ToList)
    Log(res.As(JSON).ToString)
End Sub

Private Sub Sort(Input As List) As List
    Dim res As List
    res.Initialize
    For Each m As Map In Input
        res.Add(CreateItemType(m.Get("service"), m.Get("count")...

Erel

B4X founder
Staff member
Licensed User
Longtime User
B4X:
Sub Process_Globals
    Type ItemType (Service As String, Count As Int, Amount As Int, Id As Int)
    Dim msg As String = $"[{"service":"1","count":"110","amount":99000,"id":"1413"},{"service":"1","count":"3","amount":53000,"id":"3"},{"service":"1","country":"5","operator":"1","count":"29702","amount":63000,"id":"9"},{"service":"1","count":"605","amount":87000,"id":"95"},{"service":"1","count":"102","amount":46000,"id":"8"}]"$
End Sub

Sub AppStart (Args() As String)
    Dim res As List = Sort(msg.As(JSON).ToList)
    Log(res.As(JSON).ToString)
End Sub

Private Sub Sort(Input As List) As List
    Dim res As List
    res.Initialize
    For Each m As Map In Input
        res.Add(CreateItemType(m.Get("service"), m.Get("count"), m.Get("amount"), m.Get("id")))
    Next
    res.SortType("Amount", True)
    Return res
End Sub


Public Sub CreateItemType (Service As String, Count As Int, Amount As Int, Id As Int) As ItemType
    Dim t1 As ItemType
    t1.Initialize
    t1.Service = Service
    t1.Count = Count
    t1.Amount = Amount
    t1.Id = Id
    Return t1
 
Upvote 1
Solution

miling

Member
Hi @Erel
The output looks like this:

JSON:
[

    "[IsInitialized=true, amount=46000, count=102\n, country=null, description=null, emoji=null\n, id=8, operator=null, repeat=null\n, service=1, time=null]",

    "[IsInitialized=true, amount=53000, count=3\n, country=null, description=null, emoji=null\n, id=3, operator=null, repeat=null\n, service=1, time=null]",

    "[IsInitialized=true, amount=63000, count=29702\n, country=5, description=null, emoji=null\n, id=9, operator=1, repeat=null\n, service=1, time=null]",

    "[IsInitialized=true, amount=87000, count=605\n, country=null, description=null, emoji=null\n, id=95, operator=null, repeat=null\n, service=1, time=null]",

    "[IsInitialized=true, amount=99000, count=110\n, country=null, description=null, emoji=null\n, id=1413, operator=null, repeat=null\n, service=1, time=null]"

]

If I want it to be like this:

JSON:
[{"service":"1","count":"102","amount":46000,"id":"8"},{"service":"1","count":"3","amount":53000,"id":"3"},{"service":"1","country":"5","operator":"1","count":"29702","amount":63000,"id":"9"},{"service":"1","count":"605","amount":87000,"id":"95"},{"service":"1","count":"110","amount":99000,"id":"1413"}
]
 
Upvote 0

miling

Member
You are correct. I missed the step to convert the type back to map.


Please post the solut solution.

My solution is somewhat unconventional, but I am satisfied with the outcome.

My solution:
Sub AppStart (Args() As String)

    Dim res As List = Sort(msg.As(JSON).ToList)

    Dim dataList As List
    dataList.Initialize
    
    For Each str As String In res
        Dim jsonObject As Map
        jsonObject.Initialize
        
        Dim pairs() As String = Regex.Split(",\s*(?=\w+=)", str)
        For Each pair As String In pairs
            Dim keyValue() As String = Regex.Split("=", pair)
            If keyValue.Length = 2 Then
                Dim key As String = keyValue(0).Trim
                Dim value As String = keyValue(1).Trim
                jsonObject.Put(key, value)
            End If
        Next
        
        dataList.Add(jsonObject)
    Next
    
    Dim sortedJsonList As List
    sortedJsonList.Initialize
    
    For Each item As Map In dataList
        Dim jg As JSONGenerator
        jg.Initialize(item)
        sortedJsonList.Add(jg.ToString)
    Next
    
    Dim exJson As JSONGenerator
    exJson.Initialize2(sortedJsonList)
    Dim jsonSortResult As String = exJson.ToString
    jsonSortResult = jsonSortResult.Replace($"["{\"["$,$"[{""$)
    jsonSortResult = jsonSortResult.Replace($"}"]"$,"}]")
    jsonSortResult = jsonSortResult.Replace($"\""$,$"""$)
    jsonSortResult = jsonSortResult.Replace($"}","{"$,"},{")
    Log(jsonSortResult)
End Sub

@Erel Now, if possible, could you please explain the proper method to me?
 
Upvote 0

aeric

Expert
Licensed User
Longtime User
I am also looking for a solution.
Then I got an idea by using SQL. Here is my solution but the output JSON contains Null values. However, it is more flexible as you can sort more than one fields.

B4X:
 #AdditionalJar: sqlite-jdbc-3.41.2.1
Sub Process_Globals
    Type KeyValueList (Keys As List, Values As List)
    Dim msg As String = $"[{"service":"1","count":"110","amount":99000,"id":"1413"},{"service":"1","count":"3","amount":53000,"id":"3"},{"service":"1","country":"5","operator":"1","count":"29702","amount":63000,"id":"9"},{"service":"1","count":"605","amount":87000,"id":"95"},{"service":"1","count":"102","amount":46000,"id":"8"}]"$
    Dim OrderList As List = Array("amount")
End Sub

Sub AppStart (Args() As String)
    Dim List1 As List = msg.As(JSON).ToList
    Dim keys As List = Array("service", "country", "operator", "count", "amount", "id")
    Dim cols As List = Array(DBUtils.DB_TEXT, DBUtils.DB_TEXT, DBUtils.DB_TEXT, DBUtils.DB_TEXT, DBUtils.DB_TEXT, DBUtils.DB_TEXT)
    'Dim cols As List = Array(DBUtils.DB_INTEGER, DBUtils.DB_INTEGER, DBUtils.DB_INTEGER, DBUtils.DB_INTEGER, DBUtils.DB_INTEGER, DBUtils.DB_INTEGER)
    Dim schema As KeyValueList = CreateKeyValueList(keys, cols)

    Dim imdb As SQL ' in-memory database
    imdb.InitializeSQLite("", ":memory:", True)
    DBUtils.CreateTable(imdb, "TempList", ConvertKeyValueList2Map(schema), "id")
    DBUtils.InsertMaps(imdb, "TempList", List1)
    
    Dim Query As String = "SELECT * FROM TempList ORDER BY " & List2CommaString(OrderList)
    Dim SortedMap As Map = DBUtils.ExecuteJSON(imdb, Query, Null, 0, cols)
    Dim root As List = SortedMap.Get("root")
    Log(root.As(JSON).ToString)
    StartMessageLoop
End Sub

Public Sub ConvertKeyValueList2Map (kvl As KeyValueList) As Map
    Dim obj As Map = CreateMap()
    For i = 0 To kvl.Keys.Size - 1
        obj.Put(kvl.Keys.Get(i), kvl.Values.Get(i))
    Next
    Return obj
End Sub

Private Sub List2CommaString (list As List) As String
    Dim SB As StringBuilder
    SB.Initialize
    For Each item In list
        If SB.Length > 0 Then SB.Append(", ")
        SB.Append(item)
    Next
    Return SB.ToString
End Sub

Public Sub CreateKeyValueList (Keys As List, Values As List) As KeyValueList
    Dim t1 As KeyValueList
    t1.Initialize
    t1.Keys = Keys
    t1.Values = Values
    Return t1
End Sub

JSON:
Waiting for debugger to connect...
Program started.
CreateTable: CREATE TABLE IF NOT EXISTS [TempList] ([service] TEXT, [country] TEXT, [operator] TEXT, [count] TEXT, [amount] TEXT, [id] TEXT PRIMARY KEY)
InsertMaps (first query out of 5): INSERT INTO [TempList] ([amount], [service], [count], [id]) VALUES (?, ?, ?, ?)
ExecuteJSON: SELECT * FROM TempList ORDER BY amount
[
    {
        "country": null,
        "amount": "46000",
        "service": "1",
        "count": "102",
        "id": "8",
        "operator": null
    },
    {
        "country": null,
        "amount": "53000",
        "service": "1",
        "count": "3",
        "id": "3",
        "operator": null
    },
    {
        "country": "5",
        "amount": "63000",
        "service": "1",
        "count": "29702",
        "id": "9",
        "operator": "1"
    },
    {
        "country": null,
        "amount": "87000",
        "service": "1",
        "count": "605",
        "id": "95",
        "operator": null
    },
    {
        "country": null,
        "amount": "99000",
        "service": "1",
        "count": "110",
        "id": "1413",
        "operator": null
    }
]
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
could you please explain the proper method to me?
All that was missing was:
I missed the step to convert the type back to map.
One way to implement (I'm just including modified Sort method and additional helper sub):
B4X:
Private Sub Sort(Input As List) As List
    Dim res As List
    res.Initialize
    For Each m As Map In Input
        res.Add(CreateItemType(m.Get("service"), m.Get("count"), m.Get("amount"), m.Get("id")))
    Next
    res.SortType("Amount", True)
    '
    'Convert itemtypes back to maps
    '
    Dim res2 As List
    res2.Initialize
    For Each item As ItemType In res
        res2.Add(ItemType2Map(item))
    Next
    Return res2
End Sub

Public Sub ItemType2Map(input As ItemType) As Map
    Dim m As Map
    m.Initialize
    m.Put("service", input.Service)
    m.Put("count", input.Count)
    m.Put("amount", input.Amount)
    m.Put("id", input.Id)
    Return m
End Sub
 
Upvote 2
Top