Sorting on a shared nested struct property in Go
我已经从重新思考数据库中提取了json数据集,然后使用rethinkgo将数据序列化为结构。我需要能够处理此数据的子集,并根据其属性之一的值对其进行排序。
为了避免使上述问题变得更加复杂,我创建了一个简化的(基于水果的)示例,该示例说明了所使用的结构以及我要实现的结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 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
如您所见,我有两个结构,Apples和Bananas,它们都共享Fruit结构中的属性。排序(包括接口函数Len,Swap,Less)和主要函数,它们设置了苹果和香蕉的数据结构,然后尝试对它们进行排序。
我要苹果和香蕉都需要的一种类型(类型ByNumSeeds,Len,Swap,Less)能够在Apple和Bananas都从Fruit结构AvgNumSeeds共享的属性上分别对苹果和香蕉进行排序。
我在这段代码中创建的排序将一片苹果作为其接口,并且确实通过AvgNumSeeds对我的一系列苹果进行了排序。但是,我找不到找到使其与Apple和Banana结构一起使用的方法。
我最初的想法是将接口视为"水果"切片,但是可以理解的是,我得到了错误:
1 | 60: cannot convert apples (type []Apple) to type ByNumSeeds |
我的下一个想法是通过以某种方式将一小块苹果/香蕉浇铸成一小块水果来解决此错误,但这并不是一件正确的事情。
在寻找解决方案的过程中,我遇到了一个名为sortutil的软件包,该软件包具有一个称为AscByField的函数,该函数采用结构和字段名称作为排序依据。我还没有尝试过,但是该软件包很清楚地表明它效率不高,因为它通过使用反射并首先尝试使用标准接口方法来工作。
有没有一种方法可以实现嵌套结构的排序,而不必为每个"子"结构类型重复排序?
多态的解决方案是接口。 如您所见,单独嵌入在这里实际上并不起作用,因为您仍然具有不同的类型。 这是对示例的重做,以帮助您开始使用http://play.golang.org/p/7HV_HJ3Gw0,或者这可能更易于阅读(通常将未导出的结构隐藏在导出的界面后面) )http://play.golang.org/p/z3CHj002Jq
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | 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) } |
但是,从您的示例中我还是很警惕,您试图使合成像继承一样工作(但这可能只是来自简化的示例)。 嵌入不会像继承那样为您提供"是"关系,而只会给您"具有"。 让您的类型提供一个通用接口,使您可以通过相同的排序功能运行所有兼容类型。
在您的示例中唯一真正的陷阱是