编程 一个能让你少写循环和判断的 Go 开源包,支持泛型

2024-11-17 03:52:43 +0800 CST views 756

一个能让你少写循环和判断的 Go 开源包,支持泛型

在开发过程中,大家经常会遇到需要处理列表数据的情况。比如,当你从数据库查询用户订单时,查询结果通常会以一个对象列表的形式返回:

[]*Order {
    &{
        ID: 1,
        OrderNo: "20240903628359373756980001"
        ...
    },
    ...
}

得到了这个结果集后,我们常常需要对其进行各种操作,比如判断某个 ID 为 1 的订单是否存在于列表中:

var exists bool
for _, order := range orders {
    if order.ID == 1 {
        exists = true
    }
}

虽然这种操作不复杂,但经常要手动编写循环和判断逻辑,难免有些繁琐。为了减少循环次数,通常我们会将列表转换为 map,以订单 ID 为 key:

map[int64]Order{
    1: {
       ID: 1,
       OrderNo: "20240903628359373756980001"
       ...
    }
}

这些列表和哈希表(SliceMap)操作几乎是我们日常开发中不可避免的工作,常见操作包括:

  • Slice 中查找元素的位置
  • 判断某个元素是否存在
  • 查找所有符合条件的元素
  • Slice 转换为 Map
  • 获取 Map 的所有键或值

例如,JavaScript 中的 mapreducefilter,以及 Java 的 Stream API 都极大地方便了这些操作。但 Go 标准库并未提供类似的功能,因此很多项目都会手动编写大量的工具函数,比如 InSliceXXXInSlice 等等。

然而,Go 1.18 引入了泛型,使得编写这些工具函数变得更加容易和简洁。在此之前,像 go-funk 这样的库就已经提供了大量的函数工具,比如 ContainsDifferenceIndexOfFilterToMap 等。go-funk 是在 Go 1.18 之前发布的,因此为了适应多种类型,它使用了反射。

Go 泛型的引入:lo

Go 1.18 支持泛型后,lo 库应运而生,它基于泛型实现,提供了类似于 JavaScript 中 Lodash 的工具函数,例如 mapfiltercontainsfind 等。相比于 go-funklo 库不再使用反射,效率更高,代码也更加简洁。以 Contains 函数为例:

func Contains[T comparable](collection []T, element T) bool {
    for i := range collection {
        if collection[i] == element {
            return true
        }
    }
    return false
}

利用泛型,只需将 T 约束为可比较类型 comparable,就可以用 == 进行比较,整体代码非常简单。

接下来,我将演示一些常见的 SliceMap 操作,展示如何使用 lo 库简化这些任务。

1. Filter 筛选符合条件的子列表

假设有一个订单列表:

[]*Order{
    &{
        ID: 1,
        OrderNo: "20240903628359373756980001",
        UserId: 255,
        ...
    },
    ...
}

我们可以通过 Filter 函数筛选出 UserId 等于指定值的订单:

func FindUserOrders(orders []*Order, userId int64) []*Order {
    userOrders := lo.Filter(orders, func(item *Order, index int) bool {
        return item.UserId == userId
    })
    return userOrders
}

2. 从订单列表中提取所有订单 ID

有时我们需要从列表中提取出所有 ID 进行进一步操作,可以使用 Map 函数:

orderIds := lo.Map(orders, func(item *Order, index int) int64 {
    return item.ID
})

3. 将列表转换为 Map

为了减少遍历次数,可以将列表转换为以订单 ID 为键的 Map

orderMap := lo.SliceToMap(orders, func(item *Order) (int64, *Order) {
    return item.ID, item
})

4. 根据字段进行分组

如果需要将订单按 UserId 分组,可以使用 GroupBy 函数:

userOrderMap := lo.GroupBy(orders, func(item *Order) int64 {
    return item.UserId
})

5. 计算订单总金额

可以使用 Reduce 函数求出订单的总金额:

totalPrice := lo.Reduce(orders, func(agg int, item *Order, index int) int {
    return agg + item.PayMoney
}, 0)

6. 多线程遍历

lo 库还提供了多线程遍历的支持,可以使用 Foreach 函数并发处理集合中的元素:

import lop "github.com/samber/lo/parallel"

lop.ForEach([]string{"hello", "world"}, func(x string, _ int) {
    println(x)
})

7. Map 的常用操作

lo 库还提供了 Map 操作的工具函数,例如 KeysValues 等。其 API 名称与其他编程语言中类似功能的函数非常接近,便于理解和使用。

使用建议

尽管 lo 库非常强大,但它并不是万能的。在能用简单循环解决问题时,尽量避免过度依赖 lo。过度使用可能会使代码变得复杂难懂,尤其是嵌套调用时。此外,过度依赖函数式编程风格也可能增加代码的维护难度。

总结

lo 库通过泛型为 Go 带来了简洁高效的工具函数,减少了循环和判断逻辑的编写。如果你的 Go 项目还不支持泛型,可以尝试使用 go-funk。希望本文能帮助大家更好地理解和使用这些工具库,编写出更加简洁优雅的代码。

更多内容可以参考 lo 的官方文档:https://github.com/samber/lo

推荐文章

一个简单的html卡片元素代码
2024-11-18 18:14:27 +0800 CST
Linux查看系统配置常用命令
2024-11-17 18:20:42 +0800 CST
html5在客户端存储数据
2024-11-17 05:02:17 +0800 CST
php腾讯云发送短信
2024-11-18 13:50:11 +0800 CST
js常用通用函数
2024-11-17 05:57:52 +0800 CST
mysql 优化指南
2024-11-18 21:01:24 +0800 CST
HTML5的 input:file上传类型控制
2024-11-19 07:29:28 +0800 CST
Vue3中如何处理SEO优化?
2024-11-17 08:01:47 +0800 CST
Vue3中如何处理路由和导航?
2024-11-18 16:56:14 +0800 CST
MyLib5,一个Python中非常有用的库
2024-11-18 12:50:13 +0800 CST
mendeley2 一个Python管理文献的库
2024-11-19 02:56:20 +0800 CST
js生成器函数
2024-11-18 15:21:08 +0800 CST
Vue3中如何实现国际化(i18n)?
2024-11-19 06:35:21 +0800 CST
H5保险购买与投诉意见
2024-11-19 03:48:35 +0800 CST
pip安装到指定目录上
2024-11-17 16:17:25 +0800 CST
程序员茄子在线接单