Go 切片转集合(Slice to Set)
Posted 恋喵大鲤鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go 切片转集合(Slice to Set)相关的知识,希望对你有一定的参考价值。
文章目录
1.Golang 的 Set 类型是什么
我们都知道 Golang 没有集合(Set)类型,为何如此设计呢?
因为 Golang 是一门追求简单、现代、易于使用的语言,所以不会引入不必要的特性。
Set 就是一个例子,Golang 有了 Map,当 Map 中的 value 为空结构体 struct 时,其不就是一个集合(Set)么,所以不用再单独引入一个 Set 类型。
2.切片转集合(Slice to Set)
有了集合,在某些场景下,我们可能需要完成切片到集合类型的转换。
Golang 中,利用反射,我们可以将任意类型的切片或数组转换为对应类型的集合。
// toSetE converts a slice or array to map[any]struct and returns an error if occurred.
func toSetE(i any) (any, error)
// Check params.
if i == nil
return nil, fmt.Errorf("the input i is nil")
t := reflect.TypeOf(i)
kind := t.Kind()
if kind != reflect.Slice && kind != reflect.Array
return nil, fmt.Errorf("the input %#v of type %T isn't a slice or array", i, i)
// Execute the conversion.
v := reflect.ValueOf(i)
mT := reflect.MapOf(t.Elem(), reflect.TypeOf(struct))
mV := reflect.MakeMapWithSize(mT, v.Len())
for j := 0; j < v.Len(); j++
mV.SetMapIndex(v.Index(j), reflect.ValueOf(struct))
return mV.Interface(), nil
在上面函数的基础上,我们可以封装我们想要的不同类型的转换函数。
转换成 int 集合。
// ToIntSet converts a slice or array to map[int]struct.
func ToIntSet(i any) map[int]struct
m, _ := ToIntSetE(i)
return m
// ToIntSetE converts a slice or array to map[int]structSet with error.
func ToIntSetE(i any) (map[int]struct, error)
m, err := toSetE(i)
if err != nil
return nil, err
if v, ok := m.(map[int]struct); ok
return v, nil
dst := make(map[int]struct, reflect.ValueOf(m).Len())
for _, k := range reflect.ValueOf(m).MapKeys()
v, err := cast.ToIntE(k.Interface())
if err != nil
return nil, err
dst[v] = struct
return dst, nil
转换成 float64 集合。
// ToFloat64Set converts a slice or array to map[float64]struct.
func ToFloat64Set(i any) map[float64]struct
m, _ := ToFloat64SetE(i)
return m
// ToFloat64SetE converts a slice or array to map[float64]struct and returns an error if occurred.
func ToFloat64SetE(i any) (map[float64]struct, error)
m, err := toSetE(i)
if err != nil
return nil, err
if v, ok := m.(map[float64]struct); ok
return v, nil
dst := make(map[float64]struct, reflect.ValueOf(m).Len())
for _, k := range reflect.ValueOf(m).MapKeys()
v, err := cast.ToFloat64E(k.Interface())
if err != nil
return nil, err
dst[v] = struct
return dst, nil
转换成 string 集合。
// ToStrSet converts a slice or array to map[string]struct.
func ToStrSet(i any) map[string]struct
m, _ := ToStrSetE(i)
return m
// ToStrSetE converts a slice or array to map[string]struct and returns an error if occurred.
func ToStrSetE(i any) (map[string]struct, error)
m, err := toSetE(i)
if err != nil
return nil, err
if v, ok := m.(map[string]struct); ok
return v, nil
dst := make(map[string]struct, reflect.ValueOf(m).Len())
for _, k := range reflect.ValueOf(m).MapKeys()
v, err := cast.ToStringE(k.Interface())
if err != nil
return nil, err
dst[v] = struct
return dst, nil
转换示例:
package main
import (
"fmt"
"reflect"
"github.com/spf13/cast"
)
func main()
intSet, err := ToIntSetE([]int1, 2, 3)
fmt.Println(intSet, err)
f64Set, err := ToFloat64SetE([]float641.1, 2.2, 3.3)
fmt.Println(f64Set, err)
strSet, err := ToStrSetE([]string"foo", "bar", "baz")
fmt.Println(strSet, err)
运行输出:
map[1: 2: 3:] <nil>
map[1.1: 2.2: 3.3:] <nil>
map[bar: baz: foo:] <nil>
3.泛型
Golang 在 1.18 中引入了千呼万唤的泛型,利用泛型,我们可以不用针对具体类型单独封装,少写上面很多重复的代码。
// ToSet converts a slice or array to map[T]struct and returns a nil if error occurred.
func ToSet[T comparable](i any) map[T]struct
m, _ := ToSetE[T](i)
return m
// ToSetE converts a slice or array to map[T]struct and returns an error if occurred.
// Note that the the element type of input don't need to be equal to the map key type.
// For example, []uint641, 2, 3 can be converted to map[uint64]struct1:struct, 2:struct,3:struct
// and also can be converted to map[string]struct"1":struct, "2":struct, "3":struct
// if you want.
// Note that this function is implemented through 1.18 generics, so the element type needs to
// be specified when calling it, e.g. ToSetE[int]([]int1,2,3).
func ToSetE[T comparable](i any) (map[T]struct, error)
// Check params.
if i == nil
return nil, fmt.Errorf("the input i is nil")
t := reflect.TypeOf(i)
kind := t.Kind()
if kind != reflect.Slice && kind != reflect.Array
return nil, fmt.Errorf("the type %T of input %#v isn't a slice or array", i, i)
// Execute the conversion.
v := reflect.ValueOf(i)
mapT := reflect.MapOf(t.Elem(), reflect.TypeOf(struct))
mapV := reflect.MakeMapWithSize(mapT, v.Len())
for j := 0; j < v.Len(); j++
mapV.SetMapIndex(v.Index(j), reflect.ValueOf(struct))
if v, ok := mapV.Interface().(map[T]struct); ok
return v, nil
// Convert the element to the T.
set := make(map[T]struct, v.Len())
for _, k := range mapV.MapKeys()
v, err := ToAnyE[T](k.Interface())
if err != nil
return nil, err
set[v] = struct
return set, nil
注意: 如果目标集合的类型和切片或数组元素类型不一致,会尝试进行转换。转换用到的函数如下:
// ToAnyE converts one type to another and returns an error if occurred.
func ToAnyE[T any](i any) (T, error)
var t T
switch any(t).(type)
case bool:
v, err := cast.ToBoolE(i)
if err != nil
return t, err
t = any(v).(T)
case int:
v, err := cast.ToIntE(i)
if err != nil
return t, err
t = any(v).(T)
case int8:
v, err := cast.ToInt8E(i)
if err != nil
return t, err
t = any(v).(T)
case int16:
v, err := cast.ToInt16E(i)
if err != nil
return t, err
t = any(v).(T)
case int32:
v, err := cast.ToInt32E(i)
if err != nil
return t, err
t = any(v).(T)
case int64:
v, err := cast.ToInt64E(i)
if err != nil
return t, err
t = any(v).(T)
case uint:
v, err := cast.ToUintE(i)
if err != nil
return t, err
t = any(v).(T)
case uint8:
v, err := cast.ToUint8E(i)
if err != nil
return t, err
t = any(v).(T)
case uint16:
v, err := cast.ToUint16E(i)
if err != nil
return t, err
t = any(v).(T)
case uint32:
v, err := cast.ToUint32E(i)
if err != nil
return t, err
t = any(v).(T)
case uint64:
v, err := cast.ToUint64E(i)
if err != nil
return t, err
t = any(v).(T)
case float32:
v, err := cast.ToFloat32E(i)
if err != nil
return t, err
t = any(v).(T)
case float64:
v, err := cast.ToFloat64E(i)
if err != nil
return t, err
t = any(v).(T)
case string:
v, err := cast.ToStringE(i)
if err != nil
return t, err
t = any(v).(T)
default:
return t, fmt.Errorf("the type %T is not supported", t)
return t, nil
转换示例:
func main()
intSet, err := ToSetE[int]([]int1, 2, 3)
fmt.Println(intSet, err)
f64Set, err := ToSetE[float64]([]float641.1, 2.2, 3.3)
fmt.Println(f64Set, err)
strSet, err := ToSetE[string]([]string"foo", "bar", "baz")
fmt.Println(strSet, err)
运行输出:
map[1: 2: 3:] <nil>
map[1: 2: 3:] <nil>
map[bar: baz: foo:] <nil>
4.go-huge-util
为了方便大家使用,以上相关代码已开源至 Github 工具库 go-huge-util,大家可使用 go mod 方式 import 然后使用。
import (
"github.com/dablelv/go-huge-util/conv"
)
// Convert bool slice or array to set.
bools := []booltrue, false, true
set := conv.ToBoolSet(bools)
set, _ := conv.ToBoolSetE(bools)
set := conv.ToSet[bool](bools)
set, _ := conv.ToSetE[bool](bools)
// Convert int slice or array to set.
ints := []int1, 2, 3
set := conv.ToIntSet(ints)
set, _ := conv.ToIntSetE(ints)
set := conv.ToSetG[int](ints)
set, _ := conv.ToSetE[int](ints)
// Convert string slice or array to set.
strs := []string"foo", "bar", "baz"
set := conv.ToStrSet(strs)
set, _ := conv.ToStrSetE(strs)
set := conv.ToSet[string](strs)
set, _ := conv.ToSetE[string](strs)
// Convert int8, int16, int32, uint etc. slice or array to set.
// ...
参考文献
github.com/dablelv/go-huge-util
以上是关于Go 切片转集合(Slice to Set)的主要内容,如果未能解决你的问题,请参考以下文章
吴裕雄--天生自然--Go 语言学习笔记--Go 语言切片(Slice)
GO语言切片Slice数组Array的比较;利用多维切片接受EXCEL内容的输出
GO语言学习系列六——GO的数组 array与切片 slice