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

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

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

推荐文章

HTML5的 input:file上传类型控制
2024-11-19 07:29:28 +0800 CST
Vue3中如何进行错误处理?
2024-11-18 05:17:47 +0800 CST
Vue3 中提供了哪些新的指令
2024-11-19 01:48:20 +0800 CST
Golang中国地址生成扩展包
2024-11-19 06:01:16 +0800 CST
16.6k+ 开源精准 IP 地址库
2024-11-17 23:14:40 +0800 CST
如何开发易支付插件功能
2024-11-19 08:36:25 +0800 CST
jQuery `$.extend()` 用法总结
2024-11-19 02:12:45 +0800 CST
Gin 与 Layui 分页 HTML 生成工具
2024-11-19 09:20:21 +0800 CST
Git 常用命令详解
2024-11-18 16:57:24 +0800 CST
介绍 Vue 3 中的新的 `emits` 选项
2024-11-17 04:45:50 +0800 CST
api接口怎么对接
2024-11-19 09:42:47 +0800 CST
快速提升Vue3开发者的效率和界面
2025-05-11 23:37:03 +0800 CST
thinkphp分页扩展
2024-11-18 10:18:09 +0800 CST
实用MySQL函数
2024-11-19 03:00:12 +0800 CST
12 个精选 MCP 网站推荐
2025-06-10 13:26:28 +0800 CST
js函数常见的写法以及调用方法
2024-11-19 08:55:17 +0800 CST
记录一次服务器的优化对比
2024-11-19 09:18:23 +0800 CST
避免 Go 语言中的接口污染
2024-11-19 05:20:53 +0800 CST
程序员茄子在线接单