简体   繁体   English

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

[英]Sorting on a shared nested struct property in Go

I have set of json data being pulled from a rethink database, the data is then serialised into structs using rethinkgo. 我已经从重新思考数据库中提取了json数据集,然后使用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\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 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; 如您所见,我有两个结构,Apples和Bananas,它们都共享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. 排序(包括接口函数Len,Swap,Less)和主要函数,它们设置了苹果和香蕉的数据结构,然后尝试对它们进行排序。

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. 我要苹果和香蕉都需要的一种类型(类型ByNumSeeds,Len,Swap,Less)能够在Apple和Bananas都从Fruit结构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. 我在这段代码中创建的排序将一片苹果作为其接口,并且确实通过AvgNumSeeds对我的一系列苹果进行了排序。 However I am unable to find a way to get it to work with both Apple and Banana structs. 但是,我找不到找到使其与Apple和Banana结构一起使用的方法。

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. 在寻找解决方案的过程中,我遇到了一个名为sortutil的软件包,该软件包具有一个称为AscByField的函数,该函数采用结构和字段名称作为排序依据。 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? 有没有一种方法可以实现嵌套结构的排序,而不必为每个“子”结构类型重复排序?

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 这是对示例的重做,以帮助您开始使用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)
}

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 . 在您的示例中,唯一真正的陷阱将是[]struct[]interface不可互换。 If you need to convert the two, you have to create a new slice and copy the values. 如果需要转换两者,则必须创建一个新的切片并复制值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM