写惯了C#的linq,对集合进行各种操作,很丝滑,突然写go的时候,操作slice不知所措。
于是在github上找到了这个库:https://github.com/ahmetb/go-linq,又回到了丝滑的感觉。
但是在按照时间排序的时候,报错了。
先上代码:
package main
import (
"fmt"
"time"
"github.com/ahmetb/go-linq/v3"
)
func main() {
list := make([]Person, 0)
list = append(list,
Person{
Name: "tony1",
Age: 1,
Born: time.Date(2000, 1, 1, 8, 0, 0, 0, time.Local),
}, Person{
Name: "tony2",
Age: 1,
Born: time.Date(2001, 1, 1, 8, 0, 0, 0, time.Local),
},
Person{
Name: "tony3",
Age: 1,
Born: time.Date(2002, 1, 1, 8, 0, 0, 0, time.Local),
})
orderedList := make([]Person, 0)
linq.From(list).OrderByDescending(func(i interface{}) interface{} { return i.(Person).Born }).ToSlice(&orderedList)
fmt.Println(orderedList)
}
type Person struct {
Name string
Age int
Born time.Time
}
运行报如下错误:
panic: interface conversion: time.Time is not linq.Comparable: missing method CompareTo
说的是time.Time不是linq.Comparable。
查看linq.Comparable,发现定义如下:
// Comparable is an interface that has to be implemented by a custom collection
// elements in order to work with linq.
//
// Example:
// func (f foo) CompareTo(c Comparable) int {
// a, b := f.f1, c.(foo).f1
//
// if a < b {
// return -1
// } else if a > b {
// return 1
// }
//
// return 0
// }
type Comparable interface {
CompareTo(Comparable) int
}
连同注释一起复制过来了,意思是要实现比较,就必须让类型实现Comparable 接口。
那么如何让time.Time
实现linq.Comparable
接口呢?
错误的尝试:
这还不简单,直接写一个time.Time的对象方法,这样time.Time不就自动实现了linq.Comparable接口了
func (a time.Time) CompareTo(c linq.Comparable) int {
b := c.(time.Time)
if a.After(b) {
return 1
} else if a.Equal(b) {
return 0
} else {
return -1
}
}
But,当写完这个方法之后,发现报错了!!!
意思是,time.Time没有在main包中定义,原来go只允许拓展struct所在包的方法
正确的方式
既然time.Time没有定义在main包,那我们自己定义一种类型,在自己的类型上实现linq.Comparable
这个方法,而我们自定义的类型,与time.Time肯定有着千丝万缕的关系。
C#的面向对象思想:组合优于继承
那就用组合的方式,将time.Time包装到自定义类型的里面,更改后的代码如下:
方式一
package main
import (
"fmt"
"time"
"github.com/ahmetb/go-linq/v3"
)
func main() {
list := make([]Person, 0)
list = append(list,
Person{
Name: "tony1",
Age: 1,
Born: time.Date(2000, 1, 1, 8, 0, 0, 0, time.Local),
}, Person{
Name: "tony2",
Age: 1,
Born: time.Date(2001, 1, 1, 8, 0, 0, 0, time.Local),
},
Person{
Name: "tony3",
Age: 1,
Born: time.Date(2002, 1, 1, 8, 0, 0, 0, time.Local),
})
orderedList := make([]Person, 0)
linq.From(list).OrderByDescending(func(i interface{}) interface{} { return NewMytime(i.(Person).Born) }).ToSlice(&orderedList)
fmt.Println(orderedList)
}
type Person struct {
Name string
Age int
Born time.Time
}
type Mytime struct {
Time time.Time
}
func NewMytime(t time.Time) Mytime {
return Mytime{Time: t}
}
func (a Mytime) CompareTo(c linq.Comparable) int {
b := c.(Mytime)
if a.Time.After(b.Time) {
return 1
} else if a.Time.Equal(b.Time) {
return 0
} else {
return -1
}
}
方式二
package main
import (
"fmt"
"time"
"github.com/ahmetb/go-linq/v3"
)
func main() {
list := make([]Person, 0)
list = append(list,
Person{
Name: "tony1",
Age: 1,
Born: NewMytime(time.Date(2000, 1, 1, 8, 0, 0, 0, time.Local)),
}, Person{
Name: "tony2",
Age: 1,
Born: NewMytime(time.Date(2001, 1, 1, 8, 0, 0, 0, time.Local)),
},
Person{
Name: "tony3",
Age: 1,
Born: NewMytime(time.Date(2002, 1, 1, 8, 0, 0, 0, time.Local)),
})
orderedList := make([]Person, 0)
linq.From(list).OrderByDescending(func(i interface{}) interface{} { return i.(Person).Born }).ToSlice(&orderedList)
fmt.Println(orderedList)
}
type Person struct {
Name string
Age int
Born Mytime
}
type Mytime struct {
Time time.Time
}
func NewMytime(t time.Time) Mytime {
return Mytime{Time: t}
}
func (a Mytime) CompareTo(c linq.Comparable) int {
b := c.(Mytime)
if a.Time.After(b.Time) {
return 1
} else if a.Time.Equal(b.Time) {
return 0
} else {
return -1
}
}
最后运行,结果跟预期的一致:
以上两种方式,都是定义一个Mytime
将time.Time
聚合在里面。
但是反问自己,真的不能用继承的方式吗???
于是有了第三种方式的解法:
方式三
先定义一个time.Time的类型叫CustomTime
,并实现linq.Comparable
接口
type CustomTime time.Time
func (a CustomTime) CompareTo(c linq.Comparable) int {
aa := time.Time(a)
bb := time.Time(c.(CustomTime))
if aa.After(bb) {
return 1
} else if aa.Equal(bb) {
return 0
} else {
return -1
}
}
排序的时候,强制转换为CustomTime
类型:
func main() {
list := make([]Person, 0)
list = append(list,
Person{
Name: "tony1",
Age: 1,
Born: time.Date(2000, 1, 1, 8, 0, 0, 0, time.Local),
}, Person{
Name: "tony2",
Age: 1,
Born: time.Date(2001, 1, 1, 8, 0, 0, 0, time.Local),
},
Person{
Name: "tony3",
Age: 1,
Born: time.Date(2002, 1, 1, 8, 0, 0, 0, time.Local),
})
orderedList := make([]Person, 0)
linq.From(list).OrderByDescending(func(i interface{}) interface{} { return CustomTime(i.(Person).Born) }).ToSlice(&orderedList)
fmt.Println(orderedList)
}
return CustomTime(i.(Person).Born)
这里就是将time.Time
强制转换为CustomTime
因为它们是同一个类型
以后遇到要拓展第三方包或者系统struct的类型时,就推荐使用第三种方式