在Go中排序结构片段的一种灵活而优雅的方法

时间:2018-09-19 18:15:15

标签: go

让我们说我们有一个相当复杂的struct,其中包含许多字段,我需要根据不同的标准在几个地方进行排序,例如

type MySuperType struct {
    x0, x1, x2, x3 xType
    // possibly even more fields
}
// sort 1: ascending x0, then descending x1, then more stuff
// sort 2: if x4==0 then applyCriteria2a else applyCriteria2b

func f1(mySuperSlice []MySuperType) {
    // sort 'myList' according sort #1
    // do something with the sorted list
}
func f2(mySuperSlice []MySuperType) {
    // sort 'myList' according sort #2
    // do something with the sorted list
}
func f3(mySuperSlice []MySuperType) {
    // sort 'myList' according sort #1, note: we use sort #1 again!
    // do something with the sorted list
}

建议的解决方案1:
创建一个新类型([]MySuperType的别名),该类型为所需的每个排序标准实现sort.Interface
问题: (i)有一些重复的代码,因为函数LenSwap将完全相同 (ii)将会出现一堆新类型,这些新类型对程序的整体可读性没有帮助---这些新类型并不能真正代表任何内容,唯一重要的是{{ 1}}功能。

建议的解决方案2:
使用Less
这将是一个完美的解决方案(请参见this answer),但是据我了解,排序函数必须内联指定(当我尝试在其他地方定义该函数时会收到错误sort.Slice),这意味着我需要为invalid receiver type []T ([]T is an unnamed type)定义别名,我们回到解决方案1)。
现在,内联定义函数的问题是:(i)给定[]T的复杂性,该函数可能会很长,并且(ii)函数将在多个位置重复(例如,在{{1 }}和MySuperType(在我上面的示例中)-更烦人的是,在解决方案1中,排序功能可能又长又复杂。 注意:如果我们实际上没有(ii),那么(i)就不会有太大问题了。

问题:
根据我目前对Go的理解和知识,我将使用解决方案1。
但是,有谁知道可以巧妙地解决此问题的其他方法或改善上述缺点的建议呢?

3 个答案:

答案 0 :(得分:3)

为每种顺序写一个排序函数,并根据需要从f1,f2和f3调用:

func sortByX0AscX1Desc(s []MySuperType) {
    sort.Slice(s, func(i, j int) bool {
        switch {
        case s[i].x0 < s[j].x0:
            return true
        case s[i].x0 > s[j].x0:
            return false
        case s[i].x1 > s[j].x1:
            return true
        default:
            return false
        }
    })
}

func f1(mySuperSlice []MySuperType) {
    sortByX0AscX1Desc(mySuperSlice)
    // do something with the sorted list
}
func f2(mySuperSlice []MySuperType) {
    sortBySomethingElse(mySuperSlice)
    // do something with the sorted list
}
func f3(mySuperSlice []MySuperType) {
    sortByX0AscX1Desc(mySuperSlice)
    // do something with the sorted list
}

答案 1 :(得分:2)

@ThunderCat的解决方案将起作用。另一种选择是编写在切片上返回闭包且与less的{​​{1}}参数签名匹配的函数:

sort.Slice

然后将其作为func ascX0DescX1(s []MySuperType) (func(int, int) bool) { return func(i, j int) bool { if s[i].x0 == s[j].x0 { return s[i].x1 > s[j].x1 } return s[i].x0 < s[j].x0 } } 的参数传递给less

sort.Slice

答案 2 :(得分:0)

您还可以省略额外的功能,并在需要时调用sort。

type MySuperType struct {
    x0, x1, x2, x3 string
}

func f1() {
    fields := []MySuperType {
        { "a1", "b4", "c3", "d2" },
        { "a2", "b1", "c2", "d3" },
        { "a3", "b1", "c4", "d1" },
        { "a4", "b3", "c1", "d4" },
    }
    sort.SliceStable(fields, func(i, j int) bool {
        return fields[i].x1 < fields[j].x1 || fields[i].x2 > fields[j].x2
    })
    fmt.Println("by x1, then x2: ", fields)
}

结果: 按x1,然后按x2:[{a3 b1 c4 d1} {a2 b1 c2 d3} {a4 b3 c1 d4} {a1 b4 c3 d2}]