侧边栏壁纸
博主头像
Tony's Blog博主等级

行动起来,coding

  • 累计撰写 83 篇文章
  • 累计创建 58 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录
go

go-linq按照时间排序

Tony
2024-02-21 / 0 评论 / 0 点赞 / 12 阅读 / 8113 字

本文链接: https://blog.csdn.net/lishuangquan1987/article/details/123727912

写惯了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

1-uymx.png

说的是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,当写完这个方法之后,发现报错了!!!

2-auhc.png

意思是,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
	}
}

最后运行,结果跟预期的一致:

3-qkja.png

以上两种方式,都是定义一个 Mytimetime.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的类型时,就 推荐使用第三种方式

0

评论区