Golang-Map有序输出——使用orderedmap库实现

前言

工作中遇到一个问题:需要导出一个MySQL表格,表格内容由sql查询得来。但现在发现,所导出的表格中,各列的顺序不确定。多次导出, 每一次的序列顺序也是不定的。
因此确定是后端,Map使用相关导致的问题。自己想法是——创一个map[int]map{}类型,即map套map。里面的map保存原有内容,int保存序号。
经网络搜索,发现已经有开源库提供了此功能,即https://github.com/iancoleman/orderedmap库。因此对他进行学习。

功能概述

Golang内置的Map是无需的,而OrderedMap则是提供了一个有顺序的Map。其顺序是插入时的顺序。

下面是官方文档中提供的功能描述:

package main

import (
	"encoding/json"
	"github.com/iancoleman/orderedmap"
	"sort"
)

func main() {

	// use New() instead of o := map[string]interface{}{}
	o := orderedmap.New()

	// use SetEscapeHTML() to whether escape problematic HTML characters or not, defaults is true
	o.SetEscapeHTML(false)

	// use Set instead of o["a"] = 1
	o.Set("a", 1)

	// add some value with special characters
	o.Set("b", "\\.<>[]{}_-")

	// use Get instead of i, ok := o["a"]
	val, ok := o.Get("a")

	// use Keys instead of for k, v := range o
	keys := o.Keys()
	for _, k := range keys {
		v, _ := o.Get(k)
	}

	// use o.Delete instead of delete(o, key)
	o.Delete("a")

	// serialize to a json string using encoding/json
	bytes, err := json.Marshal(o)
	prettyBytes, err := json.MarshalIndent(o, "", "  ")

	// deserialize a json string using encoding/json
	// all maps (including nested maps) will be parsed as orderedmaps
	s := `{"a": 1}`
	err := json.Unmarshal([]byte(s), &o)

	// sort the keys
	o.SortKeys(sort.Strings)

	// sort by Pair
	o.Sort(func(a *orderedmap.Pair, b *orderedmap.Pair) bool {
		return a.Value().(float64) < b.Value().(float64)
	})
}


其主要用法如下:
通过orderedmap.New()新建一个Map。
通过o.Set("a", 1)设置Map中的元素内容。 (注意:key必须是string)
通过o.Get("a")获取map内容。
通过keys达到for range效果。
通过o.Delete("a")进行元素删除。
通过SortKeysSort进行排序。

源码阅读

OrderedMap的源码,包含注释等在内,一共266行,可谓是十分简短。

在这里插入图片描述

其中,一共有如下的方法可使用:

在这里插入图片描述

OrderedMap的数据结构

New()出来的那个Map是这样:


type OrderedMap struct {
	keys       []string
	values     map[string]interface{}
	escapeHTML bool
}

其中:escapeHTML是用来表示是否转义HTML字符,默认为true表示转义。可以调用SetEscapeHTML(false)方法来设置不转义HTML字符。

剩余keys是[]string,values是map。

OrderedMap有序输出的原理

做这样一个测试:


func main() {

	o := orderedmap.New()

	o.Set("key2", "v2")
	o.Set("key3", "v3")
	o.Set("key1", 1)
	fmt.Println(o.Keys())
	fmt.Println(o.Values())

}

其中,keys和values分别用于返回所有的key和value。


func (o *OrderedMap) Keys() []string {
	return o.keys
}

func (o *OrderedMap) Values() map[string]interface{} {
	return o.values
}

输出结果为:

在这里插入图片描述

我们可以看到,其key是按输入顺序进行排列的切片,value是保存了对应key和value的map。

在输出时,通过定序的key,去访问不定序的value,最终输出时实现定序的效果。(定序:指与输入顺序相同)。

基础方法

初始化

通过New方法生成一个OrderedMap,其中可以通过SetEscapeHTML对该参数进行设置。

func New() *OrderedMap {
	o := OrderedMap{}
	o.keys = []string{}
	o.values = map[string]interface{}{}
	o.escapeHTML = true
	return &o
}

func (o *OrderedMap) SetEscapeHTML(on bool) {
	o.escapeHTML = on
}

值的设置、查看及删除

通过Get获取指定key值对应的value。
通过Set对键值对进行设置。
通过Delete删除键值对。

func (o *OrderedMap) Get(key string) (interface{}, bool) {
	val, exists := o.values[key]
	return val, exists
}

func (o *OrderedMap) Set(key string, value interface{}) {
	_, exists := o.values[key]
	if !exists {
		o.keys = append(o.keys, key)
	}
	o.values[key] = value
}

func (o *OrderedMap) Delete(key string) {
	// check key is in use
	_, ok := o.values[key]
	if !ok {
		return
	}
	// remove from keys
	for i, k := range o.keys {
		if k == key {
			o.keys = append(o.keys[:i], o.keys[i+1:]...)
			break
		}
	}
	// remove from values
	delete(o.values, key)
}

获取所有kv值

通过Keys()获取所有的key。
通过Values()获取所有的Value。

func (o *OrderedMap) Keys() []string {
	return o.keys
}

func (o *OrderedMap) Values() map[string]interface{} {
	return o.values
}

SortSortKeys方法

SortKeys()方法

// SortKeys Sort the map keys using your sort func
func (o *OrderedMap) SortKeys(sortFunc func(keys []string)) {
	sortFunc(o.keys)
}

即根据传入的方法规则,对key值进行排序。例如,按字母顺序排:
o.SortKeys(sort.Strings)
测试:

func main() {

	o := orderedmap.New()

	o.Set("key2", "v2")
	o.Set("key3", "v3")
	o.Set("key1", 1)
	fmt.Println(o.Keys())
	fmt.Println(o.Values())
	o.SortKeys(sort.Strings)
	fmt.Println(o.Keys())
	fmt.Println(o.Values())
}

结果:

在这里插入图片描述

Sort()方法

源码如下:

// Sort Sort the map using your sort func
func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) {
	pairs := make([]*Pair, len(o.keys))
	for i, key := range o.keys {
		pairs[i] = &Pair{key, o.values[key]}
	}

	sort.Sort(ByPair{pairs, lessFunc})

	for i, pair := range pairs {
		o.keys[i] = pair.key
	}
}

其中,pair如下:

type Pair struct {
	key   string
	value interface{}
}

该方法内,首先通过pair保留每一个键值对。之后哦通过传入的lessFunc进行排序,再按照排序后的顺序,赋值到内部,从而实现排序。
用法示例:

// sort by Pair
o.Sort(func(a *orderedmap.Pair, b *orderedmap.Pair) bool {
     return a.Value().(float64) < b.Value().(float64)
})

Json相关

UnmarshalJSON方法,用于将JSON转换为有序MAP。

func (o *OrderedMap) UnmarshalJSON(b []byte) error {
	if o.values == nil {
		o.values = map[string]interface{}{}
	}
	err := json.Unmarshal(b, &o.values)
	if err != nil {
		return err
	}
	dec := json.NewDecoder(bytes.NewReader(b))
	if _, err = dec.Token(); err != nil { // skip '{'
		return err
	}
	o.keys = make([]string, 0, len(o.values))
	return decodeOrderedMap(dec, o)
}

MarshalJSON反之,序列化为JSON。

func (o OrderedMap) MarshalJSON() ([]byte, error) {
	var buf bytes.Buffer
	buf.WriteByte('{')
	encoder := json.NewEncoder(&buf)
	encoder.SetEscapeHTML(o.escapeHTML)
	for i, k := range o.keys {
		if i > 0 {
			buf.WriteByte(',')
		}
		// add key
		if err := encoder.Encode(k); err != nil {
			return nil, err
		}
		buf.WriteByte(':')
		// add value
		if err := encoder.Encode(o.values[k]); err != nil {
			return nil, err
		}
	}
	buf.WriteByte('}')
	return buf.Bytes(), nil
}

最后还有decodeOrderedMapdecodeSlice两个方法,是辅助json转换时候所用的,在此不再阐述。

总结&心得

1、通过OrderedMap可以实现MAP的有序化,按输入顺序输出map的内容,或按自定义规则排序后进行输出。

2、使用OrderedMap会导致性能下降,不要过度依赖。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/373419.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分布式文件存储系统minio

参考Linux搭建免费开源对象存储 wget https://dl.minio.io/server/minio/release/linux-amd64/minio yum install -y wget yum install -y wget wget https://dl.minio.io/server/minio/release/linux-amd64/minio chmod x minio sudo mv minio /usr/local/bin/ minio --vers…

黑马头条 Kafka

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 知…

玩家笔记:幻兽帕鲁搭建服务器开服教程

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…

1.0 Zookeeper 分布式配置服务教程

ZooKeeper 是 Apache 软件基金会的一个软件项目&#xff0c;它为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。 ZooKeeper 的架构通过冗余服务实现高可用性。 Zookeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来&#xff0c;构成一个高…

实践:微服务版本升级步骤以及maven仓库相关概念

进行微服务开发的时候&#xff0c;上层服务依赖于下层的服务的api&#xff0c;比如适配属于上层服务&#xff0c;用户属于下层服务。 例子: 上层服务 <!--订单管理微服务api依赖--> <dependency><groupId>com.jn.server</groupId><artifactId>…

docker部署docker运维工具

简介 主要功能:管理容器,管理镜像,管理容器网络 安装 拉取镜像 docker pull joinsunsoft/docker.ui:1.0.1 启动容器 docker run -d --name docker.ui --restart always -v /var/run/docker.sock:/var/run/docker.sock -p 10039:8999 joinsunsoft/docker.ui:1.0.1 使用 打…

【ArcGIS微课1000例】0101:删除冗余节点或折点

文章目录 一、实验描述二、实验数据三、实验过程1. 手动删除2. 简化线工具四、注意事项一、实验描述 矢量数据获取通常来源于手动或者ArcScan自动采集,其基本存储方式就是记录每个要素的点坐标,如点要素就是一个坐标、线要素由多个点要素连接形成。当某段线要素被过多的节点…

多模态对比语言图像预训练CLIP:打破语言与视觉的界限,具备零样本能力

多模态对比语言图像预训练CLIP:打破语言与视觉的界限,具备零样本能力。 一种基于多模态(图像、文本)对比训练的神经网络。它可以在给定图像的情况下,使用自然语言来预测最相关的文本片段,而无需为特定任务进行优化。CLIP的设计类似于GPT-2和GPT-3,具备出色的零射击能力…

C++中的构造函数

一、类中的6个默认成员函数 如果一个类中什么都没有&#xff0c;那么这个类就是一个空类。但空类中并不是真的什么都没有&#xff0c;编译器会自动生成6个默认成员函数 分别是构造函数、析构函数、拷贝构造函数、赋值重载函数、普通对象取地址函数、const对象取地址函数 本篇…

全链游戏的未来趋势与Bridge Champ的创新之路

为了充分探索全链游戏的特点和趋势&#xff0c;以及Bridge Champ如何作为一个创新案例融入这一发展脉络&#xff0c;我们需要深入了解这两者之间的互动和相互影响。全链游戏&#xff0c;或完全基于区块链的游戏&#xff0c;代表了游戏行业的一个重要转型&#xff0c;它们利用区…

Android meminfo 查看方法及解析

目录 Android 上查看memory 信息的方法 内存限制的信息 手动释放缓存 例 adb shell dumpsys meminfo pid 解析 adb shell dumpsys meminfo 汇总信息说明 Total RAM Free RAM ION Used RAM Lost RAM ZRAM /proc/meminfo 参考文档 Android 上查看memory 信息的方法 …

华为视频监控接入到视频监控平台 (华为网路监控摄像机IPC和华为视频节点设备VCN)

目 录 一、设备介绍 1.1 华为VCN介绍 1.2 AS-V1000视频监控平台介绍 1.3 平台服务器配置说明 二、安装、配置HW_IVS软件 2.1下载安装HW_IVS软件 2.2登录HW_IVS 2.3共享到外域 三、配置华为外域参数 3.1 PCG模块设置 3.2通信协议GBT28181配置 3.3传…

Python学习路线 - Python高阶技巧 - PySpark案例实战

Python学习路线 - Python高阶技巧 - PySpark案例实战 前言介绍Spark是什么Python On SparkPySparkWhy PySpark 基础准备PySpark库的安装构建PySpark执行环境入口对象PySpark的编程模型 数据输入RDD对象Python数据容器转RDD对象读取文件转RDD对象 数据计算map方法flatMap方法red…

肿瘤免疫分型

Elements of cancer immunity and the cancer-immune set point - PubMed (nih.gov) Daniel S Chen , Ira Mellman 人类的抗癌免疫可分为三种主要表型&#xff1a;免疫沙漠表型&#xff08;棕色&#xff09;、免疫排除表型&#xff08;蓝色&#xff09;和免疫炎症型&#xff0…

查大数据检测到风险等级太高是怎么回事?

随着金融风控越来越多元化&#xff0c;大数据作为新兴的技术被运用到贷前风控中去了&#xff0c;不少人也了解过自己的大数据&#xff0c;但是由于相关知识不足&#xff0c;看不懂报告&#xff0c;在常见的问题中&#xff0c;大数据检测到风险等级太高是怎么回事呢?小易大数据…

双非本科准备秋招(17.1)—— 力扣二叉树

1、257. 二叉树的所有路径 要求返回根节点到叶子节点的所有路径&#xff0c;这里用前序遍历就好。 每次递归前&#xff0c;都让字符串s加上当前节点的值和“->”&#xff0c;然后判断是否为叶子节点&#xff0c;如果是的话&#xff0c;说明这条路径是一个答案&#xff0c;因…

unity实现第一人称和第三人称

在角色设置两个挂载点&#xff0c;第一人称时&#xff0c;相机放在eys上面&#xff0c;切换第三人称时&#xff0c;放置到3rd节点上面&#xff0c;调整节点位置&#xff0c;达到期望效果 代码 void ThirdView(){Debug.Log("切换到第三人称");camera.SetParent(third…

本地部署TeamCity打包发布GitLab管理的.NET Framework 4.5.2的web项目

本地部署TeamCity 本地部署TeamCity打包发布GitLab管理的.NET Framework 4.5.2的web项目部署环境配置 TeamCity 服务器 URLTeamCity 上 GitLab 的相关配置GitLab 链接配置SSH 配置项目构建配置创建项目配置构建步骤构建触发器结语本地部署TeamCity打包发布GitLab管理的.NET Fra…

IntelliJ IDE 插件开发 | (六)内部模式的使用

系列文章 IntelliJ IDE 插件开发 |&#xff08;一&#xff09;快速入门IntelliJ IDE 插件开发 |&#xff08;二&#xff09;UI 界面与数据持久化IntelliJ IDE 插件开发 |&#xff08;三&#xff09;消息通知与事件监听IntelliJ IDE 插件开发 |&#xff08;四&#xff09;来查收…

2024.1.26力扣每日一题——边权重均等查询

2024.1.26 题目来源我的题解方法一 使用dfs对每一组查询都求最近公共祖先&#xff08;会超时&#xff0c;通不过&#xff09;方法二 不需要构建图&#xff0c;直接在原始数组上进行求最大公共祖先的操作。 题目来源 力扣每日一题&#xff1b;题序&#xff1a;2846 我的题解 …