~~~
package toolutilv3
import (
"encoding/json"
"fmt"
"reflect"
"sort"
)
var ToolSlice = toolSliceUtil{}
type toolSliceUtil struct{}
/*
AppendHead 向切片头部追加元素
ChangeInterfaceToSlice 将 []interface{} 类型的切片转换成指定类型的切片
ChangeSliceToInterface 将任意切片类型的切片 转换为 []interface{} 类型
Contains 方法可以检查给定的切片中是否存在某个元素
ConvertSliceToMap 将结构体切片转出[]map[string]interface{}
FindDiffVales 找到两个切片中不同的元素,返回两个切片中不同元素组成的切片
FindSameVales 找到两个切片中同同的元素,返回两个切片中相同同元素组成的切片
FindOutVales 从slice1里剔除slice2存在的值
GroupByKey 将[]map[string]interface{}按某个字段分组
MapListKeyToIntSlice 函数从输入的 []map[string]interface{} 类型的切片中提取指定 key 对应的 int 类型数值,并返回一个 []int 类型的切片。
UniqueInterface 函数用于合并多个任意类型的切片并去重
SetNilToDefaultValueByMap 字典中所有键名在 keys 数组中出现过且对应的键值为 nil 的元素改为默认值 defaultValue
SetNilToDefaultValueByMapV2 处理nil值,出现在keys的nil设置成[], 其余的nil设置成""
SetNilToDefaultValueBySlice 切片中所有键名在 keys 数组中出现过且对应的键值为 nil 的元素改为默认值 defaultValue
SetNilToDefaultValueBySliceV2 处理nil值,出现在keys的nil设置成[], 其余的nil设置成""
SortByKey 函数按照指定的 key 对应的值,对输入的 []map[string]interface{} 类型的切片进行排序。
StringToSlice 将字符串解析为切片类型
StringToSliceInt 将字符串解析为Int切片类型
StringToMap 将字符串解析为字典类型
StringToMapSlice 将字符串解析为切片类型
*/ //备注
// AppendHead 向切片头部追加元素
func (s *toolSliceUtil) AppendHead(slice []interface{}, addData interface{}) []interface{} {
return append([]interface{}{addData}, slice...)
}
// ChangeInterfaceToSlice 将 []interface{} 类型的切片转换成指定类型的切片
func (s *toolSliceUtil) ChangeInterfaceToSlice(slice []interface{}, elementType reflect.Type) interface{} {
// 创建一个新切片,使用reflect.MakeSlice函数创建
// 第一个参数是要创建的切片类型,使用reflect.SliceOf函数获取
// 第二个参数是要创建的切片长度和容量,这里都设置为输入 slice 的长度
newSlice := reflect.MakeSlice(reflect.SliceOf(elementType), len(slice), len(slice))
// 遍历输入的切片,将其中每个元素转换成指定类型,并设置到新切片中
for i := 0; i < len(slice); i++ {
// 使用reflect.ValueOf函数获取当前元素的反射对象
// 然后调用Convert方法将其转换成指定类型,并使用Set方法设置到新切片中
newSlice.Index(i).Set(reflect.ValueOf(slice[i]).Convert(elementType))
}
// 返回转换后的新切片,注意需要使用 .Interface() 方法将其转换成 interface{} 类型
return newSlice.Interface()
}
// ChangeSliceToInterface 将任意切片类型的切片 转换为 []interface{} 类型
func (s *toolSliceUtil) ChangeSliceToInterface(slice interface{}) []interface{} {
sliceValue := reflect.ValueOf(slice)
if sliceValue.Kind() != reflect.Slice {
panic("ToInterfaceSlice only supports slice types")
}
interfaceSlice := make([]interface{}, sliceValue.Len())
for i := 0; i < sliceValue.Len(); i++ {
interfaceSlice[i] = sliceValue.Index(i).Interface()
}
return interfaceSlice
}
/*
参数slice是要检查的切片,参数item是要查找的元素
*/ // Contains 方法可以检查给定的切片中是否存在某个元素
func (s *toolSliceUtil) Contains(slice interface{}, item interface{}) bool {
// 使用反射获取slice的值
v := reflect.ValueOf(slice)
// 检查v的类型是否为切片类型
if v.Kind() != reflect.Slice {
return false
}
// 获取slice元素的类型
elemType := v.Type().Elem()
// 如果元素类型不可比较,则无法使用该方法进行查找
if !elemType.Comparable() {
return false
}
// 遍历整个切片
for i := 0; i < v.Len(); i++ {
// 使用DeepEqual函数比较切片中的元素和要查找的元素
if reflect.DeepEqual(item, v.Index(i).Interface()) {
return true
}
}
// 如果循环结束仍未找到匹配项,则返回false
return false
}
/*
该函数接受一个参数 slice,可以接受任何类型的切片结构体。
在函数内部,我们首先使用反射获取传入参数的值和类型,然后遍历每个结构体实例,将其转换为 map[string]interface{} 类型,并添加到结果切片中。最后,返回转换好的切片。
如果传入的参数不是切片类型或者切片元素不是结构体类型,函数会返回一个错误 not a slice 或者 not a struct。这可以防止代码在运行时出现崩溃或者异常情况。
*/ // ConvertSliceToMap 将结构体切片转出[]map[string]interface{}
func (s *toolSliceUtil) ConvertSliceToMap(slice interface{}) []map[string]interface{} {
// 获取slice的值
sliceValue := reflect.ValueOf(slice)
// 检查slice是否为切片类型
if sliceValue.Kind() != reflect.Slice {
return []map[string]interface{}{}
}
// 创建一个空的map切片,用于存储转换后的结果
var result []map[string]interface{}
// 遍历整个slice
for i := 0; i < sliceValue.Len(); i++ {
// 获取当前元素
item := sliceValue.Index(i)
// 获取当前元素的值
itemValue := reflect.ValueOf(item.Interface())
// 检查当前元素是否为结构体类型
if itemValue.Kind() != reflect.Struct {
return []map[string]interface{}{}
}
// 创建一个空的map,用于存储当前元素的字段名和对应的值
itemMap := make(map[string]interface{})
// 遍历当前元素的所有字段
for j := 0; j < itemValue.NumField(); j++ {
// 获取当前字段的名称
fieldName := itemValue.Type().Field(j).Name
// 获取当前字段的值
fieldValue := itemValue.Field(j).Interface()
// 将字段名和对应的值添加到map中
itemMap[fieldName] = fieldValue
}
// 将当前元素转换为map后,将其添加到结果切片中
result = append(result, itemMap)
}
return result
}
// FindDiffVales 找到两个切片中不同的元素,返回两个切片中不同元素组成的切片
func (s *toolSliceUtil) FindDiffVales(slice1 []interface{}, slice2 []interface{}) []interface{} {
// 创建一个空的切片,用于存储不同的元素
diffVales := make([]interface{}, 0)
// 遍历第一个切片
for _, val1 := range slice1 {
found := false
// 在第二个切片中查找相同值
for _, val2 := range slice2 {
if val1 == val2 {
found = true
break
}
}
// 如果未找到相同值,则将其添加到不同值切片中
if !found {
diffVales = append(diffVales, val1)
}
}
// 遍历第二个切片
for _, val2 := range slice2 {
found := false
// 在第一个切片中查找相同值
for _, val1 := range slice1 {
if val2 == val1 {
found = true
break
}
}
// 如果未找到相同值,则将其添加到不同值切片中
if !found {
diffVales = append(diffVales, val2)
}
}
// 返回两个切片中不同元素组成的切片
return diffVales
}
// FindSameVales 找到两个切片中同同的元素,返回两个切片中相同同元素组成的切片
func (s *toolSliceUtil) FindSameVales(slice1 []interface{}, slice2 []interface{}) []interface{} {
sameVales := make([]interface{}, 0)
// 遍历第一个切片
for _, val1 := range slice1 {
// 在第二个切片中查找相同值
for _, val2 := range slice2 {
if val1 == val2 {
sameVales = append(sameVales, val1)
break
}
}
}
return sameVales
}
// FindOutVales 从slice1里剔除slice2存在的值
func (s *toolSliceUtil) FindOutVales(slice1 []interface{}, slice2 []interface{}) []interface{} {
// 创建一个map,用于存储slice2中出现的所有元素
m := make(map[interface{}]bool)
for _, i := range slice2 {
m[i] = true
}
// 创建一个空的切片,用于存储slice1中不同于slice2的元素
var result []interface{}
for _, i := range slice1 {
if !m[i] {
result = append(result, i)
}
}
// 返回slice2独有的元素组成的切片
return result
}
/*
参数 slice 是一个 []map[string]interface{} 类型的切片,每个 map 中包含一条数据记录。
参数 key 和 name 都是字符串类型,分别表示按照哪个键进行分组和新生成的记录的名称。
返回值是一个 []map[string]interface{} 类型的切片,其中每个 map 表示一组已经分好组的数据记录。
*/ // GroupByKey 将[]map[string]interface{}按某个字段分组
func (s *toolSliceUtil) GroupByKey(slice []map[string]interface{}, key string, name string) []map[string]interface{} {
// 创建一个 map[interface{}][]interface{} 类型的变量 transformedData,用于存储已经分组后的数据。
transformedData := make(map[interface{}][]interface{})
// 遍历输入的每个 map[string]interface{} 类型的数据记录 d,在 transformedData 中找到对应的键 k,将其添加到分组后的列表中。
for _, d := range slice {
k := d[key]
if data, ok := transformedData[k]; ok {
transformedData[k] = append(data, d)
} else {
transformedData[k] = []interface{}{d}
}
}
// 创建一个 []map[string]interface{} 类型的切片 result,遍历 transformedData map 中的所有键值对,
// 并将其转换为新的 map[string]interface{} 类型的数据记录,并添加到 result 切片中。
result := make([]map[string]interface{}, 0, len(transformedData))
for k, v := range transformedData {
m := map[string]interface{}{
key: k,
name: v,
}
result = append(result, m)
}
// 返回 result 切片即可。
return result
}
/*
如果某个元素的 key 对应的不是 int 类型,则返回一个空的 []int 类型的切片。
*/ //MapListKeyToIntSlice 函数从输入的 []map[string]interface{} 类型的切片中提取指定 key 对应的 int 类型数值,并返回一个 []int 类型的切片。
func (s *toolSliceUtil) MapListKeyToIntSlice(list []map[string]interface{}, key string) []int {
// 创建一个 []int 类型的切片 result,用于存储提取出来的 int 数值。
result := make([]int, len(list))
// 遍历输入的每个 map[string]interface{} 类型的数据记录 m,在其中找到 key 对应的值 v,
// 如果 v 是 int 类型,则将其赋值给 result 切片对应的位置;否则,返回一个空的 []int 类型的切片。
for i, m := range list {
if v, ok := m[key]; ok {
if num, ok := v.(int); ok {
result[i] = num
} else {
return []int{}
}
} else {
return []int{}
}
}
// 返回 result 切片即可。
return result
}
/*
参数 iList 是一个可变参数,可以接收任意数量的 []interface{} 类型的切片
返回值是合并并去重后的 []interface{} 类型的切片
*/ //UniqueInterface 函数用于合并多个任意类型的切片并去重
func (s *toolSliceUtil) UniqueInterface(iList ...[]interface{}) []interface{} {
// 创建一个 map[interface{}]bool 类型的变量 m,用于记录已经出现过的元素。
m := make(map[interface{}]bool)
// 遍历输入的每个 []interface{} 类型的切片 ints,对于每个元素 i,将其添加到 m map 中。
for _, ints := range iList {
for _, i := range ints {
m[i] = true
}
}
// 创建一个 []interface{} 类型的切片 result,遍历上一步中去重后的 m map 中的所有键,并将其添加到 result 切片中。
var result []interface{}
for k := range m {
result = append(result, k)
}
// 返回 result 切片即可。
return result
}
// SetNilToDefaultValueByMap 字典中所有键名在 keys 数组中出现过且对应的键值为 nil 的元素改为默认值 defaultValue
func (s *toolSliceUtil) SetNilToDefaultValueByMap(m map[string]interface{}, keys []string, defaultValue interface{}) {
for _, key := range keys {
if val, ok := m[key]; ok && val == nil {
m[key] = defaultValue
}
}
}
// SetNilToDefaultValueByMapV2 处理nil值,出现在keys的nil设置成[], 其余的nil设置成""
func (s *toolSliceUtil) SetNilToDefaultValueByMapV2(m map[string]interface{}, keys []string) {
for key, val := range m {
if val == nil {
if s.Contains(keys, key) {
m[key] = "[]"
} else {
m[key] = ""
}
}
}
}
// SetNilToDefaultValueBySlice 切片中所有键名在 keys 数组中出现过且对应的键值为 nil 的元素改为默认值 defaultValue
func (s *toolSliceUtil) SetNilToDefaultValueBySlice(slice []map[string]interface{}, keys []string, defaultValue interface{}) {
for _, m := range slice {
for _, key := range keys {
if val, ok := m[key]; ok && val == nil {
m[key] = defaultValue
}
}
}
}
// SetNilToDefaultValueBySliceV2 处理nil值,出现在keys的nil设置成[], 其余的nil设置成""
func (s *toolSliceUtil) SetNilToDefaultValueBySliceV2(slice []map[string]interface{}, keys []string) {
for _, m := range slice {
for key, val := range m {
if val == nil {
if s.Contains(keys, key) {
m[key] = []interface{}{}
} else {
m[key] = ""
}
}
}
}
}
/*
isAsc 参数控制是否为升序排列。如果 isAsc 为 true,则按照升序排列;否则,按照降序排列。
sort.SliceStable 可能会导致浮点数字段排序出现问题
*/ // SortByKey 函数按照指定的 key 对应的值,对输入的 []map[string]interface{} 类型的切片进行排序。
func (s *toolSliceUtil) SortByKey(slice []map[string]interface{}, key string, isAsc bool) {
// 调用 sort.SliceStable 函数对 slice 进行排序,并提供一个函数作为比较器。
sort.SliceStable(slice, func(i, j int) bool {
var less bool
switch v := slice[i][key].(type) {
case int:
less = v < slice[j][key].(int)
case int32:
less = v < slice[j][key].(int32)
case int64:
less = v < slice[j][key].(int64)
case float32:
less = v < slice[j][key].(float32)
case float64:
less = v < slice[j][key].(float64)
default:
fmt.Println("unsupported type: $s", reflect.TypeOf(v))
return false
}
if isAsc {
return less
}
return !less
})
}
/*
str: 待解析的字符串
elementType: 切片元素的反射类型
返回值: 解析后的切片
*/ //StringToSlice 将字符串解析为切片类型
func (s *toolSliceUtil) StringToSlice(str string, elementType reflect.Type) interface{} {
sliceType := reflect.SliceOf(elementType)
sliceValue := reflect.New(sliceType).Elem()
err := json.Unmarshal([]byte(str), sliceValue.Addr().Interface())
if err != nil {
fmt.Println(err)
}
return sliceValue.Interface()
}
// StringToSliceInt 将字符串解析为Int切片类型
func (s *toolSliceUtil) StringToSliceInt(str string) []int {
var slice []int
err := json.Unmarshal([]byte(str), &slice)
if err != nil {
fmt.Println(err)
}
return slice
}
// StringToMap 将字符串解析为字典类型
func (s *toolSliceUtil) StringToMap(str string) map[string]interface{} {
if str == "" {
return map[string]interface{}{}
}
var m map[string]interface{}
err := json.Unmarshal([]byte(str), &m)
if err != nil {
fmt.Println(err)
return map[string]interface{}{}
}
return m
}
// StringToMapSlice 将字符串解析为切片类型
func (s *toolSliceUtil) StringToMapSlice(str string) []map[string]interface{} {
if str == "" {
return []map[string]interface{}{}
}
var m []map[string]interface{}
err := json.Unmarshal([]byte(str), &m)
if err != nil {
fmt.Println(err)
return []map[string]interface{}{}
}
return m
}
~~~
- Golang
- 切片 slice
- 数组和切片的区别
- 左闭右开
- make([]int, 5) 和 make([]int, 0, 5) 区别
- 切片非线程安全,并发操作为啥不会像map一样报错
- []struct{} 如何遍历
- 切片如何删除某个元素
- append 一个nil 切片
- 哈希表 map
- 并发操作
- 并发写报错
- 并发读不会报错
- 并发读有写报错
- 并发迭代有写报错
- 自制并发安全字典
- 官方并发安全字典
- 对未初始化的 map 进行赋值操作
- map的底层
- 无序输出
- 等量扩容
- 实现集合
- map的key可以使哪些值
- 协程 go
- 协程相关阅读
- 进程、线程、协程
- 协程 (捕获异常 和 协程池)
- GPM 模型
- CSP模型
- channel
- channel 相关操作
- 交替打印
- 如何让channel 只能接收/只能发送
- channel 常见报错
- channel 死锁
- nil channel 和 已关闭的 channel
- 使用 select 来多路复用 channel
- channel 的使用
- 接口和结构体
- 简单使用
- 两个结构体能否比较
- 工厂模式
- 概念
- 简单工厂
- 方法工厂
- 堆和栈,值类型和引用类型,内存逃逸,垃圾回收
- 栈和堆
- 内存逃逸
- 值类型和引用类型
- 垃圾回收方式
- 性能优化分析工具 pprof
- golang 代码片段
- 片段一 defer
- 片段二 channel
- Golang 相关
- Golang 相关阅读
- Golang 1-10
- make 和 new 的区别
- 使用指针的场景
- Go语言的context包
- 位运算
- Copy 是浅拷贝还是深拷贝
- init 函数 和 sync.Once
- select 多路复用
- Golang 其它
- MongoDB
- 可比较类型 与 可转json 类型
- Gorm
- 面向对象和面向过程
- go语言实现-面向对象
- go语言实现-面向过程
- 限流,熔断,降级
- 了解
- 熔断配置
- 熔断例子
- 服务降级
- github.com/alibaba/sentinel-golang
- 互斥锁 读写锁 原子锁
- 为什么需要锁
- 互斥锁
- 读写锁
- 原子锁
- 互斥锁性能对比
- 原子锁性能对比
- 互斥锁 or 原子锁?
- 条件锁
- 计数器
- GoFrame
- GF1.16版本
- 修改使用的表
- 按天、周、月、年
- GoFrame 文档
- 配置文件
- 生成脚本
- 排序算法
- 相关排序
- 冒泡排序
- 选择排序
- 插入排序
- 快速排序
- 归并排序
- 堆排序
- 数据库
- 分布式怎么保证线程安全
- 数据库实现方式
- 基于表记录
- 乐观锁
- 悲观锁
- Redis实现方式
- Zookeeper实现方式
- Mysql 相关
- group_concat
- 索引优化
- 索引优化1
- 定期分析和优化索引
- 覆盖索引
- 组合索引
- 聚簇索引和非聚簇索引
- 索引类型与方式、聚簇与非聚簇索引
- 事务特征和隔离级别
- 查询优化
- mysql自增表插入数据时,Id不连续问题
- InnoDB引擎 和 MyISAM引擎区别
- 锁
- 悲观锁和乐观锁
- 查询,更新,插入语句
- 什么是死锁
- 怎么处理死锁
- MySQL 隔离级别
- 事务特征
- 隔离级别
- 废弃3
- 索引
- 索引类型和方式、聚簇和非聚簇索引(上)
- 索引类型和方式、聚簇和非聚簇索引(下)
- 回表、覆盖索引、最左前缀、联合索引、索引下推、索引合并
- Mysql 优化
- 索引的原理
- 千万级表修改表结构
- Redis
- 获取随机三条数据
- Redis 持久化方式
- 全量模式 RDB 冷备份(内存快照)
- 增量模式 AOF 热备份(文件追加)
- 过期key的删除策略、内存淘汰机制
- 数据结构
- 位图
- 网络
- 网络相关
- 游戏同步方式:帧同步和状态同步
- Websocket
- OSI模型
- TCP 与 UDP
- 三次握手四次挥手
- Http 状态码
- 1xx(信息性状态码)
- 101 服务端代码
- 101 客户端代码
- 2xx(成功状态码)
- 3xx(重定向状态码)
- 302 服务端代码
- 302 客户端代码
- 4xx(客户端错误状态码)
- 5xx(服务器错误状态码)
- 如何排查接口问题
- 网络请求和响应过程
- time_wait
- keep-alive
- http 和 rpc 的区别
- I/O多路复用 select和poll
- too many open file
- 其它技术
- git 相关操作
- 修改提交备注
- 多个提交合并成一个提交
- 回退版本
- 小程序和公众号
- 消息模板
- 获取code
- 静默登录
- 其它技术相关
- C盘空间不足
- 生成式人工智能AIGC
- 共享文件
- 接口文档, mock提供测试数据
- 抓包工具
- Python
- 安装包失败
- 自动化测试 Scrapy
- AIGC:人工智能生成内容
- PHP
- xhprof 性能分析
- 一键安装
- 哈希冲突的解决方式
- 链地址法(拉链法)
- 开放地址法
- 再哈希
- 概念1
- Nginx
- 负载均衡方式
- 加密解密
- 简单了解
- 签名算法例子
- 码例子1
- 代码例子2
- Linux
- netstat (用于查看和管理网络连接和路由表)
- ps 用于查看和管理进程
- ab 压测
- nohup 守护进程
- lsof (List Open File 获取被进程打开文件的信息)
- tail 查看日志
- 各类linux同步机制
- Socket 服务端的实现,select 和epoll的区别?
- scp 传输,awk 是一个强大的文本分析工具
- pidof
- 项目
- 棋牌
- 牌的编码
- 出牌规则
- 洗牌
- 股票
- 股票知识
- 龙虎榜数据缓存方式
- 单日龙虎榜数据
- 单只股票的历史上榜
- 遇到的问题
- 浮点数精度问题
- Mysql Sum 精度问题(float, double精度问题)
- 分页问题(数据重复)
- 工具包
- v3
- common.go
- common_test.go
- customized.go
- customized_test.go
- slice.go
- slice_test.go
- time.go
- time_test.go
- v4
- common.go
- common_test.go
- customized.go
- customized_test.go
- slice.go
- time.go
- time_test.go
- 相关阅读
- 集合 map
- 协程 goroutine
- 通道 channel
- json 和 gob 序列化和反序列化
- redis 有序集合
- 相关阅读 s
- pyTorch
- defer
- 内存泄漏
- 数据传输
- 杂项
- 一提