doume5227 2014-03-06 15:26
浏览 68
已采纳

在Go中对共享的嵌套struct属性进行排序

I have set of json data being pulled from a rethink database, the data is then serialised into structs using rethinkgo. I need to be able to work with a subset of this data and sort it based on the value of one of its properties.

To save complicating the problem with the workings of what I mentioned above, I've created a simplified (fruit based) example of the structs being used and what I'm trying to achieve.

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

", apples)
    sort.Sort(ByNumSeeds(apples))
    fmt.Printf("%+v


", apples)

    fmt.Println("Bananas")
    fmt.Printf("%+v

", bananas)
    //sort.Sort(ByNumSeeds(bananas))
    fmt.Printf("%+v

", bananas)
}

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

As you can see I've two structs, Apples and Bananas, both of which share properties from the struct Fruit; a sort (inc. interface functions Len, Swap, Less) and the main function which sets up the data structures for apples and bananas and then tries to sort them.

What I am wanting for both Apples and Bananas is one sort (type ByNumSeeds, Len, Swap, Less) that is capable of sorting both Apples and Bananas separately, on a property that they both share from the Fruit struct, AvgNumSeeds.

The sort I have created in this code takes a slice of Apples as its interface and does indeed sort my array of apples by the AvgNumSeeds. However I am unable to find a way to get it to work with both Apple and Banana structs.

My initial thought was to treat the interface as a slice of Fruit but, understandably, I then get the error:

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

My next thought was to fix this error by somehow casting a slice of Apples/Bananas to a slice of Fruit but this doesn't quite feel like the right thing to do.

In my investigation for a solution I've come across a package called sortutil which has a function called AscByField that takes the struct and the name of the field to sort by. I've not tried it yet but the package makes it quite clear that its not efficient as it works by using reflection and to try to use the standard interface method first.

Is there a way I can achieve the sorting of a nested struct without having to duplicate a sort for each 'child' struct type?

  • 写回答

1条回答 默认 最新

  • dongyan7172 2014-03-06 16:01
    关注

    The solution to polymorphism is go is interfaces. Embedding on its own doesn't really work here as you see, because you still have distinct types. Here's a re-work of your example to get you started http://play.golang.org/p/7HV_HJ3Gw0, or maybe this is a little easier to read through (it's common to hide an un-exported struct behind the exported interface) 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
    
    ", apples)
        sort.Sort(ByNumSeeds(apples))
        fmt.Printf("%+v
    
    
    ", apples)
    
        fmt.Println("Bananas")
        fmt.Printf("%+v
    
    ", bananas)
        sort.Sort(ByNumSeeds(bananas))
        fmt.Printf("%+v
    
    ", bananas)
    }
    

    I am wary from your example though, that your trying to force composition to work like inheritance (but that could just be from the simplified example). Embedding doesn't give you an "is a" relationship like inheritance, only a "has a". Having your types provide a common interface allows you to run all compliant types through the same sort function.

    The only real gotcha in your example is going to be that []struct is not interchangeable with []interface. If you need to convert the two, you have to create a new slice and copy the values.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 计组这些题应该咋做呀
  • ¥60 更换迈创SOL6M4AE卡的时候,驱动要重新装才能使用,怎么解决?
  • ¥15 让node服务器有自动加载文件的功能
  • ¥15 jmeter脚本回放有的是对的有的是错的
  • ¥15 r语言蛋白组学相关问题
  • ¥15 Python时间序列如何拟合疏系数模型
  • ¥15 求学软件的前人们指明方向🥺
  • ¥50 如何增强飞上天的树莓派的热点信号强度,以使得笔记本可以在地面实现远程桌面连接
  • ¥20 双层网络上信息-疾病传播
  • ¥50 paddlepaddle pinn