3

I want to sort a struct array by dynamic field. Here is the struct

type user struct{
    Name string `json:"name"`
    Age int     `json:"age"`
    Status int  `json:"status "`
    Type  string  `json:"type"`
}

This is an array of struct

var UserArray []user

I have to sort this array by a given field that can be any field of user struct. but I will receive that sorting field from UI as a JSON tag. Like below

sort := agnutil.GetQueryParamString(<json tag>, "sort", 0, "name")

I have tried the sort function in golang but How to use that dynamically??

sort.Slice(UserArray , func(i, j int) bool {
        return UserArray[i].<givenfield>  < UserArray[j].<givenfield>
    })
2
  • In this answer. They have used switch conditions. My request is without switch or if condition can we do this dynamically. Commented Oct 31, 2021 at 18:55
  • 1
    stackoverflow.com/questions/18926303/… You can do it through reflect, but you're still going to have to do type switches to decide how to compare. Go is a statically typed language and does't (quite yet) support generic "Comparables". Go structs may feel like Javascript or Python objects, but they are totally different under the covers. Commented Oct 31, 2021 at 19:15

1 Answer 1

1

I wanted to try sorting a slice of structs by the field's json tag, here is what I ended up having, in case it helps anyone:

package main

import (
    "fmt"
    "reflect"
    "sort"
)

func sortBy(jsonField string, arr []num) {
    if len(arr) < 1 {
        return
    }

    // first we find the field based on the json tag
    valueType := reflect.TypeOf(arr[0])

    var field reflect.StructField

    for i := 0; i < valueType.NumField(); i++ {
        field = valueType.Field(i)

        if field.Tag.Get("json") == jsonField {
            break
        }
    }

    // then we sort based on the type of the field
    sort.Slice(arr, func(i, j int) bool {
        v1 := reflect.ValueOf(arr[i]).FieldByName(field.Name)
        v2 := reflect.ValueOf(arr[j]).FieldByName(field.Name)

        switch field.Type.Name() {
        case "int":
            return int(v1.Int()) < int(v2.Int())
        case "string":
            return v1.String() < v2.String()
        case "bool":
            return !v1.Bool() // return small numbers first
        default:
            return false // return unmodified
        }
    })

    fmt.Printf("\nsort by %s:\n", jsonField)
    prettyPrint(arr)
}

func prettyPrint(arr []num) {
    for _, v := range arr {
        fmt.Printf("%+v\n", v)
    }
}

type num struct {
    Id   int    `json:"id"`
    Name string `json:"name"`
    Big  bool   `json:"big"`
}

func main() {

    userArray := []num{
        {1, "one", false},
        {5, "five", false},
        {40, "fourty", true},
        {9, "nine", false},
        {60, "sixty", true},
    }

    fmt.Println("original:")
    prettyPrint(userArray)

    sortBy("id", userArray[:])
    sortBy("name", userArray[:])
    sortBy("big", userArray[:])

}
original:
{Id:1   Name:one     Big:false}
{Id:5   Name:five    Big:false}
{Id:40  Name:fourty  Big:true}
{Id:9   Name:nine    Big:false}
{Id:60  Name:sixty   Big:true}

sort by id
{Id:1   Name:one     Big:false}
{Id:5   Name:five    Big:false}
{Id:9   Name:nine    Big:false}
{Id:40  Name:fourty  Big:true}
{Id:60  Name:sixty   Big:true}

sort by name
{Id:5   Name:five    Big:false}
{Id:40  Name:fourty  Big:true}
{Id:9   Name:nine    Big:false}
{Id:1   Name:one     Big:false}
{Id:60  Name:sixty   Big:true}

sort by big
{Id:1   Name:one     Big:false}
{Id:9   Name:nine    Big:false}
{Id:5   Name:five    Big:false}
{Id:40  Name:fourty  Big:true}
{Id:60  Name:sixty   Big:true}

Sign up to request clarification or add additional context in comments.

1 Comment

Working perfectly!. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.