如何对具有共同字段的一片空接口进行排序?

How to Sort a slice of empty Interface that have a field in common?

我有多个结构:

type FooStruct struct {
  ID int
  Field1 int
  CommonID int
}

type BarStruct struct {
  ID int
  Field2 int
  CommonID int
}

type FighterStruct struct {
  ID int
  Field3 int
  CommonID int
}

1- 它们都保存在不同的切片中:sliceOfFooStruct、sliceOfBarStruct、sliceofFighterStruct

2- 我遍历每个切片并将它们未排序地插入到公共 var commonSlice []interface{} 切片

3- 然后我需要按 CommonID​​ 将它们分类到那个切片中,这就是我有点卡住的地方。

我正在尝试:

sort.Slice(commonSlice, func(i, j int) bool { return commonSlice[i].CommonID > commonSlice[j].CommonID })

但我遇到了错误

commonSlice[i].CommonID undefined (type interface {} is interface with no methods)

我也尝试过使用 commonSlice[i].CommonID​​.(int) 转换类型,但它也不起作用。

也尝试过类似 的匿名结构和 CommonID​​ 字段,但它没有用。

如果我直接转换被比较的实际结构的类型,但硬编码类型将破坏“commonSlice”的全部目的,我认为它可以工作。

这是怎么做到的?我应该做不同的事情吗?

由于您的 commonSlice 的元素类型是 interface{} 您无法访问值的任何字段,因为它允许存储任何值,甚至不是结构的值。

一种令人沮丧的方法是使用反射来访问 CommonID 字段,但这既丑陋又缓慢。作为参考,下面是它的样子:

all := []interface{}{
    FooStruct{11, 22, 31},
    BarStruct{21, 32, 33},
    FighterStruct{21, 32, 32},
}

sort.Slice(all, func(i, j int) bool {
    commonID1 := reflect.ValueOf(all[i]).FieldByName("CommonID").Int()
    commonID2 := reflect.ValueOf(all[j]).FieldByName("CommonID").Int()
    return commonID1 > commonID2
})

fmt.Println(all)

此输出(在 Go Playground 上尝试):

[{21 32 33} {21 32 32} {11 22 31}]

而是创建一个接口来描述访问 CommonID 字段的方法:

type HasCommonID interface {
    GetCommonID() int
}

并使您的 struts 实现此接口:

func (f FooStruct) GetCommonID() int     { return f.CommonID }
func (b BarStruct) GetCommonID() int     { return b.CommonID }
func (f FighterStruct) GetCommonID() int { return f.CommonID }

并将您的值存储在此界面的一部分中:

all := []HasCommonID{
    FooStruct{11, 22, 31},
    BarStruct{21, 32, 33},
    FighterStruct{21, 32, 32},
}

然后你可以使用GetCommonID()方法在less()函数中访问它:

sort.Slice(all, func(i, j int) bool {
    return all[i].GetCommonID() > all[j].GetCommonID()
})

这将输出相同的结果,请在 Go Playground.

上尝试

这更干净、更快、可扩展。

为避免重复,您可以将公共字段和方法“外包”到一个结构中,并将其嵌入到您所有的 struts:

type Common struct {
    CommonID int
}

func (c Common) GetCommonID() int { return c.CommonID }

type FooStruct struct {
    ID     int
    Field1 int
    Common
}

type BarStruct struct {
    ID     int
    Field2 int
    Common
}

type FighterStruct struct {
    ID     int
    Field3 int
    Common
}

注意:GetCommonID()只定义一次,对于Common,其他类型不需要添加。然后使用它:

all := []HasCommonID{
    FooStruct{11, 22, Common{31}},
    BarStruct{21, 32, Common{33}},
    FighterStruct{21, 32, Common{32}},
}

sort.Slice(all, func(i, j int) bool {
    return all[i].GetCommonID() > all[j].GetCommonID()
})

输出是一样的,在Go Playground.

上试试

这还允许您在一个地方 (Common) 扩展公共字段和方法列表,而无需进一步重复。