处理集合是构建任何应用程序的重要部分。通常,您需要以下几类操作:
- 转换:将某个函数应用于集合中的每个元素,以创建一个新类型的新集合;
- 过滤:选择满足特定条件的集合中的元素;
- 聚合:从集合中计算出单个结果,通常用于汇总;
- 排序/排序:根据某些标准重新排列集合的元素;
- 访问:根据其属性或位置检索元素的操作;
- 实用程序:与集合一起工作的通用操作,但不一定完全适合上述分类。
尽管Go具有许多优点,但对于高级集合操作的内置支持相对有限,因此如果需要,您需要使用第三方包。在本文中,我将探讨几个流行的Go库,这些库可以增强语言的能力,以有效地处理集合,涵盖它们的功能和功能。这篇评论将帮助您选择合适的工具,以简化Go项目中的数据处理任务。
Introduction
让我们从上面的每个集合操作类中回顾一些流行的方法。
转换
Map — 对集合中的每个元素应用一个函数,并返回结果集合;
FlatMap — 将每个元素处理为一个元素列表,然后将这些列表展平为一个列表。
过滤
Filter — 删除不匹配谓词函数的元素;
Distinct — 从集合中删除重复的元素;
TakeWhile — 返回满足给定条件的元素,直到遇到不满足条件的元素为止;
DropWhile — 删除满足给定条件的元素,然后返回剩余的元素。
聚合
Reduce — 使用给定的函数组合集合的所有元素,并返回组合结果;
Count — 返回满足特定条件的元素数量;
Sum — 计算集合中每个元素的数字属性之和;
Max/Min — 确定元素属性中的最大值或最小值;
Average — 计算集合中元素的数字属性的平均值。
排序/排序
Sort — 根据比较器规则对集合的元素进行排序;
Reverse — 颠倒集合中元素的顺序。
访问
Find — 返回匹配给定谓词的第一个元素;
AtIndex — 检索特定索引处的元素。
实用程序
GroupBy — 根据键生成器函数将元素分类为组;
Partition — 根据谓词将集合分成两个集合:一个用于满足谓词的元素,另一个用于不满足谓词的元素;
Slice Operations — 修改集合视图或划分的操作,如切片或分块。
Go 内置的能力
在Go语言中,有几种类型可用于处理数据集合:
数组(Arrays) — 固定大小的元素集合。数组大小在声明时定义,例如 var myArray [5]int
;
切片(Slices) — 动态大小的元素集合。切片建立在数组之上,但与数组不同的是,它们可以增长或缩小。声明方式:mySlice := []int{1, 2, 3}
;
映射(Maps) — 键-值对的集合。映射可以动态增长,且键的顺序不受保证。例如 myMap := map[string]int{"first": 1, "second": 2}
创建了一个字符串键和整数值的映射;
通道(Channels) — 类型化的通信原语,允许在goroutine之间共享数据。例如 myChan := make(chan int)
创建了一个传输整数的通道。
Go标准库提供了其他结构和实用程序,可以作为集合或增强集合的功能,例如:
堆(Heap) — container/heap包为任何sort.Interface提供了堆操作。堆是具有以下特性的树:每个节点都是其子树中值最小的节点;
链表(List) — container/list包实现了双向链表;
环形链表(Ring) — container/ring包实现了环形链表的操作。
此外,作为Go标准库的一部分,还有用于处理切片和映射的包:
slices — 该包定义了与任何类型的切片一起使用的各种有用的函数;
maps — 该包定义了与任何类型的映射一起使用的各种有用的函数。
通过内置功能,您可以对集合执行一些操作:
获取数组/切片/映射的长度;
通过索引/键访问元素,对切片进行“切片”;
遍历项目。
1 | package main |
上面的代码会打印:
1 | len(s)=5 |
让我们逐个看下 Go 内置的 Package。
Slices
切片 slices包最近才出现在Go标准库中,从Go 1.21版本开始。这是语言中的一个重大进步,但我仍然更喜欢使用外部库来处理集合(您很快就会明白原因)。让我们来看看这个库如何支持所有的集合操作类别。
Aggregation
slices 能够快速在切片中找到最小/最大值:
1 | package main |
上面的代码打印:
1 | Min: 1 |
Sorting/Ordering
slices 能够使用比较函数对切片进行排序:
1 | package main |
1 | Sorted: [1 2 3 4 5] |
不过这个方法有个缺点,就是排序是原地进行的,修改了原始切片。如果该方法返回一个新的排序后的切片,从而保留原始数组会更好一点。
访问元素
slices
暴露了一些方法,允许用户在切片中查找元素的位置:
1 | package main |
1 | Index of 3: 4 |
如果你正在处理已排序的切片,你可以使用 BinarySearch 或 BinarySearchFunc 在排序的切片中搜索目标,并返回目标被找到的位置或目标将出现在排序顺序中的位置;它还返回一个布尔值,指示目标是否在切片中被找到。切片必须按递增顺序排序。
1 | package main |
1 | Position of 3: 2. Found: true |
实用函数
slices提供了许多实用函数:
1 | package main |
1 | Compare: 2 |
slices
包的官方文档地址
Map
类似于slices,maps也是从Go 1.21开始出现在Go标准库中的。它定义了各种方法来操作 maps。
1 | package main |
1 | Original: map[1:one 2:two 3:three] |
maps 包的官方文档地址
github.com/elliotchance/pie
这是我个人最喜欢的用来操作切片和映射的包。它提供了一种独特的语法,使您能够无缝地链接操作,提高了代码的可读性和效率。
使用库方法有四种方式:
- 纯调用 — 只需调用库方法并提供所需的参数;
- pie.Of — 链接多个操作,支持任何元素类型;
- pie.OfOrdered — 链接多个操作,支持数字和字符串类型;
- pie.OfNumeric — 链接多个操作,仅支持数字类型。
1 | package main |
1 | Map 1: [A B C D] |
由于诸如 Map 等函数应该返回相同类型的集合,因此这个库的链式操作相当受限。因此,我认为纯方法调用是使用这个库的最佳方式。
该库提供了 Map 方法,允许将每个元素从一种类型转换为另一种类型。
1 | package main |
还提供了 Flat 方法,它将二维切片转换为一维切片。
1 | package main |
1 | Unique Tags: [b c d e a] |
使用 Keys 或 Values 方法可以仅获取 Map 的键或值。
1 | package main |
Output:
1 | Keys: [3 1 2] |
Filter
该库提供了几种过滤原始集合的方法:Bottom、DropTop、DropWhile、Filter、FilterNot、Unique 等。
1 | Bottom 3: [4 4 4] |
Aggregation
有一个通用的聚合方法 Reduce。让我们来计算标准差:
1 | package main |
Output:
1 | Standard deviation: 1.555635 |
Reduce 方法首先将第一个切片元素作为累积值,将第二个元素作为值参数调用 reducer。这就是为什么公式看起来很奇怪。
从下面的示例中,可以找到另一个内置的聚合方法 Average。此外,您还可以找到 Min、Max、Product 等方法。
1 | package main |
1 | Average: 3.300000 |
Sorting/Ordering
有三种不同的方法可以使用 pie 对切片进行排序:
Sort — 类似于 sort.Slice。但与 sort.Slice 不同的是,返回的切片将被重新分配,以不修改输入切片;
SortStableUsing — 类似于 sort.SliceStable。但与 sort.SliceStable 不同的是,返回的切片将被重新分配,以不修改输入切片;
SortUsing — 类似于 sort.Slice。但与 sort.Slice 不同的是,返回的切片将被重新分配,以不修改输入切片。
1 | package main |
Output:
1 | Sort: [1 2 3 4 5] |
Access
pie 提供了 FindFirstUsing 方法,用于获取切片中第一个与谓词匹配的元素的索引。
1 | package main |
1 | FindFirstUsing: 2 |
pie 包含许多用于处理切片的实用方法。举几个例子:
1 | package main |
Output:
1 | Chunk: [[{Alice 25} {Bob 30}] [{Charlie 35} {David 25}] [{Eve 40} {Frank 35}]] |
下面是 pie 包的地址
elliotchance/pie/v2 库提供了一套非常完整的的处理集合的能力,极大地简化了在 Go 中处理切片的工作。其强大的方法用于操作和查询切片数据,为开发人员提供了一个强大的工具,增强了代码的可读性和效率。我强烈建议任何 Go 开发人员在下一个项目中尝试使用这个库。
github.com/samber/lo
另一个在 Go 中操作集合的流行库。在某些方面,它可能类似于流行的 JavaScript 库 Lodash。它在内部使用泛型,而不是反射。
Transform
该库支持默认的 Map
和 FlatMap
方法用于切片:
1 | package main |
1 | Map: [C A D B] |
下面的代码演示如何操作 FlatMap:
1 | package main |
此外,还可以获取映射键、值或将映射对转换为某些切片等操作:
1 | package main |
Outputs:
1 | Keys: [2 3 1] |
Filter
在 lo 库中有许多 Drop 方法:
1 | package main |
Outputs:
1 | Drop: [3 4 5] |
此外,还可以通过 predicate 过滤切片和映射:
1 | package main |
Outputs:
1 | Filter: [3 4 5] |
Aggregation
lo
package 提供了 reduce 的方法:
1 | package main |
Outputs:
1 | Standard deviation: 1.555635 |
此外,它支持一些通用的聚合方法,如 Sum、Min、Max:
1 | package main |
有一些有用的方法用于处理 channel:FanIn 和 FanOut
1 | package main |
还可以这样处理 channel:
1 | package main |
Sorting/Ordering
lo
还提供 Reverse
方法:
1 | package main |
Access
lo
库提供了 find 方法来访问元素:
1 | package main |
Outputs:
1 | Item: {Name:Charlie Age:35}, Found: true |
Find 方法同样支持 map:
1 | package main |
lo
的文档地址
总结
这里只展示了 github.com/samber/lo 库约 10% 的方法。这个库提供了许多简化函数处理的实用工具。对于 Go 开发人员来说,这个库是一个非常全面的工具包。
本文展示了一些在 Go 中处理集合时推荐的库,希望对大家的开发工作有帮助。