【GoLang】【工具分享】1、根据 JsonTag 对 slice 进行稳定排序(不支持嵌套)

1、背景

后端返回 List 列表中的 Json 序列化结构到前端,其中包含许多字段,业务需求可以按照某个字段对列表数据做 升/降序排序。

2、代码实现

前端将后端返回的 Json 关键字及排序方向作为参数传递至后端即可,后端根据 Json 排序关键字找到对应的 JsonTag 后,采用反射找到字段,再写自定义排序函数即可实现指定字段的排序。

Go 代码:

func SliceSortByJsonTag(slice interface{}, tagName string, ascending bool) {
    value := reflect.ValueOf(slice)
    typ := value.Type().Elem()

    // 匹配不到指定的 jsonTag 则不进行排序
    if name := getFieldName(typ, tagName); len(name) > 0 {
        sort.SliceStable(slice, func(i, j int) bool {
            fieldI := value.Index(i).FieldByName(name)
            fieldJ := value.Index(j).FieldByName(name)

            switch fieldI.Kind() {
            case reflect.String:
                if ascending {
                    return fieldI.String() < fieldJ.String()
                } else {
                    return fieldI.String() > fieldJ.String()
                }
            case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                if ascending {
                    return fieldI.Int() < fieldJ.Int()
                } else {
                    return fieldI.Int() > fieldJ.Int()
                }
            case reflect.Float32, reflect.Float64:
                if ascending {
                    return fieldI.Float() < fieldJ.Float()
                } else {
                    return fieldI.Float() > fieldJ.Float()
                }
            default:
                return false
            }
        })
    }
}

func getFieldName(typ reflect.Type, tagName string) string {
    for i := 0; i < typ.NumField(); i++ {
        field := typ.Field(i)
        if field.Tag.Get("json") == tagName {
            return field.Name
        }
    }

    return ""
}

单元测试:


type Person struct {
    Name   string `json:"name"`
    Age    int    `json:"age"`
    Gender string `json:"gender"`
}

func TestSliceSortByJsonTag(t *testing.T) {
    tests := []struct {
        name          string
        persons       []Person
        tagName       string
        ascending     bool
        expectedOrder []Person
    }{
        {
            name: "按姓名升序排序",
            persons: []Person{
                {Name: "Bob", Age: 30, Gender: "male"},
                {Name: "Alice", Age: 25, Gender: "female"},
                {Name: "Charlie", Age: 20, Gender: "male"},
            },
            tagName: "name",
            ascending: true,
            expectedOrder: []Person{
                {Name: "Alice", Age: 25, Gender: "female"},
                {Name: "Bob", Age: 30, Gender: "male"},
                {Name: "Charlie", Age: 20, Gender: "male"},
            },
        },
        {
            name: "按年龄降序排序",
            persons: []Person{
                {Name: "Bob", Age: 30, Gender: "male"},
                {Name: "Alice", Age: 25, Gender: "female"},
                {Name: "Charlie", Age: 20, Gender: "male"},
            },
            tagName: "age",
            ascending: false,
            expectedOrder: []Person{
                {Name: "Bob", Age: 30, Gender: "male"},
                {Name: "Alice", Age: 25, Gender: "female"},
                {Name: "Charlie", Age: 20, Gender: "male"},
            },
        },
        {
            name: "按性别升序排序",
            persons: []Person{
                {Name: "Bob", Age: 30, Gender: "male"},
                {Name: "Alice", Age: 25, Gender: "female"},
                {Name: "Charlie", Age: 20, Gender: "male"},
            },
            tagName: "gender",
            ascending: true,
            expectedOrder: []Person{
                {Name: "Alice", Age: 25, Gender: "female"},
                {Name: "Bob", Age: 30, Gender: "male"},
                {Name: "Charlie", Age: 20, Gender: "male"},
            },
        },
        {
            name: "未匹配到指定tag,不进行排序",
            persons: []Person{
                {Name: "Bob", Age: 30, Gender: "male"},
                {Name: "Alice", Age: 25, Gender: "female"},
                {Name: "Charlie", Age: 20, Gender: "male"},
            },
            tagName: "xxxx",
            ascending: true,
            expectedOrder: []Person{
                {Name: "Bob", Age: 30, Gender: "male"},
                {Name: "Alice", Age: 25, Gender: "female"},
                {Name: "Charlie", Age: 20, Gender: "male"},
            },
        },
    }

    for _, test := range tests {
        t.Run(test.name, func(t *testing.T) {
            persons := make([]Person, len(test.persons))
            copy(persons, test.persons)

            SliceSortByJsonTag(persons, test.tagName, test.ascending)

            if !reflect.DeepEqual(persons, test.expectedOrder) {
                t.Errorf("Sorting failed.\nExpected: %v\nGot: %v", test.expectedOrder, persons)
            }
        })
    }
}

3、工具改进

  • 可考虑支持 Json 嵌套结构的排序
  • 支持更多数据类型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值