【问题标题】:Sorting on a shared nested struct property in Go在 Go 中对共享的嵌套结构属性进行排序
【发布时间】:2014-04-09 08:14:02
【问题描述】:

我从 rethink 数据库中提取了一组 json 数据,然后使用 rethinkgo 将数据序列化为结构。我需要能够处理这些数据的一个子集,并根据其中一个属性的值对其进行排序。

为了避免上述问题的复杂性,我创建了一个简化的(基于水果的)示例,说明正在使用的结构以及我想要实现的目标。

package main

import (
    "fmt"
    "sort"
)

type Fruit struct {
    AvgNumSeeds int
    Name        string
}

type Apple struct {
    Fruit
    Diameter int
}

type Banana struct {
    Fruit
    Length int
}

type ByNumSeeds []Apple //[]Fruit

func (p ByNumSeeds) Len() int {
    return len(p)
}

func (p ByNumSeeds) Swap(i, j int) {
    p[i], p[j] = p[j], p[i]
}

func (p ByNumSeeds) Less(i, j int) bool {
    return p[i].AvgNumSeeds < p[j].AvgNumSeeds
}

func main() {
    apples := []Apple{
        Apple{Fruit: Fruit{AvgNumSeeds: 4, Name: "Cox"}, Diameter: 10},
        Apple{Fruit: Fruit{AvgNumSeeds: 6, Name: "Granny Smith"}, Diameter: 20},
        Apple{Fruit: Fruit{AvgNumSeeds: 5, Name: "Pink Lady"}, Diameter: 21},
        Apple{Fruit: Fruit{AvgNumSeeds: 2, Name: "Russett"}, Diameter: 15},
        Apple{Fruit: Fruit{AvgNumSeeds: 1, Name: "Crab"}, Diameter: 7},
        Apple{Fruit: Fruit{AvgNumSeeds: 7, Name: "Brambley"}, Diameter: 40},
        Apple{Fruit: Fruit{AvgNumSeeds: 3, Name: "Braeburn"}, Diameter: 25},
    }

    bananas := []Banana{
        Banana{Fruit: Fruit{AvgNumSeeds: 40, Name: "Lacatan"}, Length: 20},
        Banana{Fruit: Fruit{AvgNumSeeds: 60, Name: "Lady Finger"}, Length: 22},
        Banana{Fruit: Fruit{AvgNumSeeds: 50, Name: "Senorita"}, Length: 25},
        Banana{Fruit: Fruit{AvgNumSeeds: 20, Name: "Cavendish"}, Length: 30},
        Banana{Fruit: Fruit{AvgNumSeeds: 10, Name: "Goldfinger"}, Length: 27},
        Banana{Fruit: Fruit{AvgNumSeeds: 70, Name: "Gros Michel"}, Length: 15},
        Banana{Fruit: Fruit{AvgNumSeeds: 30, Name: "Red Dacca"}, Length: 19},
    }

    fmt.Println("Apples")
    fmt.Printf("%+v\n\n", apples)
    sort.Sort(ByNumSeeds(apples))
    fmt.Printf("%+v\n\n\n", apples)

    fmt.Println("Bananas")
    fmt.Printf("%+v\n\n", bananas)
    //sort.Sort(ByNumSeeds(bananas))
    fmt.Printf("%+v\n\n", bananas)
}

http://play.golang.org/p/EjWOf58N3x

如您所见,我有两个结构,Apples 和 Bananas,它们都共享结构 Fruit 的属性;排序(包括接口函数 Len、Swap、Less)和 main 函数,它为苹果和香蕉设置数据结构,然后尝试对它们进行排序。

我对 Apples 和 Bananas 都想要的是一种(类型 ByNumSeeds、Len、Swap、Less),它能够在 Apple 和 Bananas 都从 Fruit 结构 AvgNumSeeds 共享的属性上分别排序。

我在这段代码中创建的排序将苹果切片作为其接口,并且确实按 AvgNumSeeds 对我的苹果数组进行排序。但是我无法找到一种方法让它与 Apple 和 Banana 结构一起使用。

我最初的想法是将界面视为一片水果,但可以理解的是,我得到了错误:

60: cannot convert apples (type []Apple) to type ByNumSeeds

我的下一个想法是通过某种方式将一片苹果/香蕉投射到一片水果上来解决这个错误,但这并不完全是正确的做法。

在我对解决方案的调查中,我遇到了一个名为 sortutil 的包,它有一个名为 AscByField 的函数,该函数采用结构和字段名称进行排序。我还没有尝试过,但是这个包很清楚地表明它效率不高,因为它通过使用反射来工作,并首先尝试使用标准接口方法。

有没有一种方法可以实现嵌套结构的排序,而不必为每个“子”结构类型重复排序?

【问题讨论】:

    标签: sorting struct go nested


    【解决方案1】:

    解决多态的方法是接口。如您所见,单独嵌入在这里并不真正起作用,因为您仍然有不同的类型。这是您的示例的重新工作,以帮助您入门http://play.golang.org/p/7HV_HJ3Gw0,或者这可能更容易阅读(通常将未导出的结构隐藏在导出的接口后面)http://play.golang.org/p/z3CHj002Jq

    package main
    
    import (
        "fmt"
        "sort"
    )
    
    type fruit struct {
        avgNumSeeds int
        name        string
    }
    
    type Fruit interface {
        Name() string
        AvgNumSeeds() int
    }
    
    func (f fruit) Name() string {
        return f.name
    }
    
    func (f fruit) AvgNumSeeds() int {
        return f.avgNumSeeds
    }
    
    type Apple struct {
        fruit
        Diameter int
    }
    
    type Banana struct {
        fruit
        Length int
    }
    
    type ByNumSeeds []Fruit
    
    func (p ByNumSeeds) Len() int {
        return len(p)
    }
    
    func (p ByNumSeeds) Swap(i, j int) {
        p[i], p[j] = p[j], p[i]
    }
    
    func (p ByNumSeeds) Less(i, j int) bool {
        return p[i].AvgNumSeeds() < p[j].AvgNumSeeds()
    }
    
    func main() {
        apples := []Fruit{
            Apple{fruit: fruit{avgNumSeeds: 4, name: "Cox"}, Diameter: 10},
            Apple{fruit: fruit{avgNumSeeds: 6, name: "Granny Smith"}, Diameter: 20},
            Apple{fruit: fruit{avgNumSeeds: 5, name: "Pink Lady"}, Diameter: 21},
            Apple{fruit: fruit{avgNumSeeds: 2, name: "Russett"}, Diameter: 15},
            Apple{fruit: fruit{avgNumSeeds: 1, name: "Crab"}, Diameter: 7},
            Apple{fruit: fruit{avgNumSeeds: 7, name: "Brambley"}, Diameter: 40},
            Apple{fruit: fruit{avgNumSeeds: 3, name: "Braeburn"}, Diameter: 25},
        }
    
        bananas := []Fruit{
            Banana{fruit: fruit{avgNumSeeds: 40, name: "Lacatan"}, Length: 20},
            Banana{fruit: fruit{avgNumSeeds: 60, name: "Lady Finger"}, Length: 22},
            Banana{fruit: fruit{avgNumSeeds: 50, name: "Senorita"}, Length: 25},
            Banana{fruit: fruit{avgNumSeeds: 20, name: "Cavendish"}, Length: 30},
            Banana{fruit: fruit{avgNumSeeds: 10, name: "Goldfinger"}, Length: 27},
            Banana{fruit: fruit{avgNumSeeds: 70, name: "Gros Michel"}, Length: 15},
            Banana{fruit: fruit{avgNumSeeds: 30, name: "Red Dacca"}, Length: 19},
        }
    
        fmt.Println("Apples")
        fmt.Printf("%+v\n\n", apples)
        sort.Sort(ByNumSeeds(apples))
        fmt.Printf("%+v\n\n\n", apples)
    
        fmt.Println("Bananas")
        fmt.Printf("%+v\n\n", bananas)
        sort.Sort(ByNumSeeds(bananas))
        fmt.Printf("%+v\n\n", bananas)
    }
    

    我对您的示例持谨慎态度,您试图强制组合像继承一样工作(但这可能只是来自简化示例)。嵌入不会像继承那样给你一个“是”的关系,只有一个“有”。让你的类型提供一个通用接口允许你通过相同的排序函数运行所有兼容的类型。

    您的示例中唯一真正的问题是[]struct 不能与[]interface 互换。如果需要转换两者,则必须创建一个新切片并复制值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-04
      • 2015-04-17
      • 1970-01-01
      • 1970-01-01
      • 2018-07-02
      • 1970-01-01
      相关资源
      最近更新 更多