在Go lang中按动态字段排序

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
}

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))

There is no one to one relationship between CampaignsPagination and Status. CampaignsPagination has a slice of CampaignStatus as a field; means CampaignsPagination has many Statuses.

So I assume that you want to sort the field Campaigns []CampaignStatus. Your code is almost correct; for CampaignsPagination to implement the interface sort.Interface:

func (x *CampaignsPagination) Len() int {
    return len(x.Campaigns)
}
func (x *CampaignsPagination) Swap(i, j int) {
    a := x.Campaigns
    a[i], a[j] = a[j], a[i]
}
func (x *CampaignsPagination) Less(i, j int) bool {
    a := x.Campaigns
    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
}

But it would be more logical to introduce a sortable type:

type Statuses []CampaignStatus

func (a Statuses) Len() int {
    return len(a)
}
func (a Statuses) Swap(i, j int) {
    a[i], a[j] = a[j], a[i]
}
func (a Statuses) 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
}

And then use this as the field Campaigns:

type CampaignsPagination struct {
    Pagination PageMetadata `json:"pagination"`
    Campaigns  Statuses     `json:"campaigns"`
}