Go语言实用——好用也有三方库之lo


Go语言轮子-好用也有三方库之lo

在编程中,避免重复造轮子是一种普遍原则。我曾经写过一些关于学习官方标准库的笔记,而这篇文章将介绍一些我发现的,以及别人推荐的实用的第三方库。这也是我学习过程中的一些笔记。

库的地址是:https://github.com/samber/lo

原文的介绍是这样的:

lo - 迭代切片、地图、通道…

✨samber/lo 是一个基于 Go 1.18+ 泛型的 Lodash 风格的 Go 库。

该项目最初是作为新泛型实施的实验而开始的。在某些方面,它可能看起来像 Lodash。我曾经使用过优秀的“go-funk”包进行编码,但是“go-funk”使用反射,因此不是类型安全的。

正如预期的那样,基准测试表明,泛型比基于“reflect”包的实现速度快得多。与纯 for 循环相比,基准测试也显示出类似的性能提升。见下文。

这个库完全基于官方标准库,没有依赖其他第三方库,因此在安全性上更有保障。在自己的项目中可以直接使用,它还提供了许多有价值的抽象。

下面我将提供一些库中的使用例子。

过滤

迭代集合并返回谓词函数返回 true 的所有元素的数组。

even := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
    return x%2 == 0
})
// []int{2, 4}

类型映射

操作一种类型的切片并将其转换为另一种类型的切片:

import "github.com/samber/lo"

lo.Map([]int64{1, 2, 3, 4}, func(x int64, index int) string {
    return strconv.FormatInt(x, 10)
})
// []string{"1", "2", "3", "4"}

去重

返回数组的无重复版本,其中只保留每个元素的第一次出现。结果值的顺序由它们在数组中的出现顺序决定。

uniqValues := lo.Uniq([]int{1, 2, 2, 1})
// []int{1, 2}

条件去重

返回数组的无重复版本,其中只保留每个元素的第一次出现。结果值的顺序由它们在数组中的出现顺序决定。它接受 iteratee 并为数组中的每个元素调用它,以生成计算唯一性的标准。

uniqValues := lo.UniqBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
    return i%3
})
// []int{0, 1, 2}

通过…分组

返回一个对象,该对象由通过 iteratee 运行集合中每个元素的结果生成的键组成。

groups := lo.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
    return i%3
})
// map[int][]int{0: []int{0, 3}, 1: []int{1, 4}, 2: []int{2, 5}}

展平

返回一个单层深度的数组。

flat := lo.Flatten([][]int{{0, 1}, {2, 3, 4, 5}})
// []int{0, 1, 2, 3, 4, 5}

打乱

返回打乱值的数组。使用 Fisher-Yates 洗牌算法。

randomOrder := lo.Shuffle([]int{0, 1, 2, 3, 4, 5})
// []int{1, 4, 0, 3, 5, 2}

反转

反转数组,使第一个元素成为最后一个元素,第二个元素成为倒数第二个元素,依此类推。

⚠️这个助手是可变的。此行为可能会在v2.0.0. 参见#160

reverseOrder := lo.Reverse([]int{0, 1, 2, 3, 4, 5})
// []int{5, 4, 3, 2, 1, 0}

拒绝

与 Filter 相反,此方法返回谓词不返回 true 的集合元素。

odd := lo.Reject([]int{1, 2, 3, 4}, func(x int, _ int) bool {
    return x%2 == 0
})
// []int{1, 3}

按键挑选

返回按给定键过滤的相同地图类型。

m := lo.PickByKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []string{"foo", "baz"})
// map[string]int{"foo": 1, "baz": 3}

按值挑选

返回按给定值过滤的相同地图类型。

m := lo.PickByValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []int{1, 3})
// map[string]int{"foo": 1, "baz": 3}

范围/范围从/范围与步骤

创建从开始到结束(但不包括结束)的数字(正数和/或负数)数组。

result := lo.Range(4)
// [0, 1, 2, 3]

result := lo.Range(-4)
// [0, -1, -2, -3]

result := lo.RangeFrom(1, 5)
// [1, 2, 3, 4, 5]

result := lo.RangeFrom[float64](1.0, 5)
// [1.0, 2.0, 3.0, 4.0, 5.0]

result := lo.RangeWithSteps(0, 20, 5)
// [0, 5, 10, 15]

result := lo.RangeWithSteps[float32](-1.0, -4.0, -1.0)
// [-1.0, -2.0, -3.0]

result := lo.RangeWithSteps(1, 4, -1)
// []

result := lo.Range(0)
// []

补集

返回不包括所有给定值的切片。

subset := lo.Without([]int{0, 2, 10}, 2)
// []int{0, 10}

subset := lo.Without([]int{0, 2, 10}, 0, 1, 2, 3, 4, 5)
// []int{10}

更多的例子可以在库的地址下查看。

其中,函数的数组参数其实是一个泛型,实际上你可以是任何类型:结构体、map、string、flote64…

func Filter[V any](collection []V, predicate func(item V, index int) bool) []V {
	result := make([]V, 0, len(collection))

	for i, item := range collection {
		if predicate(item, i) {
			result = append(result, item)
		}
	}

	return result
}

来看 filter 的源码:传入的参数 collection []Vvany 类型即泛型,先是初始化的一个切片 result := make([]V, 0, len(collection)),然后在对输入参数进行遍历,在对对象进行条件筛选,返回新的切片。

实际上是很简单方法调用封装了一下。

对于数组、map、string等调用逻辑很频繁的可以尝试使用这个库,也顺便给一个 star,反正我是点了,更多源码可以在使用过程中慢慢发现,共学习共进步,good code!


文章作者: hypo
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hypo !
评论
  目录