~~~
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
- 相關閱讀
- 協程 goroutine
- 通道 channel
- json 和 gob 序列化和反序列化
- redis 有序集合
- mysql22
- 相關閱讀 s
- pyTorch
- defer
- 內存泄漏
- 數據傳輸
- 雜項
- 一提
- gogogoo
- 內容