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

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

一个能让你少写循环和判断的 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

推荐文章

paint-board:趣味性艺术画板
2024-11-19 07:43:41 +0800 CST
Nginx 状态监控与日志分析
2024-11-19 09:36:18 +0800 CST
总结出30个代码前端代码规范
2024-11-19 07:59:43 +0800 CST
使用 sync.Pool 优化 Go 程序性能
2024-11-19 05:56:51 +0800 CST
Python 获取网络时间和本地时间
2024-11-18 21:53:35 +0800 CST
Vue 3 路由守卫详解与实战
2024-11-17 04:39:17 +0800 CST
Vue3 结合 Driver.js 实现新手指引
2024-11-18 19:30:14 +0800 CST
Vue3中如何实现国际化(i18n)?
2024-11-19 06:35:21 +0800 CST
联系我们
2024-11-19 02:17:12 +0800 CST
使用Vue 3和Axios进行API数据交互
2024-11-18 22:31:21 +0800 CST
Vue3中如何进行错误处理?
2024-11-18 05:17:47 +0800 CST
Boost.Asio: 一个美轮美奂的C++库
2024-11-18 23:09:42 +0800 CST
程序员茄子在线接单