Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort by dynamic field in go lang

Tags:

sorting

go

So I'm struggling to figure out how to sort following structure by "status" field (asc, desc)

type CampaignStatus struct {
    Campaign CampaignData
    Status   string `json:"status" bson:"status"`
}

type CampaignsPagination struct {
    Pagination PageMetadata  `json:"pagination"`
    Campaigns  []CampaignStatus `json:"campaigns"`
}

Example json of full campaigns pagination:

   "pagination": {
        "page": 1,
        "per_page": 15,
        "page_count": 1,
        "total_count": 4
    },
    "campaigns": [
        {
            "Campaign": {
                "_id": "57597fc6799e0fe41d0eede6",
                "name": "Test campaign",
                "description": "Just test campaign"
                "isEmail": true
            },
            "status": "expired"
        },
        ...
    }

So my function is retuting variable ret := &CampaignsPagination{} which is filled with data from mongoDB but status is determinated by other stuff in realtime. So reflect package is saying that I'm trying to sort type of *CampaignsPagination and everything what I use pretty much ends with errors like "type CampaignsPagination does not support indexing" (using sort packag) any hints are more then welcome

Update:

How I try to sort this (but it won't compile due to (type *CampaignsPagination does not support indexing

func (a *CampaignsPagination) Len() int {
    return len(a)
}
func (a *CampaignsPagination) Swap(i, j int) {
    a[i], a[j] = a[j], a[i]
}
func (a *CampaignsPagination) Less(i, j int) bool {
    if a[i].Status < a[j].Status {
        return true
    }
    if a[i].Status > a[j].Status {
        return false
    }
    return a[i].Status < a[j].Status
}
like image 943
Splendid Avatar asked Jun 01 '26 09:06

Splendid


1 Answers

Usually sorting is defined on a slice. You try to define sorting on your CampaignsPagination struct type.

This can also be done, but it's a little unusal (e.g. what would you do if you decide you want to have another order based on another field?). Since your receiver a is not a slice but a (pointer to a) wrapper struct, when indexing and returning the length, use the slice a.Campaigns. Also string values are comparable and ordered (lexically byte-wise). So you can simply compare CampaignStatus.Status values and return the result in Less().

func (a *CampaignsPagination) Len() int {
    return len(a.Campaigns)
}
func (a *CampaignsPagination) Swap(i, j int) {
    a.Campaigns[i], a.Campaigns[j] = a.Campaigns[j], a.Campaigns[i]
}
func (a *CampaignsPagination) Less(i, j int) bool {
    return a.Campaigns[i].Status < a.Campaigns[j].Status
}

A more logical solution would be to define the sorting on a slice, e.g.:

type CampaignStatusSort []CampaignStatus

func (c CampaignStatusSort) Len() int { return len(c) }

func (c CampaignStatusSort) Swap(i, j int) { c[i], c[j] = c[j], c[i] }

func (c CampaignStatusSort) Less(i, j int) bool { return c[i].Status < c[j].Status }

And then if you have a value of type *CampaignsPagination, you can sort the campaigns like this:

cp := &CampaignsPagination{} // Init struct

sort.Sort(CampaignStatusSort(cp.Campaigns))
like image 165
icza Avatar answered Jun 03 '26 13:06

icza