go语言简单快速的按顺序遍历kv结构(map)

文章目录

需求描述

在go语言中,如果需要对map遍历,每次输出的顺序是不固定的,可以考虑存储为二维切片或结构体。

假设现在需要在页面的下拉菜单中展示一些基础的选项,需要服务端输出如下的结构:

go 复制代码
{
    "code": 200000,
    "message": "请求成功",
    "data": [
        {
            "id": 1,
            "name": "中国"
        },
        {
            "id": 2,
            "name": "美国"
        },
        {
            "id": 3,
            "name": "韩国"
        },
        {
            "id": 4,
            "name": "新加坡"
        }
    ]
}

用map实现

首先,常规思维,考虑用map存储,实现方式如下:

先定义好常量值和映射关系,代码路径:gozero/internal/constants/commonConstants.go

go 复制代码
package constants

const (
	// 国家常量值
	CountryChina     = 1
	CountryAmerica   = 2
	CountryKorea     = 3
	CountrySingapore = 4
)

var (
	//国家常量映射关系
	CountryMap = map[int]string{
		CountryChina:     "中国",
		CountryAmerica:   "美国",
		CountryKorea:     "韩国",
		CountrySingapore: "新加坡",
	}
)

然后在逻辑层使用上面定义的map:

代码路径:gozero/internal/logic/common/simpleselectlogic.go

go 复制代码
func (l *SimpleSelectLogic) SimpleSelect(req *types.SimpleSelectRequest) (resp *utils.Response, err error) {
	paramType := req.Type
	var data interface{}
	switch paramType {
	case "country":
		data = mapToSlice(constants.CountryMap)
	case "others":
		data = nil
	}
	//成功返回
	return utils.SuccessResponse(data), nil
}

// 定义结构体
type Item struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func mapToSlice(m map[int]string) []Item {
	// 提取所有的key
	keys := make([]int, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}

	// 将key和value组合成结构体切片
	sortedSlice := make([]Item, 0, len(m))
	for _, k := range keys {
		sortedSlice = append(sortedSlice, Item{ID: k, Name: m[k]})
	}

	return sortedSlice
}

通过上面的方式已经可以输出我们想要的结构了:

但是,多运行几次就会发现,每次运行后的顺序并不一致,因为go中的map的遍历是不保证顺序的。

那么,直接在遍历的时候对key进行排序再按照key的顺序输出是否可行?尝试如下:

按照map的key排序

mapToSlice方法中加上对key的排序:

go 复制代码
func mapToSortedSlice(m map[int]string) []Item {
	// 提取所有的key
	keys := make([]int, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}

	// 对key进行排序
	sort.Ints(keys)

	// 根据排序后的key顺序,将key和value组合成结构体切片
	sortedSlice := make([]Item, 0, len(m))
	for _, k := range keys {
		sortedSlice = append(sortedSlice, Item{ID: k, Name: m[k]})
	}

	return sortedSlice
}

这样修改后,可以保证输出的都是按照key由小到大排序的结果。

但是,假如现在有一种情况,产品要求在已有的下拉选项中插入一个新的选项值,并且顺序在中间。比如加入一个"英国"的选项,在"美国"后面,我们修改常量枚举值如下:

package constants

const (
	// 国家常量值
	CountryChina     = 1
	CountryAmerica   = 2
	CountryEngland   = 5 //增加的"英国"的枚举值
	CountryKorea     = 3
	CountrySingapore = 4
)

var (
	//国家常量映射关系
	CountryMap = map[int]string{
		CountryChina:     "中国",
		CountryAmerica:   "美国",
		CountryEngland:   "英国", //增加的"英国"排在"美国"下面
		CountryKorea:     "韩国",
		CountrySingapore: "新加坡",
	}
)

按照上面的方法,运行后会发现,新增加的枚举值排在了最后面:

这是因为在mapToSortedSlice方法中根据map的key排序后,后来新增的key是5,所以会排在最后面。由于已有的key(1-4)不能修改,那么只能考虑再定义一个排序的切片来自定义需要排序的数据:

go 复制代码
CountrySort = []int{
		CountryChina,
		CountryAmerica,
		CountryEngland,
		CountryKorea,
		CountrySingapore,
	}

然后在 mapToSortedSlice 方法中对上面的CountrySort进行排序后遍历。这种方法可以实现需求,但是会比较麻烦。因此,我决定改为使用二维切片来存储数据。

用二维切片实现

go 复制代码
package constants

const (
	// 国家常量值--改为存储字符串
	CountryStrChina     = "1"
	CountryStrAmerica   = "2"
	CountryStrEngland   = "5"
	CountryStrKorea     = "3"
	CountryStrSingapore = "4"
)

var (
	//国家常量映射关系--切片存储
	CountrySlice = [][]string{
		{CountryStrChina, "中国"},
		{CountryStrAmerica, "美国"},
		{CountryStrEngland, "英国"},
		{CountryStrKorea, "韩国"},
		{CountryStrSingapore, "新加坡"},
	}
)

然后,处理切片的函数如下:

go 复制代码
func formatSlice(m [][]string) []Item {
	// 根据排序后的key顺序,将key和value组合成结构体切片
	sortedSlice := make([]Item, 0, len(m))
	for _, v := range m {
		id, _ := strconv.Atoi(v[0])
		sortedSlice = append(sortedSlice, Item{ID: id, Name: v[1]})
	}
	return sortedSlice
}

调用一下:

go 复制代码
func (l *SimpleSelectLogic) SimpleSelect(req *types.SimpleSelectRequest) (resp *utils.Response, err error) {
	paramType := req.Type
	var data interface{}
	switch paramType {
	case "country":
		//data = mapToSortedSlice(constants.CountryMap)
		data = formatSlice(constants.CountrySlice)
	case "others":
		data = nil
	}
	//成功返回
	return utils.SuccessResponse(data), nil
}

输出结果验证一下,会发现按照我们定义二维切片的顺序输出了:

至此,问题解决。

如果你也需要在go中按顺序遍历kv结构,并且想偷个懒,那么不妨试试这样的方式。如果还想再偷懒,那么可以连常量都省去了,直接这么写:

go 复制代码
//国家常量映射关系--切片存储-省去常量
CountrySliceSimple = [][]string{
  {"1", "中国"},
  {"2", "美国"},
  {"5", "英国"},
  {"3", "韩国"},
  {"4", "新加坡"},
}

用结构体实现

当然,也可以直接在定义的时候存储为结构体:

go 复制代码
package constants

const (
	// 国家常量值
	CountryChina     = 1
	CountryAmerica   = 2
	CountryEngland   = 5 //增加的"英国"的枚举值
	CountryKorea     = 3
	CountrySingapore = 4

	//国家常量映射关系--结构体存储
	CountryStruct = []struct {
		Id   int    `json:"id"`
		Name string `json:"name"`
	}{
		{CountryChina, "中国"},
		{CountryAmerica, "美国"},
		{CountryEngland, "英国"},
		{999, "法国"},
		{CountryKorea, "韩国"},
		{CountrySingapore, "新加坡"},
	}
)

使用的时候也不需要转换了:

go 复制代码
func (l *SimpleSelectLogic) SimpleSelect(req *types.SimpleSelectRequest) (resp *utils.Response, err error) {
	paramType := req.Type
	var data interface{}
	switch paramType {
	case "country":
		//data = mapToSortedSlice(constants.CountryMap) //使用map
		//data = formatSlice(constants.CountrySlice) //使用切片
		data = constants.CountryStruct //使用结构体
	case "others":
		data = nil
	}
	//成功返回
	return utils.SuccessResponse(data), nil
}

源代码:https://gitee.com/rxbook/go-demo-2025/tree/master/gozero

相关推荐
smart_ljh20 分钟前
JS设计模式之单例原型
开发语言·javascript·设计模式
Java极客33 分钟前
DeepSeek访问卡顿?腾讯云接入让服务更稳定!
后端
ybq195133454311 小时前
javaEE-11.javaScript入门
开发语言·javascript·ecmascript
来恩10031 小时前
C# ASP.NET 介绍
开发语言·c#·asp.net
bestwinner2 小时前
java 集合取交集
java·开发语言
数据小小爬虫2 小时前
高效利用Python爬虫开发批量获取商品信息
开发语言·爬虫·python
韦慕霖2 小时前
C#语言的云计算
开发语言·后端·golang
文军的烹饪实验室2 小时前
使用 Flask 构建流式返回服务
后端·python·flask
为啥不吃肉捏2 小时前
《我在技术交流群算命》(三):QML的Button为什么有个蓝框去不掉啊(QtQuick.Controls由Qt5升级到Qt6的异常)
开发语言·c++·qt·开源
全栈Blue3 小时前
记录一次报错:spring security 403报错
java·后端·spring